Fix local server for multiple applications
[anna.git] / example / diameter / launcher / EventOperation.cpp
index 2b80529..cab0e83 100644 (file)
 
 // Process
 #include <EventOperation.hpp>
+#include <Launcher.hpp>
+#include <Procedure.hpp>
+#include <MyDiameterEngine.hpp>
+#include <MyLocalServer.hpp>
+#include <anna/testing/TestManager.hpp>
 
-std::string EventOperation::json2piped(const json &j) {
-  std::string result = "operacion piped";
+// Standard
+#include <fstream>
+#include <unistd.h> // chdir
+
+// Project
+#include <anna/diameter.comm/OriginHost.hpp>
+#include <anna/json/functions.hpp>
+#include <anna/diameter/codec/Message.hpp>
+#include <anna/diameter/helpers/base/functions.hpp>
+#include <anna/time/functions.hpp>
+#include <anna/core/functions.hpp>
+#include <anna/xml/xml.hpp>
+#include <anna/diameter.comm/Message.hpp>
+
+
+/////////////////////
+// Node management //
+/////////////////////
+bool EventOperation::node(std::string &response, const std::string & name) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+
+  anna::diameter::comm::OriginHost *workingNode;
+  try { workingNode = my_app.getWorkingNode(); } catch(anna::RuntimeException &ex) { ex.trace(); }
+
+  if (name != "") {
+    if (my_app.setWorkingNode(name)) {
+      response = anna::functions::asString("Forced node is now '%s'", name.c_str());
+      my_app.setOperatedHost(my_app.getWorkingNode()); // now is the new one
+    }
+    else {
+      response = anna::functions::asString("Node '%s' invalid. Nothing done", name.c_str());
+    }
+  }
+  else {
+    if (workingNode) {
+      if (a_http) {
+        response = anna::functions::encodeBase64(workingNode->asXMLString());
+      }
+      else {
+        response = workingNode->asXMLString();
+      }
+    }
+    else {
+      response = "Working node is automatic";
+    }
+  }
+  return true; // OK
+}
+
+bool EventOperation::node_auto(std::string &response) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+
+  my_app.setNodeAuto();
+  response = "Working node has been set to automatic";
+
+  return true; // OK
+}
+
+////////////////////////
+// Parsing operations //
+////////////////////////
+bool EventOperation::code(std::string &response, const std::string & diameterJson) {
+
+  bool success;
+  std::string diameterXml = anna::json::functions::json2xml(diameterJson, success);
+  if (!success) {
+    response += "json to xml failed, unable to encode !";
+    return false;
+  }
+  anna::diameter::codec::Message codecMsg; // auxiliary codec message
+  codecMsg.loadXMLString(diameterXml);
+  response = anna::functions::asHexString(codecMsg.code());
+
+  return true; // OK
+}
+
+bool EventOperation::decode(std::string &response, const std::string & diameterHex) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+
+  anna::DataBlock db_aux(true);
+  anna::functions::fromHexString(diameterHex, db_aux);
+  anna::diameter::codec::Message codecMsg; // auxiliary codec message
+  try {
+    codecMsg.decode(db_aux);
+    std::string xmlString = codecMsg.asXMLString();
+    response = anna::functions::encodeBase64(xmlString);
+  }
+  catch(anna::RuntimeException &ex) { ex.trace(); }
+
+  return true; // OK
+}
+
+bool EventOperation::loadmsg(std::string &response, const std::string & diameterJson) {
+
+  bool success;
+  std::string diameterXml = anna::json::functions::json2xml(diameterJson, success);
+  if (!success) {
+    response += "json to xml failed, unable to load message !";
+    return false;
+  }
+  anna::diameter::codec::Message codecMsg; // auxiliary codec message
+  codecMsg.loadXMLString(diameterXml);
+  response = anna::functions::encodeBase64(codecMsg.asXMLString());
+
+  return true; // OK
+}
+
+/////////////////
+// Hot changes //
+/////////////////
+bool EventOperation::services(std::string &response, const std::string & servicesJson) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+
+  bool success;
+  std::string servicesXml = anna::json::functions::json2xml(servicesJson, success);
+  if (!success) {
+    response += "json to xml failed, unable to load services !";
+    return false;
+  }
+
+  try {
+    my_app.loadServicesFromXMLString(servicesXml, true /* bind entities */);
+  }
+  catch(anna::RuntimeException &ex) {
+    ex.trace();
+    response += "loaded services with errors";
+    return false;
+  }
+  response = "loaded services correctly";
+
+  return true; // OK
+}
+
+bool EventOperation::diameterServerSessions(std::string &response, int sessions) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+
+  try {
+    my_app.getOperatedServer()->setMaxConnections(sessions);
+  }
+  catch(anna::RuntimeException &ex) {
+    ex.trace();
+    response += "fail to operate the server";
+    return false;
+  }
+  response = "new sessions successfully established on operated diameter server";
+
+  return true; // OK
+}
+
+bool EventOperation::change_dir(std::string &response, const std::string & directory) {
 
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+
+  std::string dir = directory;
+  if (dir == "") dir = my_app.getInitialWorkingDirectory();
+  bool result = (chdir(dir.c_str()) == 0);
+
+  if (result)
+    response = "New execution directory configured: ";
+  else
+    response = "Cannot assign provided execution directory: ";
+
+  response += dir;
   return result;
 }
+
+////////////////////////////////
+// Client sessions visibility //
+////////////////////////////////
+bool EventOperation::visibility(std::string &response, const std::string & action, const std::string &addressPort, int socket) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+
+  response = "";
+
+  if(addressPort != "") {
+    if(socket != -1) {
+      std::string key = addressPort;
+      key += "|";
+      key += anna::functions::asString(socket);
+
+      if(action == "hide") my_app.getOperatedEngine()->findClientSession(key)->hide();
+      if(action == "show") my_app.getOperatedEngine()->findClientSession(key)->show();
+      if(action == "hidden") response = my_app.getOperatedEngine()->findClientSession(key)->hidden() ? "true" : "false";
+      if(action == "shown") response = my_app.getOperatedEngine()->findClientSession(key)->shown() ? "true" : "false";
+    } else {
+      std::string address;
+      int port;
+      anna::functions::getAddressAndPortFromSocketLiteral(addressPort, address, port);
+
+      if(action == "hide") my_app.getOperatedEngine()->findServer(address, port)->hide();
+      if(action == "show") my_app.getOperatedEngine()->findServer(address, port)->show();
+      if(action == "hidden") response = my_app.getOperatedEngine()->findServer(address, port)->hidden() ? "true" : "false";
+      if(action == "shown") response = my_app.getOperatedEngine()->findServer(address, port)->shown() ? "true" : "false";
+    }
+  } else {
+    if(action == "hide") my_app.getOperatedEntity()->hide();
+    if(action == "show") my_app.getOperatedEntity()->show();
+    if(action == "hidden") response = my_app.getOperatedEntity()->hidden() ? "true" : "false";
+    if(action == "shown") response = my_app.getOperatedEntity()->shown() ? "true" : "false";
+  }
+
+  return true; // OK
+}
+
+
+///////////////
+// Snapshots //
+///////////////
+bool EventOperation::collect(std::string &response) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+
+  my_app.resetCounters();
+  my_app.resetStatistics();
+  response = "All process counters & statistic information have been reset";
+
+  return true; // OK
+}
+
+bool EventOperation::context(std::string &response, const std::string & targetFile) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+
+  std::string contextFile = (targetFile != "") ? targetFile : anna::functions::asString("/var/tmp/anna.context.%05d", my_app.getPid());
+  my_app.writeContext(contextFile);
+  response = anna::functions::asString("Context dumped on file '%s'", contextFile.c_str());
+
+  return true; // OK
+}
+
+bool EventOperation::forceCountersRecord(std::string &response) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+
+  my_app.forceCountersRecord();
+  response = "Current counters have been dump to disk";
+
+  return true; // OK
+}
+
+bool EventOperation::log_statistics_samples(std::string &response, const std::string & list) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+
+  my_app.logStatisticsSamples(list);
+  response = anna::functions::asString("Log statistics samples for '%s' concepts", list.c_str());
+
+  return true; // OK
+}
+
+bool EventOperation::show_oam(std::string &response) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+
+  anna::xml::Node root("root");
+  response = anna::xml::Compiler().apply(my_app.oamAsXML(&root));
+  if (a_http)
+     response = anna::functions::encodeBase64(response);
+
+  return true; // OK
+}
+
+bool EventOperation::show_stats(std::string &response) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+
+  anna::xml::Node root("root");
+  response = anna::xml::Compiler().apply(my_app.statsAsXML(&root));
+  if (a_http)
+     response = anna::functions::encodeBase64(response);
+
+  return true; // OK
+}
+
+/////////////////////
+// Flow operations //
+/////////////////////
+bool EventOperation::sendmsg_hex_2e(std::string &response, const std::string & diameterJson_or_Hex, bool msg_or_hex) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::diameter::codec::Message codecMsg; // auxiliary codec message
+  bool success;
+  anna::diameter::comm::Message *msg;
+
+  if(msg_or_hex) {
+    std::string diameterXml = anna::json::functions::json2xml(diameterJson_or_Hex, success);
+    if (!success) {
+      response += "json to xml failed, unable to send message !";
+      return false;
+    }
+    codecMsg.loadXMLString(diameterXml);
+    try {
+      my_app.updateOperatedOriginHostWithMessage(codecMsg);
+      msg = my_app.getOperatedHost()->createCommMessage();
+      msg->clearBody();
+    }
+    catch(anna::RuntimeException &ex) {
+      ex.trace();
+      response += "invalid operated host";
+      return false;
+    }
+    try { codecMsg.valid(); } catch(anna::RuntimeException &ex) { ex.trace(); }  // at least we need to see validation errors although it will continue sending (see validation mode configured in launcher)
+    msg->setBody(codecMsg.code());
+  } else {
+    // Get DataBlock from hex content:
+    anna::DataBlock db_aux(true);
+    std::string hexString = diameterJson_or_Hex;
+    hexString.erase(std::remove(hexString.begin(), hexString.end(), ':'), hexString.end());
+    LOGDEBUG(
+        std::string msg = "Hex string (remove colons if exists): ";
+    msg += hexString;
+    anna::Logger::debug(msg, ANNA_FILE_LOCATION);
+    );
+    anna::functions::fromHexString(hexString, db_aux); // could launch exception
+    try {
+      my_app.updateOperatedOriginHostWithMessage(db_aux);
+      msg = my_app.getOperatedHost()->createCommMessage();
+    }
+    catch(anna::RuntimeException &ex) {
+      ex.trace();
+      response += "invalid operated host";
+      return false;
+    }
+    msg->setBody(db_aux);
+    try { if(my_app.getOperatedHost()->logEnabled()) codecMsg.decode(db_aux); } catch(anna::RuntimeException &ex) { ex.trace(); }
+  }
+
+  success = my_app.getOperatedEntity()->send(msg);
+  my_app.getOperatedHost()->releaseCommMessage(msg);
+
+  // Detailed log:
+  if(my_app.getOperatedHost()->logEnabled()) {
+    anna::diameter::comm::Server *usedServer = my_app.getOperatedEntity()->getLastUsedResource();
+    anna::diameter::comm::ClientSession *usedClientSession = usedServer ? usedServer->getLastUsedResource() : NULL;
+    std::string detail = usedClientSession ? usedClientSession->asString() : "[null client session]"; // shouldn't happen
+    my_app.getOperatedHost()->writeLogFile(codecMsg, (success ? "sent2e" : "send2eError"), detail);
+  }
+
+
+  response = "Operation processed"; // could be failed
+  return success;
+}
+
+bool EventOperation::sendmsg_hex_2c(std::string &response, const std::string & diameterJson_or_Hex, bool msg_or_hex) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::diameter::codec::Message codecMsg; // auxiliary codec message
+  bool success;
+  anna::diameter::comm::Message *msg;
+
+  if(msg_or_hex) {
+    std::string diameterXml = anna::json::functions::json2xml(diameterJson_or_Hex, success);
+    if (!success) {
+      response += "json to xml failed, unable to send message !";
+      return false;
+    }
+    codecMsg.loadXMLString(diameterXml);
+    try {
+      my_app.updateOperatedOriginHostWithMessage(codecMsg);
+      msg = my_app.getOperatedHost()->createCommMessage();
+      msg->clearBody();
+    }
+    catch(anna::RuntimeException &ex) {
+      ex.trace();
+      response += "invalid operated host";
+      return false;
+    }
+    try { codecMsg.valid(); } catch(anna::RuntimeException &ex) { ex.trace(); }  // at least we need to see validation errors although it will continue sending (see validation mode configured in launcher)
+    msg->setBody(codecMsg.code());
+  } else {
+    // Get DataBlock from hex content:
+    anna::DataBlock db_aux(true);
+    std::string hexString = diameterJson_or_Hex;
+    hexString.erase(std::remove(hexString.begin(), hexString.end(), ':'), hexString.end());
+    LOGDEBUG(
+        std::string msg = "Hex string (remove colons if exists): ";
+    msg += hexString;
+    anna::Logger::debug(msg, ANNA_FILE_LOCATION);
+    );
+    anna::functions::fromHexString(hexString, db_aux); // could launch exception
+    try {
+      my_app.updateOperatedOriginHostWithMessage(db_aux);
+      msg = my_app.getOperatedHost()->createCommMessage();
+    }
+    catch(anna::RuntimeException &ex) {
+      ex.trace();
+      response += "invalid operated host";
+      return false;
+    }
+    msg->setBody(db_aux);
+    try { if(my_app.getOperatedHost()->logEnabled()) codecMsg.decode(db_aux); } catch(anna::RuntimeException &ex) { ex.trace(); }
+  }
+
+  success = my_app.getOperatedServer()->send(msg);
+  my_app.getOperatedHost()->releaseCommMessage(msg);
+
+  // Detailed log:
+  if(my_app.getOperatedHost()->logEnabled()) {
+    anna::diameter::comm::ServerSession *usedServerSession = my_app.getOperatedServer()->getLastUsedResource();
+    std::string detail = usedServerSession ? usedServerSession->asString() : "[null server session]"; // shouldn't happen
+    my_app.getOperatedHost()->writeLogFile(codecMsg, (success ? "sent2c" : "send2cError"), detail);
+  }
+
+
+  response = "Operation processed"; // could be failed
+  return success;
+}
+
+bool EventOperation::answermsg_action_2e(std::string &response, const std::string & diameterJson_or_action, bool msg_or_action) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::diameter::codec::Message codecMsg; // auxiliary codec message
+  anna::diameter::codec::Message *message;
+  bool success;
+
+  if (msg_or_action) {
+
+    std::string diameterXml = anna::json::functions::json2xml(diameterJson_or_action, success);
+    if (!success) {
+      response += "json to xml failed, unable to send message !";
+      return false;
+    }
+    codecMsg.loadXMLString(diameterXml);
+    try {
+      my_app.updateOperatedOriginHostWithMessage(codecMsg);
+      message = my_app.getOperatedHost()->getCodecEngine()->createMessage(diameterXml, false /* is xml string */); // loads xml again, lesser of two evils
+      LOGDEBUG(anna::Logger::debug(message->asXMLString(), ANNA_FILE_LOCATION));
+    }
+    catch(anna::RuntimeException &ex) {
+      ex.trace();
+      response += "invalid operated host";
+      return false;
+    }
+
+    if(message->isRequest()) {
+      response += "cannot program diameter requests. Answer type must be provided";
+      return false;
+    }
+
+    int code = message->getId().first;
+    LOGDEBUG(anna::Logger::debug("Adding a new programed 'answer to entity' to the FIFO queue corresponding to its message code ...", ANNA_FILE_LOCATION));
+    response = "Added 'answer to entity' to the FIFO queue corresponding to its message code";
+    my_app.getOperatedHost()->getReactingAnswers()->addMessage(code, message);
+  }
+  else { // action
+
+    if(diameterJson_or_action == "list") { // programmed answers FIFO's to stdout
+      response = anna::functions::encodeBase64(my_app.getOperatedHost()->getReactingAnswers()->asString("ANSWERS TO ENTITY"));
+    } else if (diameterJson_or_action == "rotate") {
+      my_app.getOperatedHost()->getReactingAnswers()->rotate(true);
+      response = "rotate";
+    } else if (diameterJson_or_action == "exhaust") {
+      my_app.getOperatedHost()->getReactingAnswers()->rotate(false);
+      response = "exhaust";
+    } else if (diameterJson_or_action == "clear") {
+      my_app.getOperatedHost()->getReactingAnswers()->clear();
+      response = "clear";
+    } else if (diameterJson_or_action == "dump") {
+      my_app.getOperatedHost()->getReactingAnswers()->dump("programmed_answer");
+      response = "dump";
+    }
+  }
+
+
+  return true; // OK
+}
+
+bool EventOperation::answermsg_action_2c(std::string &response, const std::string & diameterJson_or_action, bool msg_or_action) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::diameter::codec::Message codecMsg; // auxiliary codec message
+  anna::diameter::codec::Message *message;
+  bool success;
+
+  if (msg_or_action) {
+
+    std::string diameterXml = anna::json::functions::json2xml(diameterJson_or_action, success);
+    if (!success) {
+      response += "json to xml failed, unable to send message !";
+      return false;
+    }
+    codecMsg.loadXMLString(diameterXml);
+    try {
+      my_app.updateOperatedOriginHostWithMessage(codecMsg);
+      message = my_app.getOperatedHost()->getCodecEngine()->createMessage(diameterXml, false /* is xml string */); // loads xml again, lesser of two evils
+      LOGDEBUG(anna::Logger::debug(message->asXMLString(), ANNA_FILE_LOCATION));
+    }
+    catch(anna::RuntimeException &ex) {
+      ex.trace();
+      response += "invalid operated host";
+      return false;
+    }
+
+    if(message->isRequest()) {
+      response += "cannot program diameter requests. Answer type must be provided";
+      return false;
+    }
+
+    int code = message->getId().first;
+    LOGDEBUG(anna::Logger::debug("Adding a new programed 'answer to client' to the FIFO queue corresponding to its message code ...", ANNA_FILE_LOCATION));
+    my_app.getOperatedHost()->getReactingAnswers()->addMessage(code, message);
+    response = "Added 'answer to client' to the FIFO queue corresponding to its message code";
+  }
+  else { // action
+
+    if(diameterJson_or_action == "list") { // programmed answers FIFO's to stdout
+      response = anna::functions::encodeBase64(my_app.getOperatedHost()->getReactingAnswers()->asString("ANSWERS TO CLIENT"));
+    } else if (diameterJson_or_action == "rotate") {
+      my_app.getOperatedHost()->getReactingAnswers()->rotate(true);
+      response = "rotate";
+    } else if (diameterJson_or_action == "exhaust") {
+      my_app.getOperatedHost()->getReactingAnswers()->rotate(false);
+      response = "exhaust";
+    } else if (diameterJson_or_action == "clear") {
+      my_app.getOperatedHost()->getReactingAnswers()->clear();
+      response = "clear";
+    } else if (diameterJson_or_action == "dump") {
+      my_app.getOperatedHost()->getReactingAnswers()->dump("programmed_answer");
+      response = "dump";
+    }
+  }
+
+
+  return true; // OK
+}
+
+/////////////////
+// FSM testing //
+/////////////////
+bool EventOperation::test_id__description(std::string &response, unsigned int id, const std::string & description) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  try {
+    anna::testing::TestCase *tc = testManager.getTestCase(id); // creates / reuses
+    tc->setDescription(description);
+    response = std::to_string(tc->getId());
+  }
+  catch(anna::RuntimeException &ex) {
+    ex.trace();
+    response += "invalid ip-limit";
+    return false;
+  }
+
+  return true; // OK
+}
+
+bool EventOperation::test_id__ip_limit(std::string &response, unsigned int id, int amount) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  try {
+    anna::testing::TestCase *tc = testManager.getTestCase(id); // creates / reuses
+    tc->addIpLimit(amount);
+    response = std::to_string(tc->getId());
+  }
+  catch(anna::RuntimeException &ex) {
+    ex.trace();
+    response += "invalid ip-limit";
+    return false;
+  }
+
+  return true; // OK
+}
+
+bool EventOperation::test_id__timeout(std::string &response, unsigned int id, int msecs) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  try {
+    anna::Millisecond timeout = my_app.checkTimeMeasure("Test case timeout", anna::functions::asString(msecs));
+    anna::testing::TestCase *tc = testManager.getTestCase(id); // creates / reuses
+    tc->addTimeout(timeout);
+    response = std::to_string(tc->getId());
+  }
+  catch(anna::RuntimeException &ex) {
+    ex.trace();
+    response += "invalid timeout";
+    return false;
+  }
+
+  return true; // OK
+}
+
+bool EventOperation::test_id__sendmsg2e_2c(std::string &response, unsigned int id, bool _2e_or_2c, const std::string & diameterJson, int stepNumber) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  bool success;
+  std::string diameterXml = anna::json::functions::json2xml(diameterJson, success);
+  if (!success) {
+    response += "json to xml failed, unable to load message !";
+    return false;
+  }
+  anna::diameter::codec::Message codecMsg; // auxiliary codec message
+  codecMsg.loadXMLString(diameterXml);
+
+  LOGWARNING(
+     if (!codecMsg.isRequest() && (stepNumber == -1))
+      anna::Logger::warning("Step number has not been provided. Take into account that this answer message will be sent 'as is' and sequence information could be wrong at the remote peer", ANNA_FILE_LOCATION);
+  );
+
+  try {
+    anna::testing::TestCase *tc = testManager.getTestCase(id); // creates / reuses
+    my_app.updateOperatedOriginHostWithMessage(codecMsg);
+    if (_2e_or_2c)
+      tc->addSendDiameterXml2e(codecMsg.code(), my_app.getOperatedHost(), stepNumber);
+    else
+      tc->addSendDiameterXml2c(codecMsg.code(), my_app.getOperatedHost(), stepNumber);
+
+    response = std::to_string(tc->getId());
+  }
+  catch(anna::RuntimeException &ex) {
+    ex.trace();
+    response += "failed";
+    return false;
+  }
+
+  return true; // OK
+}
+
+bool EventOperation::test_id__delay(std::string &response, unsigned int id, int msecs) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  try {
+    anna::testing::TestCase *tc = testManager.getTestCase(id); // creates / reuses
+    anna::Millisecond delay = ((msecs == 0 /* special case */) ? (anna::Millisecond)0 : my_app.checkTimeMeasure("Test case delay step", anna::functions::asString(msecs)));
+    tc->addDelay(delay); // creates / reuses
+    response = std::to_string(tc->getId());
+  }
+  catch(anna::RuntimeException &ex) {
+    ex.trace();
+    response += "invalid delay";
+    return false;
+  }
+
+  return true; // OK
+}
+
+bool EventOperation::test_id__sh_command(std::string &response, unsigned int id, const std::string & script) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  try {
+    anna::testing::TestCase *tc = testManager.getTestCase(id); // creates / reuses
+    tc->addCommand(script); // creates / reuses
+    response = std::to_string(tc->getId());
+  }
+  catch(anna::RuntimeException &ex) {
+    ex.trace();
+    response += "failed";
+    return false;
+  }
+
+  return true; // OK
+}
+
+bool EventOperation::test_id__waitfefc_hex(std::string &response, unsigned int id, bool fe_or_fc, const std::string & hex, bool strict) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  // Get DataBlock from hex content:
+  anna::DataBlock db_aux(true);
+  std::string hexString = hex;
+  hexString.erase(std::remove(hexString.begin(), hexString.end(), ':'), hexString.end());
+  LOGDEBUG(
+      std::string msg = "Hex string (remove colons if exists): ";
+  msg += hexString;
+  anna::Logger::debug(msg, ANNA_FILE_LOCATION);
+  );
+
+  std::string regexp;
+  try {
+    anna::functions::fromHexString(hexString, db_aux); // could launch exception
+    regexp = anna::functions::asHexString(db_aux);
+  }
+  catch(anna::RuntimeException &ex) {
+    ex.trace();
+    response += "failed";
+    return false;
+  }
+
+  // optional 'full':
+  if(!strict) {
+    //// If request, we will ignore sequence data:
+    //if (anna::diameter::codec::functions::requestBit(db_aux))
+    regexp.replace (24, 16, "[0-9A-Fa-f]{16}");
+
+    regexp.insert(0, "^");
+    regexp += "$";
+  }
+
+  try {
+    anna::testing::TestCase *tc = testManager.getTestCase(id); // creates / reuses
+    tc->addWaitDiameterRegexpHex(fe_or_fc, regexp);
+    response = std::to_string(tc->getId());
+  }
+  catch(anna::RuntimeException &ex) {
+    ex.trace();
+    response += "failed";
+    return false;
+  }
+
+  return true; // OK
+}
+
+bool EventOperation::test_id__waitfefc_msg(std::string &response, unsigned int id, bool fe_or_fc, const std::string & diameterJson, bool strict) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  bool success;
+  std::string diameterXml = anna::json::functions::json2xml(diameterJson, success);
+  if (!success) {
+    response += "json to xml failed, unable to load message !";
+    return false;
+  }
+
+  try {
+    anna::diameter::codec::Message codecMsg; // auxiliary codec message
+    codecMsg.loadXMLString(diameterXml);
+    std::string regexp = codecMsg.asXMLString(true /* normalization */);
+
+    // Now we must insert regular expressions in hop-by-hop, end-to-end and Origin-State-Id:
+
+    // optional 'full':
+    if(!strict) {
+      std::string::size_type pos, pos_1, pos_2;
+
+      pos = regexp.find("end-to-end-id=", 0u);
+      if (pos != std::string::npos) {
+        pos = regexp.find("\"", pos);
+        pos_1 = pos;
+        pos = regexp.find("\"", pos+1);
+        pos_2 = pos;
+        regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
+      }
+
+      pos = regexp.find("hop-by-hop-id=", 0u);
+      if (pos != std::string::npos) {
+        pos = regexp.find("\"", pos);
+        pos_1 = pos;
+        pos = regexp.find("\"", pos+1);
+        pos_2 = pos;
+        regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
+      }
+
+      // For this representation: <avp name="Origin-State-Id" data="1428633668"/>
+      //pos = regexp.find("Origin-State-Id", 0u);
+      //pos = regexp.find("\"", pos);
+      //pos = regexp.find("\"", pos+1);
+      //pos_1 = pos;
+      //pos = regexp.find("\"", pos+1);
+      //pos_2 = pos;
+      //regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
+      // But we have this one: <avp data="1428633668" name="Origin-State-Id"/>
+      pos = regexp.find("Origin-State-Id", 0u);
+      if (pos != std::string::npos) {
+        pos = regexp.rfind("\"", pos);
+        pos = regexp.rfind("\"", pos-1);
+        pos = regexp.rfind("\"", pos-1);
+        pos_1 = pos;
+        pos = regexp.find("\"", pos+1);
+        pos_2 = pos;
+        regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
+      }
+
+      //regexp.insert(0, "^");
+      //regexp += "$";
+    }
+
+    anna::testing::TestCase *tc = testManager.getTestCase(id); // creates / reuses
+    tc->addWaitDiameterRegexpXml(fe_or_fc, regexp);
+    response = std::to_string(tc->getId());
+  }
+  catch(anna::RuntimeException &ex) {
+    ex.trace();
+    response += "failed";
+    return false;
+  }
+
+  return true; // OK
+}
+
+bool EventOperation::test_id__waitfefc(std::string &response, unsigned int id, bool fe_or_fc,
+                         const std::string & code,
+                         const std::string & bitR,
+                         const std::string & hopByHop,
+                         const std::string & applicationId,
+                         const std::string & sessionId,
+                         const std::string & resultCode,
+                         const std::string & msisdn,
+                         const std::string & imsi,
+                         const std::string & serviceContextId) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  try { // [code]|[bitR]|[hopByHop]|[applicationId]|[sessionId]|[resultCode]|[msisdn]|[imsi]|[serviceContextId]
+    anna::testing::TestCase *tc = testManager.getTestCase(id); // creates / reuses
+    tc->addWaitDiameter(fe_or_fc, code, bitR, hopByHop, applicationId, sessionId, resultCode, msisdn, imsi, serviceContextId);
+    response = std::to_string(tc->getId());
+  }
+  catch(anna::RuntimeException &ex) {
+    ex.trace();
+    response += "failed";
+    return false;
+  }
+
+  return true; // OK
+}
+
+/////////////////////////
+// Testcases execution //
+/////////////////////////
+bool EventOperation::test__ttps(std::string &response, int amount) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  bool success = testManager.configureTTPS(amount);
+  if (success) {
+    response = "Assigned new test launch rate to ";
+    response += anna::functions::asString(amount);
+    response += " events per second";
+  }
+  else {
+    response += "unable to configure the test rate provided";
+  }
+
+  return success; // OK
+}
+
+bool EventOperation::test__next(std::string &response, int syncAmount) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  if (syncAmount < 1) {
+    response += "the parameter 'sync-amount' must be a positive integer value";
+    return false;
+  }
+
+  bool success = testManager.execTestCases(syncAmount);
+
+  response = (success ? "P" : "Not completely p" /* completed cycle and no repeats, rare case */);
+  response += "rocessed ";
+  response += anna::functions::asString(syncAmount);
+  response += ((syncAmount > 1) ? " test cases synchronously" : " test case");
+
+  return success;
+}
+
+bool EventOperation::test__ip_limit(std::string &response, int amount) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  if (amount > -2) {
+    testManager.setInProgressLimit(amount);
+    response = "New in-progress limit: ";
+    response += (amount != -1) ? anna::functions::asString(amount) : "[no limit]";
+  }
+  else {
+    response = "In-progress limit amount: ";
+    int limit = testManager.getInProgressLimit();
+    response += (limit != -1) ? anna::functions::asString(limit) : "[no limit]";
+    response += "; currently there are ";
+    response += anna::functions::asString(testManager.getInProgressCount());
+    response += " test cases running";
+  }
+
+  return true; // OK
+}
+
+bool EventOperation::test__goto(std::string &response, int id) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  bool success = testManager.gotoTestCase(id);
+
+  if (success) {
+    response = "Position updated for id provided (";
+  }
+  else {
+    response += "cannot found test id (";
+  }
+  response += anna::functions::asString(id);
+  response += ")";
+
+  return success;
+}
+
+bool EventOperation::test__run(std::string &response, int id) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  bool success = testManager.runTestCase(id);
+
+  if (success) {
+    response = "Test executed for id provided (";
+  }
+  else {
+    response += "cannot found test id (";
+  }
+  response += anna::functions::asString(id);
+  response += ")";
+
+  return success;
+}
+
+bool EventOperation::test__look(std::string &response, int id) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  anna::testing::TestCase *testCase = testManager.findTestCase(id);
+  if (!testCase) {
+    if (id == -1) {
+       response += "no current test case detected (testing started ?)";
+    }
+    else {
+      response += "cannot found test id (";
+      response += anna::functions::asString(id);
+      response += ")";
+    }
+
+    return false;
+  }
+
+  if (a_http)
+    response = anna::functions::encodeBase64(testCase->asXMLString());
+  else
+    response = testCase->asXMLString();
+
+  return true; // OK
+}
+
+bool EventOperation::test__state(std::string &response, int id) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  anna::testing::TestCase *testCase = testManager.findTestCase(id);
+  if (!testCase) {
+    if (id == -1) {
+       response += "no current test case detected (testing started ?)";
+    }
+    else {
+      response += "cannot found test id (";
+      response += anna::functions::asString(id);
+      response += ")";
+    }
+
+    return false;
+  }
+
+  response = anna::testing::TestCase::asText(testCase->getState());
+  return testCase->isSuccess();
+}
+
+bool EventOperation::test__interact(std::string &response, int amount, unsigned int id) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  if (amount < -1) {
+    response += "interactive amount must be -1 (to disable interactive mode) or a positive number.";
+    return false;
+  }
+
+  anna::testing::TestCase *testCase = testManager.findTestCase(id);
+  if (testCase) {
+    if (amount == -1) {
+      testCase->makeInteractive(false);
+      response = "Interactive mode disabled";
+    }
+    else {
+      testCase->addInteractiveAmount(amount);
+      response = "Added interactive amount of ";
+      response += anna::functions::asString(amount);
+      response += " units";
+      if (amount == 0) response += " (0: freezing a non-interactive testcase, no effect on already interactive)";
+    }
+    response += " for test case id ";
+    response += anna::functions::asString(id);
+  }
+  else {
+    response += "cannot found test id (";
+    response += anna::functions::asString(id);
+    response += ")";
+    return false;
+  }
+
+  return true; // OK
+}
+
+bool EventOperation::test__reset(std::string &response, bool soft_hard, int id) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  anna::testing::TestCase *testCase = ((id != -1) ? testManager.findTestCase(id) : NULL);
+  if (testCase) {
+    bool done = testCase->reset(!soft_hard);
+    response = "Test ";
+    response += (soft_hard ? "soft":"hard");
+    response += " reset for id ";
+    response += anna::functions::asString(id);
+    response += done ? ": done": ": not done";
+  }
+  else {
+    if (id == -1) {
+      bool anyReset = testManager.resetPool(!soft_hard);
+      response = (soft_hard ? "Soft":"Hard");
+      response += " reset have been sent to all programmed tests: "; response += anyReset ? "some/all have been reset" : "nothing was reset";
+    }
+    else {
+      response += "cannot found test id (";
+      response += anna::functions::asString(id);
+      response += ")";
+      return false;
+    }
+  }
+
+  return true; // OK
+}
+
+bool EventOperation::test__repeats(std::string &response, int amount) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  if (amount < 0) amount = -1;
+  testManager.setPoolRepeats(amount);
+  std::string nolimit = (amount != -1) ? "":" [no limit]";
+  response = anna::functions::asString("Pool repeats: %d%s (current cycle: %d)", amount, nolimit.c_str(), testManager.getPoolCycle());
+
+  return true; // OK
+}
+
+bool EventOperation::test__auto_reset(std::string &response, bool soft_hard) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  testManager.setAutoResetHard(!soft_hard);
+  response = anna::functions::asString("Auto-reset configured to '%s'", (soft_hard ? "soft":"hard"));
+
+  return true; // OK
+}
+
+bool EventOperation::test__initialized(std::string &response) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  response = anna::functions::asString("%lu", testManager.getInitializedCount());
+
+  return true; // OK
+}
+
+bool EventOperation::test__finished(std::string &response) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  response = anna::functions::asString("%lu", testManager.getFinishedCount());
+
+  return true; // OK
+}
+
+bool EventOperation::test__clear(std::string &response, int id) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  if (id == -1) {
+     return testManager.clearPool(response);
+  }
+
+  if (!testManager.findTestCase(id)) {
+      response += "cannot found test id (";
+      response += anna::functions::asString(id);
+      response += ")";
+      return false;
+  }
+
+  return testManager.clearTestCase(response, id);
+}
+
+bool EventOperation::test__junit(std::string &response, const std::string & targetFile) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  std::ofstream out;
+  out.open(targetFile.c_str());
+
+  if(out.is_open() == false) {
+    response += "error opening '";
+    response += targetFile;
+    response += "'";
+    return false;
+  }
+
+  out << testManager.junitAsXMLString() << std::endl;
+  out.close();
+
+  response = "Junit report written on '";
+  response += targetFile;
+  response += "'";
+
+  return true; // OK
+}
+
+bool EventOperation::test__summary_counts(std::string &response) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  response = anna::functions::encodeBase64(testManager.summaryCounts());
+
+  return true; // OK
+}
+
+bool EventOperation::test__summary_states(std::string &response) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  response = anna::functions::encodeBase64(testManager.summaryStates());
+
+  return true; // OK
+}
+
+bool EventOperation::test__summary(std::string &response) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  response = anna::functions::encodeBase64(testManager.asXMLString());
+
+  return true; // OK
+}
+
+bool EventOperation::test__report(std::string &response, const std::string & state, bool enable) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  std::string _state = state;
+
+  if(_state == "initialized")
+    testManager.setDumpInitializedReports(enable);
+  else if(_state == "in-progress")
+    testManager.setDumpInProgressReports(enable);
+  else if(_state == "failed")
+    testManager.setDumpFailedReports(enable);
+  else if(_state == "success")
+    testManager.setDumpSuccessReports(enable);
+  else if(_state == "all") {
+    _state = "any";
+    testManager.setDumpAllReports(enable);
+  }
+  else if(_state == "none") {
+    enable = !enable;
+    _state = "any";
+    testManager.setDumpAllReports(enable);
+  }
+  else {
+    response += "invalid state (allowed: initialized|in-progress|failed|success|[all]|none)";
+    return false;
+  }
+
+  response = (enable ? "Report enabled " : "Report disabled ");
+  response += "for tests in '";
+  response += _state;
+  response += "' state";
+
+  return true; // OK
+}
+
+bool EventOperation::test__report_hex(std::string &response, bool enable) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  testManager.setDumpHex(enable);
+  response = (testManager.getDumpHex() ? "Report includes hexadecimal messages" : "Report excludes hexadecimal messages");
+
+  return true; // OK
+}
+
+bool EventOperation::test__dump_stdout(std::string &response, bool enable) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
+
+  testManager.setDumpStdout(enable);
+  response = (testManager.getDumpStdout() ? "Test manager dumps progress into stdout" : "Test manager does not dump progress into stdout");
+
+  return true; // OK
+}
+
+bool EventOperation::test__dynamic(std::string &response, const nlohmann::json &arguments) {
+
+  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+
+  Procedure p(&my_app);
+  try {
+    p.execute(arguments, response);
+  }
+  catch(anna::RuntimeException &ex) {
+    ex.trace();
+    response += ex.asString();
+    return false;
+  }
+
+  return true; // OK
+}
+
+