Simplify parsed variables. Improve dump configuration for test report by state type
[anna.git] / example / diameter / launcher / Launcher.cpp
index edeaf88..4219399 100644 (file)
@@ -65,50 +65,51 @@ const char *ServicesDTD = "\
 \n\
    originRealm:                             Node identifier (Origin-Realm name).\n\
    applicationId:                           The Application-Id provided must exists as a registered 'stack id'.\n\
-   originHost:                              Diameter application host name (system name). If missing, process sets o.s. hostname\n\
+   originHost:                              Diameter application host name (system name). If missing, process sets o.s. hostname.\n\
                                             Note that if you have two or more realms, the names must be different.\n\
    cer:                                     User defined CER path file to be encoded to establish diameter connections.\n\
-                                            If missing, will be harcoded\n\
+                                            If missing, will be harcoded.\n\
    dwr:                                     User defined DWR path file to be encoded for diameter protocol keep alive.\n\
-                                            If missing, will be harcoded\n\
+                                            If missing, will be harcoded.\n\
    allowedInactivityTime:                   Milliseconds for the maximum allowed inactivity time on server sessions born over the\n\
-                                            local server before being reset. If missing, default value of 90000 will be assigned\n\
+                                            local server before being reset. If missing, default value of 90000 will be assigned.\n\
    tcpConnectDelay:                         Milliseconds to wait TCP connect to any server. If missing, default value of 200 will\n\
                                             be assigned\n\
    answersTimeout:                          Milliseconds to wait pending application answers from diameter peers. If missing,\n\
-                                            default value of 10000 will be assigned\n\
+                                            default value of 10000 will be assigned.\n\
    ceaTimeout:                              Milliseconds to wait CEA from diameter server. If missing, default value of 'answersTimeout'\n\
-                                            will be assigned\n\
+                                            will be assigned.\n\
    watchdogPeriod:                          Milliseconds for watchdog timer (Tw) for diameter keep-alive procedure. If missing, default\n\
-                                            value of 30000 will be assigned\n\
+                                            value of 30000 will be assigned.\n\
    entity:                                  Target diameter entity (comma-separated '<address>:<port>' format).\n\
-                                            For example: 10.20.30.40:3868,10.20.30.41:3868. If missing, no entity will be enabled\n\
-   entityServerSessions:                    Diameter entity server sessions (0: diameter entity disabled). Default value of 1\n\
+                                            For example: 10.20.30.40:3868,10.20.30.41:3868. If missing, no entity will be enabled.\n\
+   entityServerSessions:                    Diameter entity server sessions (0: diameter entity disabled). Default value of 1.\n\
    diameterServer:                          Diameter own server address in '<address>:<port>' format. For example: 10.20.30.40:3868.\n\
-                                            If missing, no local server will be enabled\n\
-   diameterServerSessions:                  Diameter own server available connections (0: diameter server disabled). Default value of 1\n\
+                                            If missing, no local server will be enabled.\n\
+   diameterServerSessions:                  Diameter own server available connections (0: diameter server disabled). Default value of 1.\n\
+                                            Negative value implies no limit accepting incoming connections.\n\
    balance:                                 Balance over entity servers instead of doing standard behaviour (first primary, secondary\n\
-                                            if fails, etc.). Default value 'no'\n\
+                                            if fails, etc.). Default value 'no'.\n\
    sessionBasedModelsClientSocketSelection: By default, round-robin will be applied for IEC model (SMS/MMS), and Session-Id Low Part\n\
                                             will be analyzed for ECUR/SCUR model (data, voice and content). You could change ECUR/SCUR\n\
                                             analysis behaviour providing 'SessionIdHighPart', 'SessionIdOptionalPart' (atoi applied;\n\
-                                            usually subscriber id data, i.e. MSISDN or IMSI) and 'RoundRobin' (also 'SessionIdLowPart')\n\
-   retries:                                 Expired responses will cause a number of request retransmissions. Disabled by default (0 retries)\n\
+                                            usually subscriber id data, i.e. MSISDN or IMSI) and 'RoundRobin' (also 'SessionIdLowPart').\n\
+   retries:                                 Expired responses will cause a number of request retransmissions. Disabled by default (0 retries).\n\
    log:                                     Process log file (operations result, traffic log, etc.). By default '<originRealm>.launcher.log'.\n\
                                             Empty string or \"null\" name, to disable. Warning: there is no rotation for log files\n\
-                                            (use logrotate or whatever you consider)\n\
+                                            (use logrotate or whatever you consider).\n\
    splitLog:                                Splits log file (appends to log filename, extensions with the type of event: see help on\n\
                                             startup information-level traces). No log files for code/decode and load operations are created.\n\
-                                            Default value 'no'\n\
+                                            Default value 'no'.\n\
    detailedLog:                             Insert detailed information at log files. Should be disabled on automatic tests. Useful on\n\
-                                            'balance' mode to know messages flow along the sockets. Default value 'no'\n\
+                                            'balance' mode to know messages flow along the sockets. Default value 'no'.\n\
    dumpLog:                                 Write to disk every incoming/outcoming message named as:\n\
                                                '<originRealm>.<hop by hop>.<end to end>.<message code>.<request|answer>.<type of event>.xml'\n\
-                                            Default value 'no'\n\
+                                            Default value 'no'.\n\
    burstLog:                                Burst operations log file. By default '<originRealm>.launcher.burst'. Empty string or \"null\" name, to disable.\n\
                                             Warning: there is no rotation for log files (use logrotate or whatever). Output: dot (.) for each\n\
                                             burst message sent/pushed, cross (x) for popped ones, and order number when multiple of 1% of burst\n\
-                                            list size, plus OTA requests when changed\n\
+                                            list size, plus OTA requests when changed.\n\
 \n\
 -->\n\
 \n\
@@ -132,7 +133,11 @@ Launcher::Launcher() : anna::comm::Application("launcher", "DiameterLauncher", "
 
 
 void Launcher::servicesFromXML(const anna::xml::Node* servicesNode, bool eventOperation) throw(anna::RuntimeException) {
-  //<!ATTLIST stack id CDATA #REQUIRED dictionary CDATA #REQUIRED>
+
+  CommandLine& cl(anna::CommandLine::instantiate());
+  bool allLogsDisabled = cl.exists("disableLogs");
+
+    //<!ATTLIST stack id CDATA #REQUIRED dictionary CDATA #REQUIRED>
   const anna::xml::Attribute *id, *dictionary;
 
   // <!ATTLIST node originRealm CDATA #REQUIRED applicationId CDATA #REQUIRED originHost CDATA #IMPLIED cer CDATA #IMPLIED dwr CDATA #IMPLIED allowedInactivityTime CDATA #IMPLIED tcpConnectDelay CDATA #IMPLIED answersTimeout CDATA #IMPLIED ceaTimeout CDATA #IMPLIED watchdogPeriod CDATA #IMPLIED entity CDATA #IMPLIED entityServerSessions CDATA #IMPLIED diameterServer CDATA #IMPLIED diameterServerSessions CDATA #IMPLIED balance (yes | no) #IMPLIED sessionBasedModelsClientSocketSelection (SessionIdLowPart | SessionIdHighPart | SessionIdOptionalPart | RoundRobin) #IMPLIED retries CDATA #IMPLIED log CDATA #IMPLIED splitLog (yes | no) #IMPLIED detailedLog (yes | no) #IMPLIED dumpLog (yes | no) #IMPLIED burstLog (yes | no) #IMPLIED>
@@ -140,7 +145,6 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode, bool eventOp
   *answersTimeout, *ceaTimeout, *watchdogPeriod, *entity, *entityServerSessions,
   *diameterServer, *diameterServerSessions, *balance, *sessionBasedModelsClientSocketSelection,
   *retries, *log, *splitLog, *detailedLog, *dumpLog, *burstLog;
-
   // Never clear services content from here (append new data from xml). At the moment no node removing is implemented in this process
 
   // Stacks
@@ -159,7 +163,6 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode, bool eventOp
   anna::diameter::comm::ApplicationMessageOamModule & appMsgOamModule = anna::diameter::comm::ApplicationMessageOamModule::instantiate();
   appMsgOamModule.enableCounters(); // this special module is disabled by default (the only)
   static int scope_id = 3;
-  bool id_0_registered = false;
   unsigned int id_value;
   std::string codecEngineName;
 
@@ -172,6 +175,11 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode, bool eventOp
       dictionary = (*it)->getAttribute("dictionary");
       id_value = id->getIntegerValue();
 
+      if (stackEngine.getDictionary(id_value)) { // Ignore (but don't fail) dictionary load with same stack id already registered
+        LOGWARNING(anna::Logger::warning(anna::functions::asString("Ignore dictionary load for stack id already registered: %llu", id_value), ANNA_FILE_LOCATION));
+        continue;
+      }
+
       try {
         d = stackEngine.createDictionary(id_value, dictionary->getValue());
         LOGDEBUG(anna::Logger::debug(anna::functions::asString("Created dictionary (%p) for stack id %llu", d, id_value), ANNA_FILE_LOCATION));
@@ -185,10 +193,7 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode, bool eventOp
         throw ex;
       }
 
-      if (id_value == 0) {
-        id_0_registered = true;
-        bpd = d;
-      }
+      bpd = d; // base protocol dictionary in case of monostack. If multistack, will be calculated later
 
       // Create codec engine and register it in the codec engine manager:
       codecEngineName = anna::functions::asString("CodecEngineForStackId_%llu", id_value);
@@ -236,14 +241,10 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode, bool eventOp
   // Basic checking for multistack:
   bool multistack = (stackEngine.stack_size() > 1);
   if (multistack) {
-    if(!id_0_registered)
+    bpd = stackEngine.getDictionary(0);
+    if(!bpd)
       throw anna::RuntimeException("In multistack applications is mandatory register a stack id = 0 using a dictionary which contains the needed elements to build base protocol messages (CER/A, DWR/A, DPR/A, STR/A, etc.)", ANNA_FILE_LOCATION);
   }
-  else { // monostack
-    if (!bpd)
-      bpd = ce->getDictionary(); // in mono-stack applications, we understand the existing stack as the used
-                                 // for base protocol, regardless if it is registered with stack id 0 or not
-  }
 
   // REALMS:
   for(anna::xml::Node::const_child_iterator it = servicesNode->child_begin(); it != servicesNode->child_end(); it++) {
@@ -356,13 +357,15 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode, bool eventOp
       }
 
       // Logs:
-      std::string realm = commEngine->getRealm();
-      std::string s_log = realm + ".launcher.log"; if (log) s_log = log->getValue();
-      bool b_splitLog = (splitLog ? (splitLog->getValue() == "yes") : false);
-      bool b_detailedLog = (detailedLog ? (detailedLog->getValue() == "yes") : false);
-      bool b_dumpLog = (dumpLog ? (dumpLog->getValue() == "yes") : false);
-      std::string s_burstLog = realm + ".launcher.burst"; if (burstLog) s_burstLog = burstLog->getValue();
-      a_workingNode->setLogs(s_log, b_splitLog, b_detailedLog, b_dumpLog, s_burstLog);
+      if (!allLogsDisabled) {
+        std::string realm = commEngine->getRealm();
+        std::string s_log = realm + ".launcher.log"; if (log) s_log = log->getValue();
+        bool b_splitLog = (splitLog ? (splitLog->getValue() == "yes") : false);
+        bool b_detailedLog = (detailedLog ? (detailedLog->getValue() == "yes") : false);
+        bool b_dumpLog = (dumpLog ? (dumpLog->getValue() == "yes") : false);
+        std::string s_burstLog = realm + ".launcher.burst"; if (burstLog) s_burstLog = burstLog->getValue();
+        a_workingNode->setLogs(s_log, b_splitLog, b_detailedLog, b_dumpLog, s_burstLog);
+      }
 
 
       // Lazy initialization for comm engine:
@@ -800,7 +803,7 @@ void Launcher::resetCounters() throw() {
 
 void Launcher::signalUSR2() throw(anna::RuntimeException) {
   LOGNOTICE(
-      std::string msg = "Captured signal SIGUSR2. Reading tasks at '";
+  std::string msg = "Captured signal SIGUSR2. Reading tasks at '";
   msg += SIGUSR2_TASKS_INPUT_FILENAME;
   msg += "' (results will be written at '";
   msg += SIGUSR2_TASKS_OUTPUT_FILENAME;
@@ -815,7 +818,6 @@ void Launcher::signalUSR2() throw(anna::RuntimeException) {
   std::ofstream out_file(SIGUSR2_TASKS_OUTPUT_FILENAME);
 
   if(!in_file.is_open()) throw RuntimeException("Unable to read tasks", ANNA_FILE_LOCATION);
-
   if(!out_file.is_open()) throw RuntimeException("Unable to write tasks", ANNA_FILE_LOCATION);
 
   while(getline(in_file, line)) {
@@ -835,6 +837,7 @@ void Launcher::signalUSR2() throw(anna::RuntimeException) {
   }
 
   in_file.close();
+  out_file << "EOF\n";
   out_file.close();
 }
 
@@ -975,6 +978,10 @@ std::string Launcher::help() const throw() {
   result += "\n                                      to load new nodes once the ADML is started, regardless if command";
   result += "\n                                      line '--services' parameter was used or not. Those services which";
   result += "\n                                      are not correctly loaded will be ignored to keep the process alive.";
+  result += "\n                                     If you need to load services as deltas, you must firstly load the";
+  result += "\n                                      diameter base dictionary with stack id 0, because all the realms";
+  result += "\n                                      will use this dictionary to encode/decode base protocol messages";
+  result += "\n                                      managed by the communication engine.";
   result += "\n";
   result += "\ndiameterServerSessions|<integer>     Updates the maximum number of accepted connections to diameter";
   result += "\n                                      server socket.";
@@ -988,6 +995,10 @@ std::string Launcher::help() const throw() {
   result += "\n                                     This operation applies over all the registered realm nodes";
   result += "\n                                      except if one specific working node has been set.";
   result += "\nforceCountersRecord                  Forces dump to file the current counters of the process.";
+  result += "\nshow-oam                             Dumps current counters of the process. This is also done at";
+  result += "\n                                      process context dump.";
+  result += "\nshow-stats                           Dumps statistics of the process. This is also done at process";
+  result += "\n                                      context dump.";
   result += "\n";
   result += "\n<visibility action>[|<address>:<port>][|socket id]";
   result += "\n";
@@ -1153,6 +1164,7 @@ std::string Launcher::help() const throw() {
   result += "\n";
   result += "\n                              delay|<msecs>              Blocking step until the time lapse expires. Useful to give ";
   result += "\n                                                          some cadence control and time schedule for a specific case.";
+  result += "\n                                                         A value of 0 could be used as a dummy step.";
   result += "\n                              wait<fe/fc>|<condition>    Blocking step until condition is fulfilled. The message could";
   result += "\n                                                          received from entity (waitfe) or from client (waitfc).";
   result += "\n";
@@ -1305,13 +1317,18 @@ std::string Launcher::help() const throw() {
   result += "\n";
   result += "\n                                 Provide 0 in order to stop the timer triggering.";
   result += "\n";
-  result += "\n                                 There timer manager resolution currently harcoded allows a maximum  of ";
+  result += "\n                                 The timer manager resolution currently harcoded allows a maximum  of ";
   result += anna::functions::asString(1000/a_admlMinResolution); result += " events";
   result += "\n                                 per second. To reach greater rates ADML will join synchronously the needed number of";
   result += "\n                                 new time-triggered test cases per a single event, writting a warning-level trace to";
   result += "\n                                 advice about the risk of burst sendings and recommend launching multiple instances";
   result += "\n                                 to achieve such load with a lower rate per instance.";
   result += "\n";
+  result += "\n   test|next[|<sync-amount>]     Forces the execution of the next test case(s) without waiting for test manager tick.";
+  result += "\n                                 Provide an integer value for 'sync-amount' to send a burst synchronous amount of the";
+  result += "\n                                 next programmed test cases (1 by default). This event works regardless the timer tick";
+  result += "\n                                 function, but it is normally used with the test manager tick stopped.";
+  result += "\n";
   result += "\n   test|ip-limit[|amount]        In-progress limit of test cases. No new test cases will be launched over this value";
   result += "\n                                 (test Manager tick work will be ignored). Zero-value is equivalent to stop the clock.";
   result += "\n                                 tick, -1 is used to specify 'no limit' which is the default. If missing amount, the";
@@ -1323,15 +1340,6 @@ std::string Launcher::help() const throw() {
   result += "\n                                 Test cases reports are not dumped on process context (too many information in general).";
   result += "\n                                 The report contains context information in every moment: this operation acts as a snapshot.";
   result += "\n";
-  result += "\n   test|report[|[yes]|no]        Every time a test case is finished, its xml representation will be dump on a file under";
-  result += "\n                                 the execution directory (or the one configured in process command-line 'tmDir') with";
-  result += "\n                                 the name 'cycle-<cycle id>.testcase-<test case id>.xml'. This option is disabled by";
-  result += "\n                                 default reducing IO. ADML process context won't show test manager whole information";
-  result += "\n                                 because it could be a huge amount of data to write. Anyway, you could use the 'look'";
-  result += "\n                                 operation to see the desired report(s).";
-  result += "\n";
-  result += "\n   test|report-hex[|[yes]|no]    Reports could include the diameter messages in hexadecimal format. Disabled by default.";
-  result += "\n";
   result += "\n   test|interact|amount|id       Makes interactive a specific test case id. The amount is the margin of execution steps";
   result += "\n                                 to be done. Normally, we will execute 'test|interact|0|<test case id>', which means that";
   result += "\n                                 the test case is selected to be interactive, but no step is executed. Then you have to";
@@ -1354,7 +1362,26 @@ std::string Launcher::help() const throw() {
   result += "\n";
   result += "\n   test|clear                    Clears all the programmed test cases and stop testing (if in progress).";
   result += "\n";
-  result += "\n   test|stat                     Global statistics.";
+  result += "\n   test|summary                  Test manager general report (number of test cases, counts by state, global configuration,";
+  result += "\n                                 forced in-progress limitation, reports visibility, etc.). Be careful when you have reports";
+  result += "\n                                 enabled because the programmed test cases dumps could be heavy (anyway you could enable the";
+  result += "\n                                 dumps separately, for any of the possible states: Initialized, InProgress, Failed, Success).";
+  result += "\n";
+  result += "\n   test|report|<initialized/in-progress/failed/success/all>[|[yes]|no]";
+  result += "\n";
+  result += "\n                                 Enables/disables report generation for a certain test case state: initialized, in-progress,";
+  result += "\n                                 failed or success. This applies to report summary (former described operation) and automatic";
+  result += "\n                                 dumps during testing where only failed or success states will appear: every time a test case";
+  result += "\n                                 is finished its xml representation will be dump on a file under the execution directory (or";
+  result += "\n                                 the one configured in process command-line 'tmDir') with the name:";
+  result += "\n";
+  result += "\n                                    'cycle-<cycle id>.testcase-<test case id>.xml'.";
+  result += "\n";
+  result += "\n                                 By default, all the states are disabled to avoid IO overload. In most of cases not all the";
+  result += "\n                                 tests are going to fail and you could enable only such failed dumps, but you can enable all";
+  result += "\n                                 the types if you want (use reserved word 'all' for this).";
+  result += "\n";
+  result += "\n   test|report-hex[|[yes]|no]    Reports could include the diameter messages in hexadecimal format. Disabled by default.";
   result += "\n";
   result += "\n";
   result += "\nUSING OPERATIONS INTERFACE";
@@ -1385,6 +1412,8 @@ std::string Launcher::help() const throw() {
   result += "\n    and then see the results:";
   result += "\n   cat "; result += SIGUSR2_TASKS_OUTPUT_FILENAME;
   result += "\n";
+  result += "\n   (this file is ended with EOF final line, useful managing huge batch files to ensure the job completion)";
+  result += "\n";
   result += "\nYou could place more than one line (task) in the input file. Output reports will be appended in that";
   result += "\n case over the output file. Take into account that all the content of the task file will be executed";
   result += "\n sinchronously by the process. If you are planning traffic load, better use the asynchronous http";
@@ -1436,6 +1465,18 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
     return;
   }
 
+  // OAM & statistics:
+  if(operation == "show-oam") {
+    anna::xml::Node root("root");
+    response_content = anna::xml::Compiler().apply(oamAsXML(&root));
+    return;
+  }
+  if(operation == "show-stats") {
+    anna::xml::Node root("root");
+    response_content = anna::xml::Compiler().apply(statsAsXML(&root));
+    return;
+  }
+
   ///////////////////////////////////////////////////////////////////
   // Tokenize operation
   Tokenizer params;
@@ -1515,7 +1556,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
     }
     catch(anna::RuntimeException &ex) {
       ex.trace();
-      response_content = anna::functions::asString("Loaded services from file '%s' with some problems (ignored ones)", servicesFile.c_str());
+      response_content = anna::functions::asString("Loaded services from file '%s' with errors", servicesFile.c_str());
       return;
     }
     response_content = anna::functions::asString("Loaded services from file '%s'", servicesFile.c_str());
@@ -1736,17 +1777,21 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
     }
 
   } else if((opType == "test")) {
-    // test|<id>|<command>[|parameters]   Add a new step to the test case ...
-    // test|ttps|<amount>                 Starts/resume the provided number of time ticks per second (ttps). The ADML starts ...
-    // test|ip-limit[|amount]             In-progress limit of test cases. No new test cases will be launched over this value ...
-    // test|repeats|<amount>              Restarts the whole programmed test list when finished the amount number of times ...
-    // test|report[|[yes]|no]             Every time a test case is finished a report file in xml format will be created under ...
-    // test|report-hex[|[yes]|no]         Reports could include the diameter messages in hexadecimal format. Disabled by default.
-    // test|goto|<id>                     Updates current test pointer position.
-    // test|look[|id]                     Show programmed test case for id provided, current when missing ...
-    // test|interact|amount|id            Makes interactive a specific test case id. The amount is the margin of execution steps ...
-    // test|reset|<soft/hard>[|id]        Reset the test case for id provided, all the tests when missing ...
-    // test|clear                         Clears all the programmed test cases.
+    // test|<id>|<command>[|parameters]         Add a new step to the test case ...
+    // test|ttps|<amount>                       Starts/resume the provided number of time ticks per second (ttps). The ADML starts ...
+    // test|next[|<sync-amount>]                Forces the execution of the next test case(s) without waiting for test manager tick ...
+    // test|ip-limit[|amount]                   In-progress limit of test cases. No new test cases will be launched over this value ...
+    // test|repeats|<amount>                    Restarts the whole programmed test list when finished the amount number of times ...
+    // test|report|<initialized/in-progress/failed/success/all>[|[yes]|no]
+    //                                          Enables/disables report generation for a certain test case state: initialized, in-progress ...
+    // test|report-hex[|[yes]|no]               Reports could include the diameter messages in hexadecimal format. Disabled by default.
+    // test|goto|<id>                           Updates current test pointer position.
+    // test|look[|id]                           Show programmed test case for id provided, current when missing ...
+    // test|interact|amount|id                  Makes interactive a specific test case id. The amount is the margin of execution steps ...
+    // test|reset|<soft/hard>[|id]              Reset the test case for id provided, all the tests when missing ...
+    // test|clear                               Clears all the programmed test cases.
+    // test|summary                             Test manager general report (number of test cases, counts by state ...
+
 
     if(param1 == "ttps") {
       if (numParams > 2)
@@ -1762,6 +1807,22 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
         opt_response_content += "unable to configure the test rate provided";
       }
     }
+    else if (param1 == "next") {
+      if (numParams > 2)
+        throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
+
+      int sync_amount = ((param2 != "") ? atoi(param2.c_str()) : 1);
+
+      if (sync_amount < 1)
+        throw anna::RuntimeException("The parameter 'sync-amount' must be a positive integer value", ANNA_FILE_LOCATION);
+
+      bool success = testManager.execTestCases(sync_amount);
+
+      opt_response_content = (success ? "" : "not completely " /* completed cycle and no repeats, rare case */);
+      opt_response_content += "processed ";
+      opt_response_content += anna::functions::asString(sync_amount);
+      opt_response_content += ((sync_amount > 1) ? " test cases synchronously" : " test case");
+    }
     else if(param1 == "ip-limit") {
       if (numParams > 2)
         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
@@ -1792,12 +1853,30 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
       opt_response_content += anna::functions::asString("Pool repeats: %d%s (current cycle: %d)", repeats, nolimit.c_str(), testManager.getPoolCycle());
     }
     else if(param1 == "report") {
-      if (numParams > 2)
+      if (numParams > 3)
         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
 
-      if(param2 == "") param2 = "yes";
-      testManager.setDumpReports((param2 == "yes"));
-      opt_response_content += (testManager.getDumpReports() ? "report enabled" : "report disabled");
+      if(param2 != "initialized" && param2 != "in-progress" && param2 != "failed" && param2 != "success" && param2 != "all")
+        throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
+
+      if(param3 == "") param3 = "yes";
+      bool enable = (param3 == "yes");
+
+      if(param2 == "initialized")
+        testManager.setDumpInitializedReports(enable);
+      else if(param2 == "in-progress")
+        testManager.setDumpInProgressReports(enable);
+      else if(param2 == "failed")
+        testManager.setDumpFailedReports(enable);
+      else if(param2 == "success")
+        testManager.setDumpSuccessReports(enable);
+      else // all
+        testManager.setDumpAllReports(enable);
+
+      opt_response_content += (enable ? "report enabled " : "report disabled ");
+      opt_response_content += "for tests in '";
+      opt_response_content += param2;
+      opt_response_content += "' state";
     }
     else if(param1 == "report-hex") {
       if (numParams > 2)
@@ -1916,6 +1995,10 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
         opt_response_content = "there are not programmed test cases to be removed";
       }
     }
+    else if(param1 == "summary") {
+      response_content = testManager.asXMLString();
+      return;
+    }
     else {
       int id = atoi(param1.c_str());
       if(id < 0)
@@ -1964,7 +2047,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
         if (numParams > 3)
           throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
         if(param3 == "") throw anna::RuntimeException("Missing milliseconds for 'delay' command in test id operation", ANNA_FILE_LOCATION);
-        anna::Millisecond delay = checkTimeMeasure("Test case delay step", param3);
+        anna::Millisecond delay = ((param3 == "0" /* special case */) ? (anna::Millisecond)0 : checkTimeMeasure("Test case delay step", param3));
         testManager.getTestCase(id)->addDelay(delay); // creates / reuses
       }
       else if ((param2 == "waitfe")||(param2 == "waitfc")) {
@@ -2111,15 +2194,34 @@ throw() {
     it->second->asXML(result);
   }
 
+  // OAM & statistics:
+  oamAsXML(result);
+  statsAsXML(result);
+
+  // Testing: could be heavy if test case reports are enabled
+  TestManager::instantiate().asXML(result);
+
+  return result;
+}
+
+anna::xml::Node* Launcher::oamAsXML(anna::xml::Node* parent) const
+throw() {
+  anna::xml::Node* result = parent->createChild("Oam");
+
   // OAM:
   anna::diameter::comm::OamModule::instantiate().asXML(result);
   anna::diameter::comm::ApplicationMessageOamModule::instantiate().asXML(result);
   anna::diameter::codec::OamModule::instantiate().asXML(result);
+
+  return result;
+}
+
+anna::xml::Node* Launcher::statsAsXML(anna::xml::Node* parent) const
+throw() {
+  anna::xml::Node* result = parent->createChild("Statistics");
+
   // Statistics:
   anna::statistics::Engine::instantiate().asXML(result);
 
-  // Testing: could be heavy if test case reports are enabled
-  //TestManager::instantiate().asXML(result);
-
   return result;
 }