Add unix timestamp milliseconds to dumpLog messages
[anna.git] / example / diameter / launcher / Launcher.cpp
index b2ee171..aaa80be 100644 (file)
@@ -11,6 +11,7 @@
 #include <iostream>     // std::cout
 #include <math.h> // ceil
 #include <climits>
+#include <unistd.h> // chdir
 
 // Project
 #include <anna/timex/Engine.hpp>
@@ -33,8 +34,8 @@
 #include <TestCase.hpp>
 
 
-#define SIGUSR2_TASKS_INPUT_FILENAME "./sigusr2.in"
-#define SIGUSR2_TASKS_OUTPUT_FILENAME "./sigusr2.out"
+#define SIGUSR2_TASKS_INPUT_FILENAME "sigusr2.in"
+#define SIGUSR2_TASKS_OUTPUT_FILENAME "sigusr2.out"
 
 
 const char *ServicesDTD = "\
@@ -100,10 +101,10 @@ const char *ServicesDTD = "\
    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\
-   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\
+   detailedLog:                             Insert detailed information at log files (timestamps, communication resources, etc.). Useful\n\
+                                            to analyze the messages flow along the sockets (specially on 'balance' mode). Default 'no'.\n\
    dumpLog:                                 Write to disk every incoming/outcoming message named as:\n\
-                                               '<originHost>.<hop by hop>.<end to end>.<message code>.<request|answer>.<type of event>.xml'\n\
+                                               '<unix ms timestamp>.<originHost>.<hop by hop>.<end to end>.<message code>.<request|answer>.<type of event>.xml'\n\
                                             Default value 'no'.\n\
    burstLog:                                Burst operations log file. By default '<originHost>.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\
@@ -129,6 +130,13 @@ Launcher::Launcher() : anna::comm::Application("launcher", "DiameterLauncher", "
 }
 
 
+std::string Launcher::getSignalUSR2InputFile() const throw() {
+  return (a_initialWorkingDirectory + "/" + SIGUSR2_TASKS_INPUT_FILENAME);
+}
+
+std::string Launcher::getSignalUSR2OutputFile() const throw() {
+  return (a_initialWorkingDirectory + "/" + SIGUSR2_TASKS_OUTPUT_FILENAME);
+}
 
 
 void Launcher::servicesFromXML(const anna::xml::Node* servicesNode, bool eventOperation) throw(anna::RuntimeException) {
@@ -203,7 +211,7 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode, bool eventOp
       const anna::xml::Attribute *vm_attr = (*it)->getAttribute("validationMode", false /* no exception */);
       const anna::xml::Attribute *vd_attr = (*it)->getAttribute("validationDepth", false /* no exception */);
       const anna::xml::Attribute *fm_attr = (*it)->getAttribute("fixMode", false /* no exception */);
-      const anna::xml::Attribute *if_attr = (*it)->getAttribute("ignoreFlags", false /* no exception */);
+      const anna::xml::Attribute *if_attr = (*it)->getAttribute("ignoreFlagsOnValidation", false /* no exception */);
 
       std::string vm_value = vm_attr ? vm_attr->getValue() : "AfterDecoding";
       std::string vd_value = vd_attr ? vd_attr->getValue() : "FirstError";
@@ -566,6 +574,13 @@ throw(anna::RuntimeException) {
 
   // Start time:
   a_start_time.setNow();
+
+  // Initial working directory:
+  char cwd[1024];
+  if (getcwd(cwd, sizeof(cwd)) == NULL)
+    throw anna::RuntimeException("Cannot retrieve initial working directory !!", ANNA_FILE_LOCATION);
+  a_initialWorkingDirectory = cwd;
+
   // Statistics:
   anna::statistics::Engine::instantiate().enable();
 
@@ -801,11 +816,15 @@ void Launcher::resetCounters() throw() {
 }
 
 void Launcher::signalUSR2() throw(anna::RuntimeException) {
+
+  std::string inputFile = getSignalUSR2InputFile();
+  std::string outputFile = getSignalUSR2OutputFile();
+
   LOGNOTICE(
   std::string msg = "Captured signal SIGUSR2. Reading tasks at '";
-  msg += SIGUSR2_TASKS_INPUT_FILENAME;
+  msg += inputFile;
   msg += "' (results will be written at '";
-  msg += SIGUSR2_TASKS_OUTPUT_FILENAME;
+  msg += outputFile;
   msg += "')";
   anna::Logger::notice(msg, ANNA_FILE_LOCATION);
   );
@@ -813,13 +832,18 @@ void Launcher::signalUSR2() throw(anna::RuntimeException) {
   // Operation:
   std::string line;
   std::string response_content;
-  std::ifstream in_file(SIGUSR2_TASKS_INPUT_FILENAME);
-  std::ofstream out_file(SIGUSR2_TASKS_OUTPUT_FILENAME);
+  std::ifstream in_file(inputFile);
+  std::ofstream out_file(outputFile);
 
   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)) {
+
+    // Ignore comments and blank lines:
+    if (line[0] == '#')  continue;
+    if (std::string::npos == line.find_first_not_of(" \t")) continue;
+
     LOGDEBUG(
         std::string msg = "Processing line: ";
     msg += line;
@@ -994,6 +1018,11 @@ std::string Launcher::help() const throw() {
   result += "\n                                     This operation applies over all the registered host 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 += "\nchange-dir[|directory]               Changes the execution point which could be fine to ease some";
+  result += "\n                                     file system interaction tasks. Be care about some requirements";
+  result += "\n                                     (for example if you have a user defined counters directory as";
+  result += "\n                                     relative path this must exists from the new execution directory).";
+  result += "\n                                     If nothing provided, initial working directory will be restored.";
   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";
@@ -1347,7 +1376,7 @@ std::string Launcher::help() const throw() {
   result += "\n                                 steps margin, which could be useful to 'freeze' a test in the middle of its execution.";
   result += "\n                                 You could also provide -1 to make it non-interactive resuming it from the current step.";
   result += "\n";
-  result += "\n   test|reset|<soft/hard>[|id]   Reset the test case for id provided, all the tests when missing. It could be hard/soft:";
+  result += "\n   test|reset|<[soft]/hard>[|id] Reset the test case for id provided, all the tests when missing. It could be hard/soft:";
   result += "\n                                 - hard: you probably may need to stop the load rate before. This operation initializes";
   result += "\n                                         all test cases regardless their states.";
   result += "\n                                 - soft: only for finished cases (those with 'Success' or 'Failed' states). It does not";
@@ -1366,7 +1395,7 @@ std::string Launcher::help() const throw() {
   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/none>[|[yes]|no]";
+  result += "\n   test|report|<initialized/in-progress/failed/success/[all]/none>[|[yes]|no]";
   result += "\n";
   result += "\n                                 Enables/disables report generation for a certain test case state: initialized, in-progress,";
   result += "\n                                 failed or success (also 'all' and 'none' reserved words could be used). This applies to report";
@@ -1404,13 +1433,13 @@ std::string Launcher::help() const throw() {
   result += "\n";
   result += "\nThe alternative using SIGUSR2 signal requires the creation of the task(s) file which will be read at";
   result += "\n signal event:";
-  result += "\n   echo \"<<operation>\" > "; result += SIGUSR2_TASKS_INPUT_FILENAME;
+  result += "\n   echo \"<<operation>\" > "; result += getSignalUSR2InputFile();
   result += "\n    then";
   result += "\n   kill -12 <pid>";
   result += "\n    or";
   result += "\n   kill -s SIGUSR2 <pid>";
   result += "\n    and then see the results:";
-  result += "\n   cat "; result += SIGUSR2_TASKS_OUTPUT_FILENAME;
+  result += "\n   cat "; result += getSignalUSR2OutputFile();
   result += "\n";
   result += "\n   (this file is ended with EOF final line, useful managing huge batch files to ensure the job completion)";
   result += "\n";
@@ -1430,7 +1459,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
   LOGDEBUG(anna::Logger::debug(operation, ANNA_FILE_LOCATION));
 
   // Default response:
-  response_content = "Operation processed with exception (see traces): ";
+  response_content = "Operation processed with exception: ";
   response_content += operation;
   std::string opt_response_content = ""; // aditional response content
   anna::DataBlock db_aux(true);
@@ -1520,7 +1549,9 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
   // Check the number of parameters:
   bool wrongBody = false;
 
+  if((opType == "change-dir") && (numParams > 1)) wrongBody = true;
   if((opType == "node") && (numParams > 1)) wrongBody = true;
+
   if((opType == "node_auto") && (numParams > 0)) wrongBody = true;
 
   if(((opType == "code") || (opType == "decode")) && (numParams != 2)) wrongBody = true;
@@ -1549,6 +1580,18 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
     return;
   }
 
+  // Change execution directory:
+  if(opType == "change-dir") {
+    if (param1 == "") param1 = a_initialWorkingDirectory;
+    if (chdir(param1.c_str()) == 0)
+      response_content = "New execution directory configured: ";
+    else
+      response_content = "Cannot assign provided execution directory: ";
+
+    response_content += param1;
+    return;
+  }
+
   if(opType == "services") {
     std::string servicesFile = ((numParams == 1) ? param1 : "services.xml");
     try {
@@ -1782,13 +1825,13 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
     // 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/none>[|[yes]|no]
+    // test|report|<initialized/in-progress/failed/success/[all]/none>[|[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|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 ...
 
@@ -1856,6 +1899,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(param2 == "") param2 = "all";
       if(param3 == "") param3 = "yes";
       bool enable = (param3 == "yes");
 
@@ -1867,10 +1911,15 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
         testManager.setDumpFailedReports(enable);
       else if(param2 == "success")
         testManager.setDumpSuccessReports(enable);
-      else if(param2 == "all")
+      else if(param2 == "all") {
+        param2 = "any";
+        testManager.setDumpAllReports(enable);
+      }
+      else if(param2 == "none") {
+        enable = !enable;
+        param2 = "any";
         testManager.setDumpAllReports(enable);
-      else if(param2 == "none")
-        testManager.setDumpAllReports(!enable);
+      }
       else
         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
 
@@ -1959,7 +2008,8 @@ 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 (param2 != "soft" && param2 != "hard")
+      if(param2 == "") param2 = "soft";
+     if (param2 != "soft" && param2 != "hard")
         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
 
       int id = ((param3 != "") ? atoi(param3.c_str()) : -1);
@@ -1976,7 +2026,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
       else {
         if (id == -1) {
           bool anyReset = testManager.resetPool((param2 == "hard") ? true:false);
-          opt_response_content = "reset have been sent to all programmed tests: "; opt_response_content += anyReset ? "some/all have been reset" : "nothing was reset";
+          opt_response_content = param2; opt_response_content += " reset have been sent to all programmed tests: "; opt_response_content += anyReset ? "some/all have been reset" : "nothing was reset";
         }
         else {
           opt_response_content = "cannot found test id (";
@@ -2074,10 +2124,13 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
         }
       }
       else if (param2 == "sh-command") {
-        if (numParams > 4)
-          throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
+        // Allow pipes in command:
+        //if (numParams > 4)
+        //  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 script/executable command-line for 'sh-command' in test id operation", ANNA_FILE_LOCATION);
-        testManager.getTestCase(id)->addCommand(param3); // creates / reuses
+        std::string token = "|sh-command|";
+        std::string command = operation.substr(operation.find(token) + token.size());
+        testManager.getTestCase(id)->addCommand(command); // creates / reuses
       }
       else {
         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
@@ -2189,12 +2242,17 @@ throw() {
   anna::comm::Application::asXML(result);
   // Timming:
   result->createAttribute("StartTime", a_start_time.asString());
+  result->createAttribute("InitialWorkingDirectory", a_initialWorkingDirectory);
   result->createAttribute("SecondsLifeTime", anna::time::functions::lapsedMilliseconds() / 1000);
   // Diameter:
   for (origin_hosts_it it = a_originHosts.begin(); it != a_originHosts.end(); it++) {
     it->second->asXML(result);
   }
 
+  // Registered codec engines:
+  anna::diameter::codec::EngineManager &em = anna::diameter::codec::EngineManager::instantiate();
+  em.asXML(result);
+
   // OAM & statistics:
   oamAsXML(result);
   statsAsXML(result);