Add second work package for REST API implementation
authorEduardo Ramos Testillano (eramedu) <eduardo.ramos.testillano@ericsson.com>
Sun, 10 May 2020 20:18:42 +0000 (22:18 +0200)
committerEduardo Ramos Testillano (eramedu) <eduardo.ramos.testillano@ericsson.com>
Sun, 10 May 2020 20:21:57 +0000 (22:21 +0200)
Complete flow operations
Add updateOperatedOriginHostWithMessage and getOriginHost to
work with datablocks.
This improve also legacy code (sigusr2 interface is gaining
with this feature when managing operation which uses hexadecimal
messages

Codec engine implementation adds also a createMessage version
working with xml representation, not only with xml file.

27 files changed:
example/diameter/launcher/EventOperation.cpp
example/diameter/launcher/EventOperation.hpp
example/diameter/launcher/Launcher.cpp
example/diameter/launcher/Launcher.hpp
example/diameter/launcher/MyHandler.cpp
example/diameter/launcher/deployments/adml/start.sh
example/diameter/launcher/resources/HELP.md
example/diameter/launcher/resources/rest_api/ct/flow-operations/answermsg2c_test.py [new file with mode: 0644]
example/diameter/launcher/resources/rest_api/ct/flow-operations/answermsg2e_test.py [new file with mode: 0644]
example/diameter/launcher/resources/rest_api/ct/flow-operations/sendhex_test.py [new file with mode: 0644]
example/diameter/launcher/resources/rest_api/ct/flow-operations/sendmsg_test.py [new file with mode: 0644]
example/diameter/launcher/resources/rest_api/ct/parsing-operations/code_test.py
example/diameter/launcher/resources/rest_api/ct/parsing-operations/decode_test.py
example/diameter/launcher/resources/rest_api/ct/parsing-operations/loadmsg_test.py
example/diameter/launcher/resources/rest_api/ct/resources/aaa-diameterHex.json.in [new file with mode: 0644]
example/diameter/launcher/resources/rest_api/ct/resources/aaa-diameterJson-request.json [new file with mode: 0644]
example/diameter/launcher/resources/rest_api/ct/resources/aar-diameterJson-request.json [new file with mode: 0644]
example/diameter/launcher/resources/rest_api/ct/resources/aar-diameterJsonFromOwnToAF-request.json [new file with mode: 0644]
example/diameter/launcher/resources/rest_api/ct/resources/aarFromOwnToAF.hex [new file with mode: 0644]
example/diameter/launcher/resources/rest_api/ct/resources/aarFromOwnToAF.xml [new file with mode: 0644]
example/diameter/launcher/resources/rest_api/ct/resources/diameterHex.json.in [deleted file]
example/diameter/launcher/resources/rest_api/ct/resources/diameterJson-request.json [deleted file]
example/diameter/launcher/resources/rest_api/helpers/diameterJsonHelper/examples/aaa.xml [new file with mode: 0644]
include/anna/diameter/codec/EngineImpl.hpp
include/anna/diameter/helpers/base/functions.hpp
source/diameter/codec/EngineImpl.cpp
source/diameter/helpers/base/functions.cpp

index be0c168..935940d 100644 (file)
 //#include <anna/diameter/codec/EngineManager.hpp>
 //#include <anna/http/Transport.hpp>
 //#include <anna/diameter/stack/Engine.hpp>
-//#include <anna/diameter/helpers/base/functions.hpp>
+#include <anna/diameter/helpers/base/functions.hpp>
 //#include <anna/time/functions.hpp>
 //#include <anna/diameter.comm/ApplicationMessageOamModule.hpp>
 //#include <anna/testing/defines.hpp>
 #include <anna/xml/xml.hpp>
 //#include <anna/diameter.comm/OriginHost.hpp>
 //#include <anna/diameter.comm/OriginHostManager.hpp>
+#include <anna/diameter.comm/Message.hpp>
 //
 //// Process
 //#include <Launcher.hpp>
@@ -321,79 +322,255 @@ bool EventOperation::show_stats(std::string &response) {
 /////////////////////
 // Flow operations //
 /////////////////////
-bool EventOperation::sendmsg2e(std::string &response, const std::string & diameterJson) {
+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);
 
-  return true; // OK
-}
-
-bool EventOperation::sendmsg2c(std::string &response, const std::string & diameterJson) {
-
-  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
-
+  // 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);
+  }
 
 
-  return true; // OK
+  response = "Operation processed"; // could be failed
+  return success;
 }
 
-bool EventOperation::answermsg2e(std::string &response, const std::string & diameterJson) {
+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);
 
-  return true; // OK
-}
-
-bool EventOperation::answermsg2c(std::string &response, const std::string & diameterJson) {
-
-  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
-
+  // 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);
+  }
 
 
-  return true; // OK
+  response = "Operation processed"; // could be failed
+  return success;
 }
 
-bool EventOperation::answermsg2e_action(std::string &response, const std::string & action) {
+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;
+    }
 
-  return true; // OK
-}
-
-bool EventOperation::answermsg2c_action(std::string &response, const std::string & action) {
-
-  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+    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.getOperatedEntity()->getReactingAnswers()->addMessage(code, message);
+  }
+  else { // action
+
+    if(diameterJson_or_action == "list") { // programmed answers FIFO's to stdout
+      response = anna::functions::encodeBase64(my_app.getOperatedEntity()->getReactingAnswers()->asString("ANSWERS TO ENTITY"));
+    } else if (diameterJson_or_action == "rotate") {
+      my_app.getOperatedEntity()->getReactingAnswers()->rotate(true);
+      response = "rotate";
+    } else if (diameterJson_or_action == "exhaust") {
+      my_app.getOperatedEntity()->getReactingAnswers()->rotate(false);
+      response = "exhaust";
+    } else if (diameterJson_or_action == "clear") {
+      my_app.getOperatedEntity()->getReactingAnswers()->clear();
+      response = "clear";
+    } else if (diameterJson_or_action == "dump") {
+      my_app.getOperatedEntity()->getReactingAnswers()->dump("programmed_answer");
+      response = "dump";
+    }
+  }
 
 
   return true; // OK
 }
 
-bool EventOperation::sendhex2e(std::string &response, const std::string & diameterHex) {
+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;
+    }
 
-  return true; // OK
-}
-
-bool EventOperation::sendhex2c(std::string &response, const std::string & diameterHex) {
-
-  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+    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.getOperatedServer()->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.getOperatedServer()->getReactingAnswers()->asString("ANSWERS TO CLIENT"));
+    } else if (diameterJson_or_action == "rotate") {
+      my_app.getOperatedServer()->getReactingAnswers()->rotate(true);
+      response = "rotate";
+    } else if (diameterJson_or_action == "exhaust") {
+      my_app.getOperatedServer()->getReactingAnswers()->rotate(false);
+      response = "exhaust";
+    } else if (diameterJson_or_action == "clear") {
+      my_app.getOperatedServer()->getReactingAnswers()->clear();
+      response = "clear";
+    } else if (diameterJson_or_action == "dump") {
+      my_app.getOperatedServer()->getReactingAnswers()->dump("programmed_answer");
+      response = "dump";
+    }
+  }
 
 
   return true; // OK
 }
 
-
 /////////////////
 // FSM testing //
 /////////////////
index 59ede33..be117cf 100644 (file)
@@ -100,14 +100,10 @@ public:
   bool show_stats(std::string &response);
 
   // Flow operations
-  bool sendmsg2e(std::string &response, const std::string & diameterJson);
-  bool sendmsg2c(std::string &response, const std::string & diameterJson);
-  bool answermsg2e(std::string &response, const std::string & diameterJson);
-  bool answermsg2c(std::string &response, const std::string & diameterJson);
-  bool answermsg2e_action(std::string &response, const std::string & action = "list");
-  bool answermsg2c_action(std::string &response, const std::string & action = "list");
-  bool sendhex2e(std::string &response, const std::string & diameterHex);
-  bool sendhex2c(std::string &response, const std::string & diameterHex);
+  bool sendmsg_hex_2e(std::string &response, const std::string & diameterJson_or_Hex, bool msg_or_hex);
+  bool sendmsg_hex_2c(std::string &response, const std::string & diameterJson_or_Hex, bool msg_or_hex);
+  bool answermsg_action_2e(std::string &response, const std::string & diameterJson_or_action, bool msg_or_action);
+  bool answermsg_action_2c(std::string &response, const std::string & diameterJson_or_action, bool msg_or_action);
 
   // FSM testing
   // test_id__<command>
index d0151cd..e52b5d2 100644 (file)
@@ -144,6 +144,7 @@ Launcher::Launcher() : anna::comm::Application("launcher", "DiameterLauncher", "
   a_counterRecorderClock = NULL;
 
   a_workingNode = NULL;
+  a_operatedHost = NULL;
 
   a_httpServerSocket = NULL;
 }
@@ -574,17 +575,26 @@ anna::diameter::comm::OriginHost *Launcher::getOriginHost(const anna::diameter::
   return (getOriginHost(originHost));
 }
 
+anna::diameter::comm::OriginHost *Launcher::getOriginHost(const anna::DataBlock &messageDataBlock) const throw(anna::RuntimeException) {
+  std::string originHost = anna::diameter::helpers::base::functions::getOriginHost(messageDataBlock);
+  return (getOriginHost(originHost));
+}
+
 bool Launcher::uniqueOriginHost() const throw() {
   anna::diameter::comm::OriginHostManager &ohm = anna::diameter::comm::OriginHostManager::instantiate();
   return (ohm.size() == 1);
 }
 
-
 void Launcher::updateOperatedOriginHostWithMessage(const anna::diameter::codec::Message &message) throw(anna::RuntimeException) {
   if (!a_operatedHost) // priority for working node by mean 'node' operation
     a_operatedHost = getOriginHost(message);
 }
 
+void Launcher::updateOperatedOriginHostWithMessage(const anna::DataBlock &messageDataBlock) throw(anna::RuntimeException) {
+  if (!a_operatedHost) // priority for working node by mean 'node' operation
+    a_operatedHost = getOriginHost(messageDataBlock);
+}
+
 anna::diameter::comm::OriginHost *Launcher::getWorkingNode() const throw(anna::RuntimeException) {
   if(!a_workingNode)
     throw anna::RuntimeException("Working node not identified (try to load services)", ANNA_FILE_LOCATION);
@@ -1177,6 +1187,7 @@ bool Launcher::eventOperation(const std::string &operation, std::string &respons
       // Get DataBlock from file with hex content:
       if(!getDataBlockFromHexFile(param1, db_aux))
         throw anna::RuntimeException("Error reading hex file provided", ANNA_FILE_LOCATION);
+      updateOperatedOriginHostWithMessage(db_aux);
       msg = getOperatedHost()->createCommMessage();
       msg->setBody(db_aux);
       try { if(getOperatedHost()->logEnabled()) codecMsg.decode(db_aux); } catch(anna::RuntimeException &ex) { ex.trace(); }
@@ -1192,6 +1203,87 @@ bool Launcher::eventOperation(const std::string &operation, std::string &respons
       std::string detail = usedClientSession ? usedClientSession->asString() : "<null client session>"; // shouldn't happen
       getOperatedHost()->writeLogFile(codecMsg, (success ? "sent2e" : "send2eError"), detail);
     }
+  } else if((opType == "sendxml2c") || (opType == "sendhex2c")) {
+    anna::diameter::comm::Message *msg;
+
+    if(opType == "sendxml2c") {
+      codecMsg.loadXMLFile(param1);
+      updateOperatedOriginHostWithMessage(codecMsg);
+      msg = getOperatedHost()->createCommMessage();
+      msg->clearBody();
+      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 file with hex content:
+      if(!getDataBlockFromHexFile(param1, db_aux))
+        throw anna::RuntimeException("Error reading hex file provided", ANNA_FILE_LOCATION);
+      updateOperatedOriginHostWithMessage(db_aux);
+      msg = getOperatedHost()->createCommMessage();
+      msg->setBody(db_aux);
+    }
+
+    bool success = getOperatedServer()->send(msg);
+    getOperatedHost()->releaseCommMessage(msg);
+
+    // Detailed log:
+    if(getOperatedHost()->logEnabled()) {
+      anna::diameter::comm::ServerSession *usedServerSession = getOperatedServer()->getLastUsedResource();
+      std::string detail = usedServerSession ? usedServerSession->asString() : "<null server session>"; // shouldn't happen
+      getOperatedHost()->writeLogFile(codecMsg, (success ? "sent2c" : "send2cError"), detail);
+    }
+  } else if(opType == "answerxml2e") {
+
+    if(param1 == "") { // programmed answers FIFO's to stdout
+      response = getOperatedEntity()->getReactingAnswers()->asString("ANSWERS TO ENTITY");
+      return true; // OK
+    } else if (param1 == "rotate") {
+      getOperatedEntity()->getReactingAnswers()->rotate(true);
+    } else if (param1 == "exhaust") {
+      getOperatedEntity()->getReactingAnswers()->rotate(false);
+    } else if (param1 == "clear") {
+      getOperatedEntity()->getReactingAnswers()->clear();
+    } else if (param1 == "dump") {
+      getOperatedEntity()->getReactingAnswers()->dump("programmed_answer");
+    } else {
+      codecMsg.loadXMLFile(param1);
+      updateOperatedOriginHostWithMessage(codecMsg);
+      anna::diameter::codec::Message *message = getOperatedHost()->getCodecEngine()->createMessage(param1); // loads xml again, lesser of two evils
+      LOGDEBUG(anna::Logger::debug(message->asXMLString(), ANNA_FILE_LOCATION));
+
+      if(message->isRequest())
+        throw anna::RuntimeException("Cannot program diameter requests. Answer type must be provided", ANNA_FILE_LOCATION);
+
+      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));
+      getOperatedEntity()->getReactingAnswers()->addMessage(code, message);
+    }
+  } else if(opType == "answerxml2c") {
+
+    if(param1 == "") { // programmed answers FIFO's to stdout
+      response = getOperatedServer()->getReactingAnswers()->asString("ANSWERS TO CLIENT");
+      return true; // OK
+    } else if (param1 == "rotate") {
+      getOperatedServer()->getReactingAnswers()->rotate(true);
+    } else if (param1 == "exhaust") {
+      getOperatedServer()->getReactingAnswers()->rotate(false);
+    } else if (param1 == "clear") {
+      getOperatedServer()->getReactingAnswers()->clear();
+    } else if (param1 == "dump") {
+      getOperatedServer()->getReactingAnswers()->dump("programmed_answer");
+    } else {
+      codecMsg.loadXMLFile(param1);
+      updateOperatedOriginHostWithMessage(codecMsg);
+      anna::diameter::codec::Message *message = getOperatedHost()->getCodecEngine()->createMessage(param1); // loads xml again, lesser of two evils
+      LOGDEBUG(anna::Logger::debug(message->asXMLString(), ANNA_FILE_LOCATION));
+
+      if(message->isRequest())
+        throw anna::RuntimeException("Cannot program diameter requests. Answer type must be provided", ANNA_FILE_LOCATION);
+
+      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));
+      getOperatedServer()->getReactingAnswers()->addMessage(code, message);
+    }
   } else if((opType == "burst")) {
 
     if (!uniqueOriginHost())
@@ -1784,34 +1876,6 @@ bool Launcher::eventOperation(const std::string &operation, std::string &respons
       }
     }
 
-  } else if((opType == "sendxml2c") || (opType == "sendhex2c")) {
-    anna::diameter::comm::Message *msg;
-
-    if(opType == "sendxml2c") {
-      codecMsg.loadXMLFile(param1);
-      updateOperatedOriginHostWithMessage(codecMsg);
-      msg = getOperatedHost()->createCommMessage();
-      msg->clearBody();
-      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 file with hex content:
-      if(!getDataBlockFromHexFile(param1, db_aux))
-        throw anna::RuntimeException("Error reading hex file provided", ANNA_FILE_LOCATION);
-      msg = getOperatedHost()->createCommMessage();
-      msg->setBody(db_aux);
-    }
-
-    bool success = getOperatedServer()->send(msg);
-    getOperatedHost()->releaseCommMessage(msg);
-
-    // Detailed log:
-    if(getOperatedHost()->logEnabled()) {
-      anna::diameter::comm::ServerSession *usedServerSession = getOperatedServer()->getLastUsedResource();
-      std::string detail = usedServerSession ? usedServerSession->asString() : "<null server session>"; // shouldn't happen
-      getOperatedHost()->writeLogFile(codecMsg, (success ? "sent2c" : "send2cError"), detail);
-    }
   } else if(opType == "loadxml") {
     codecMsg.loadXMLFile(param1);
     response = codecMsg.asXMLString();
@@ -1820,61 +1884,14 @@ bool Launcher::eventOperation(const std::string &operation, std::string &respons
     int diameterServerSessions = atoi(param1.c_str());
     getOperatedServer()->setMaxConnections(diameterServerSessions);
 
-  } else if(opType == "answerxml2c") {
-    if(param1 == "") { // programmed answers FIFO's to stdout
-      response = getOperatedServer()->getReactingAnswers()->asString("ANSWERS TO CLIENT");
-      return true; // OK
-    } else if (param1 == "rotate") {
-      getOperatedServer()->getReactingAnswers()->rotate(true);
-    } else if (param1 == "exhaust") {
-      getOperatedServer()->getReactingAnswers()->rotate(false);
-    } else if (param1 == "clear") {
-      getOperatedServer()->getReactingAnswers()->clear();
-    } else if (param1 == "dump") {
-      getOperatedServer()->getReactingAnswers()->dump("programmed_answer");
-    } else {
-      codecMsg.loadXMLFile(param1);
-      updateOperatedOriginHostWithMessage(codecMsg);
-      anna::diameter::codec::Message *message = getOperatedHost()->getCodecEngine()->createMessage(param1); // loads xml again, lesser of two evils
-      LOGDEBUG(anna::Logger::debug(message->asXMLString(), ANNA_FILE_LOCATION));
 
-      if(message->isRequest())
-        throw anna::RuntimeException("Cannot program diameter requests. Answer type must be provided", ANNA_FILE_LOCATION);
 
-      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));
-      getOperatedServer()->getReactingAnswers()->addMessage(code, message);
-    }
-  } else if(opType == "answerxml2e") {
-
-    if(param1 == "") { // programmed answers FIFO's to stdout
-      response = getOperatedEntity()->getReactingAnswers()->asString("ANSWERS TO ENTITY");
-      return true; // OK
-    } else if (param1 == "rotate") {
-      getOperatedEntity()->getReactingAnswers()->rotate(true);
-    } else if (param1 == "exhaust") {
-      getOperatedEntity()->getReactingAnswers()->rotate(false);
-    } else if (param1 == "clear") {
-      getOperatedEntity()->getReactingAnswers()->clear();
-    } else if (param1 == "dump") {
-      getOperatedEntity()->getReactingAnswers()->dump("programmed_answer");
-    } else {
-      codecMsg.loadXMLFile(param1);
-      updateOperatedOriginHostWithMessage(codecMsg);
-      anna::diameter::codec::Message *message = getOperatedHost()->getCodecEngine()->createMessage(param1); // loads xml again, lesser of two evils
-      LOGDEBUG(anna::Logger::debug(message->asXMLString(), ANNA_FILE_LOCATION));
-
-      if(message->isRequest())
-        throw anna::RuntimeException("Cannot program diameter requests. Answer type must be provided", ANNA_FILE_LOCATION);
-
-      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));
-      getOperatedEntity()->getReactingAnswers()->addMessage(code, message);
-    }
   } else {
     throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
   }
 
+
+
   // HTTP response
   response = "Operation correctly processed: "; response += operation;
   if (opt_response != "") {
index dab3415..c8f34e6 100644 (file)
@@ -93,10 +93,13 @@ public:
   void setNodeAuto() { a_workingNode = NULL; a_operatedHost = NULL; }
   anna::diameter::comm::OriginHost *getOriginHost(const std::string &name) const throw(anna::RuntimeException);
   anna::diameter::comm::OriginHost *getOriginHost(const anna::diameter::codec::Message &message) const throw(anna::RuntimeException);
+  anna::diameter::comm::OriginHost *getOriginHost(const anna::DataBlock &messageDataBlock) const throw(anna::RuntimeException);
+
   bool uniqueOriginHost() const throw();
 
   // Operated host for communication resources smart assignment ////////////////////////////////////////////////////////////
   void updateOperatedOriginHostWithMessage(const anna::diameter::codec::Message &message) throw(anna::RuntimeException);
+  void updateOperatedOriginHostWithMessage(const anna::DataBlock &messageDataBlock) throw(anna::RuntimeException);
   anna::diameter::comm::OriginHost *getWorkingNode() const throw(anna::RuntimeException);
   anna::diameter::comm::OriginHost *getOperatedHost() const throw(anna::RuntimeException);
   void setOperatedHost(anna::diameter::comm::OriginHost *);
index ed49f4f..49205ed 100644 (file)
@@ -114,7 +114,7 @@ throw(anna::RuntimeException) {
     }
     catch(anna::RuntimeException &ex) {
       ex.trace();
-      LOGINFORMATION(anna::Logger::information("XXXXXXXXXXXX EXCEPCION PENDIENTE DE CATCHEAR ABAJO !!! XXXXXXXXXXXXXXXXX", ANNA_FILE_LOCATION));
+      LOGINFORMATION(anna::Logger::information("doPOST exception: TODO control in inner method. Check traces", ANNA_FILE_LOCATION));
     }
   }
 
@@ -252,15 +252,26 @@ bool MyHandler::doPOST(const std::string &uri, const nlohmann::json &j, std::str
 
   // Flow operations
   else if ((opType == "/sendmsg2e")||(opType == "/sendmsg2c")) {
-    auto it = j.find("diameterJson");
-    if (it != j.end())
+    auto itJ = j.find("diameterJson");
+    if (itJ != j.end()) {
       if (opType == "/sendmsg2e")
-        result = eop.sendmsg2e(response, it->dump(4)); // get the object as string (always indentation = 4)
+        result = eop.sendmsg_hex_2e(response, itJ->dump(4), true); // get the object as string (always indentation = 4)
       else
-        result = eop.sendmsg2c(response, it->dump(4)); // get the object as string (always indentation = 4)
+        result = eop.sendmsg_hex_2c(response, itJ->dump(4), true); // get the object as string (always indentation = 4)
+    }
     else
       response += "missing 'diameterJson' object field";
   }
+  else if ((opType == "/sendhex2e")||(opType == "/sendhex2c")) {
+    auto itH = j.find("diameterHex");
+    if (itH != j.end())
+      if (opType == "/sendhex2e")
+        result = eop.sendmsg_hex_2e(response, *itH, false);
+      else
+        result = eop.sendmsg_hex_2c(response, *itH, false);
+    else
+      response += "missing 'diameterHex' string field";
+  }
   else if ((opType == "/answermsg2e")||(opType == "/answermsg2c")) {
     auto itJ = j.find("diameterJson");
     auto itA = j.find("action");
@@ -268,33 +279,20 @@ bool MyHandler::doPOST(const std::string &uri, const nlohmann::json &j, std::str
     bool hasA = (itA != j.end());
 
     if (hasJ != hasA) { // XOR
-
-      if (opType == "/answermsg2e") {
-        if (hasJ)
-          result = eop.answermsg2e(response, itJ->dump(4)); // get the object as string (always indentation = 4)
-        else
-          result = eop.answermsg2e_action(response, *itA);
-      }
-      else {
-        if (hasJ)
-          result = eop.answermsg2c(response, itJ->dump(4)); // get the object as string (always indentation = 4)
-        else
-          result = eop.answermsg2c_action(response, *itA);
+      std::string action;
+      if (hasA) {
+        action = *itA;
+        if (action == "") action = "list"; // default if missing
       }
+
+      if (opType == "/answermsg2e")
+        result = eop.answermsg_action_2e(response, (hasJ ? itJ->dump(4) : action), hasJ); // itJ: get the object as string (always indentation = 4)
+      else
+        result = eop.answermsg_action_2c(response, (hasJ ? itJ->dump(4) : action), hasJ); // itJ: get the object as string (always indentation = 4)
     }
     else
       response += "missing 'diameterJson' object or 'action' string field (only one accepted)";
   }
-  else if ((opType == "/sendhex2e")||(opType == "/sendhex2c")) {
-    auto it = j.find("diameterHex");
-    if (it != j.end())
-      if (opType == "/sendhex2e")
-        result = eop.sendhex2e(response, *it);
-      else
-        result = eop.sendhex2c(response, *it);
-    else
-      response += "missing 'diameterHex' string field";
-  }
 
   // FSM testing
   // test_id__<command>
index b8f2e9f..b53a90e 100755 (executable)
@@ -16,6 +16,7 @@ mkdir -p counters test-reports
 rm -f counters/* test-reports/* *.csv
 export LD_LIBRARY_PATH=$PWD/dynlibs
 args=$(eval echo $(grep -v ^# $ARGS))
+#ulimit -c unlimited
 ./ADML ${args} &
 echo $! > .pid
 echo "Done !"
index 8b453fa..fec3b72 100644 (file)
@@ -1162,7 +1162,7 @@ or
 ```
 {
     "result":"<true or false>",
-    "response":"<response>"
+    "response":"<response or base64-encoded output for 'list' action>"
 }
 ```
 
@@ -1193,7 +1193,7 @@ or
 ```
 {
     "result":"<true or false>",
-    "response":"<response>"
+    "response":"<response or base64-encoded output for 'list' action>"
 }
 ```
 
diff --git a/example/diameter/launcher/resources/rest_api/ct/flow-operations/answermsg2c_test.py b/example/diameter/launcher/resources/rest_api/ct/flow-operations/answermsg2c_test.py
new file mode 100644 (file)
index 0000000..63e7a21
--- /dev/null
@@ -0,0 +1,80 @@
+import pytest
+
+
+def test_001_given_diameter_answer_json_representation_i_want_to_provision_it_to_reacts_to_diameter_client_requests(resources, admlc):
+
+  requestBody = { "name":"ownHostId.operatorRealm.com" }
+  responseBodyRef = { "success":"true", "response":"Forced node is now 'ownHostId.operatorRealm.com'" }
+
+  # Send POST
+  response = admlc.postDict("/node", requestBody)
+
+  # Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+  requestBody = resources("aaa-diameterJson-request.json")
+  responseBodyRef = { "success":"true", "response":"Added 'answer to client' to the FIFO queue corresponding to its message code" }
+
+  # Send POST
+  response = admlc.post("/answermsg2c", requestBody)
+
+  ### Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+def test_002_i_want_to_list_the_answer_to_client_FIFO_queue(b64_decode, mylogger, admlc):
+
+  requestBody = { "action":"list" } # also empty action is interpreted as "list"
+
+  # Send POST
+  response = admlc.postDict("/answermsg2c", requestBody)
+
+  # Verify response is ok (omit response content because it is dynamic: will be logged)
+  assert response["status"] == 200
+  assert response["body"]["success"] == "true"
+  body_response = b64_decode(response["body"]["response"])
+  mylogger.info("\nBASE64 DECODED RESPONSE: \n\n" + body_response)
+
+def test_003_i_want_to_dump_the_answer_to_client_FIFO_queue(admlc):
+
+  requestBody = { "action":"dump" }
+  responseBodyRef = { "success":"true", "response":"dump" }
+
+  # Send POST
+  response = admlc.postDict("/answermsg2c", requestBody)
+
+  ### Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+def test_004_i_want_to_clear_the_answer_to_client_FIFO_queue(admlc):
+
+  requestBody = { "action":"clear" }
+  responseBodyRef = { "success":"true", "response":"clear" }
+
+  # Send POST
+  response = admlc.postDict("/answermsg2c", requestBody)
+
+  ### Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+def test_005_i_want_to_exhaust_the_answer_to_client_FIFO_queue(admlc):
+
+  requestBody = { "action":"exhaust" }
+  responseBodyRef = { "success":"true", "response":"exhaust" }
+
+  # Send POST
+  response = admlc.postDict("/answermsg2c", requestBody)
+
+  ### Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+def test_006_i_want_to_rotate_the_answer_to_client_FIFO_queue(admlc):
+
+  requestBody = { "action":"rotate" }
+  responseBodyRef = { "success":"true", "response":"rotate" }
+
+  # Send POST
+  response = admlc.postDict("/answermsg2c", requestBody)
+
+  ### Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
diff --git a/example/diameter/launcher/resources/rest_api/ct/flow-operations/answermsg2e_test.py b/example/diameter/launcher/resources/rest_api/ct/flow-operations/answermsg2e_test.py
new file mode 100644 (file)
index 0000000..6f97b84
--- /dev/null
@@ -0,0 +1,80 @@
+import pytest
+
+
+def test_001_given_diameter_answer_json_representation_i_want_to_provision_it_to_reacts_to_diameter_entity_requests(resources, admlc):
+
+  requestBody = { "name":"afHost.afRealm.com" }
+  responseBodyRef = { "success":"true", "response":"Forced node is now 'afHost.afRealm.com'" }
+
+  # Send POST
+  response = admlc.postDict("/node", requestBody)
+
+  # Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+  requestBody = resources("aaa-diameterJson-request.json")
+  responseBodyRef = { "success":"true", "response":"Added 'answer to entity' to the FIFO queue corresponding to its message code" }
+
+  # Send POST
+  response = admlc.post("/answermsg2e", requestBody)
+
+  ### Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+def test_002_i_want_to_list_the_answer_to_entity_FIFO_queue(b64_decode, mylogger, admlc):
+
+  requestBody = { "action":"list" } # also empty action is interpreted as "list"
+
+  # Send POST
+  response = admlc.postDict("/answermsg2e", requestBody)
+
+  # Verify response is ok (omit response content because it is dynamic: will be logged)
+  assert response["status"] == 200
+  assert response["body"]["success"] == "true"
+  body_response = b64_decode(response["body"]["response"])
+  mylogger.info("\nBASE64 DECODED RESPONSE: \n\n" + body_response)
+
+def test_003_i_want_to_dump_the_answer_to_entity_FIFO_queue(admlc):
+
+  requestBody = { "action":"dump" }
+  responseBodyRef = { "success":"true", "response":"dump" }
+
+  # Send POST
+  response = admlc.postDict("/answermsg2e", requestBody)
+
+  ### Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+def test_004_i_want_to_clear_the_answer_to_entity_FIFO_queue(admlc):
+
+  requestBody = { "action":"clear" }
+  responseBodyRef = { "success":"true", "response":"clear" }
+
+  # Send POST
+  response = admlc.postDict("/answermsg2e", requestBody)
+
+  ### Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+def test_005_i_want_to_exhaust_the_answer_to_entity_FIFO_queue(admlc):
+
+  requestBody = { "action":"exhaust" }
+  responseBodyRef = { "success":"true", "response":"exhaust" }
+
+  # Send POST
+  response = admlc.postDict("/answermsg2e", requestBody)
+
+  ### Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+def test_006_i_want_to_rotate_the_answer_to_entity_FIFO_queue(admlc):
+
+  requestBody = { "action":"rotate" }
+  responseBodyRef = { "success":"true", "response":"rotate" }
+
+  # Send POST
+  response = admlc.postDict("/answermsg2e", requestBody)
+
+  ### Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
diff --git a/example/diameter/launcher/resources/rest_api/ct/flow-operations/sendhex_test.py b/example/diameter/launcher/resources/rest_api/ct/flow-operations/sendhex_test.py
new file mode 100644 (file)
index 0000000..a07b7c4
--- /dev/null
@@ -0,0 +1,81 @@
+import pytest
+
+def test_001_given_diameter_hex_representation_i_want_to_send_message_to_diameter_entity_and_check_it_fails_for_auto_node(resources, admlc):
+
+  responseBodyRef = { "success":"true", "response":"Working node has been set to automatic" }
+
+  # Send POST
+  response = admlc.post("/node-auto")
+
+  # Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+  diameterHex = resources("aar.hex")
+  #requestBody = resources("aaa-diameterHex.json.in", diameterHex=diameterHex.rstrip())
+  requestBodyDict = { "diameterHex":"{}".format(diameterHex.rstrip()) }
+
+  responseBodyRef = { "success":"false", "response":"Internal error (check ADML traces): invalid operated host" }
+
+  # Send POST
+  response = admlc.postDict("/sendhex2e", requestBodyDict)
+
+  # Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+  # IT FAILS BECAUSE MESSAGE SENT HAS OCS3 AS ORIGIN HOST, AND THIS IS NOT REGISTERED, SO node-auto COULD NOT DEDUCE THE CORRECT RESOURCE
+
+@pytest.mark.xfail(reason="Server must receive client connection to be available. Component test is very fast and this could fail the first time. Wait and test again. TODO: wait for available status after 'services' loading.")
+def test_002_given_diameter_hex_representation_i_want_to_send_message_to_diameter_entity_and_check_it_succeed_for_correct_node(resources, admlc):
+
+  requestBody = { "name":"afHost.afRealm.com" }
+  responseBodyRef = { "success":"true", "response":"Forced node is now 'afHost.afRealm.com'" }
+
+  # Send POST
+  response = admlc.postDict("/node", requestBody)
+
+  # Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+  diameterHex = resources("aar.hex")
+  #requestBody = resources("aaa-diameterHex.json.in", diameterHex=diameterHex.rstrip())
+  requestBodyDict = { "diameterHex":"{}".format(diameterHex.rstrip()) }
+
+  responseBodyRef = { "success":"true", "response":"Operation processed" }
+
+  # Send POST
+  response = admlc.postDict("/sendhex2e", requestBodyDict)
+
+  # Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+@pytest.mark.xfail(reason="Server must receive client connection to be available. Component test is very fast and this could fail the first time. Wait and test again. TODO: wait for available status after 'services' loading.")
+def test_003_given_diameter_hex_representation_i_want_to_send_message_to_diameter_client_and_check_it_succeed_for_correct_node(resources, admlc):
+
+#  # WE HAVE TO CONFIGURE THE CLIENT AS OPERATED HOST, BECAUSE HEX SENDING PROCEDURE IS NOT GOING TO UPDATE OPERATED HOST WITH CODEC:
+#  requestBody = { "name":"afHost.afRealm.com" }
+#  responseBodyRef = { "success":"true", "response":"Forced node is now 'afHost.afRealm.com'" }
+  requestBody = { "name":"ownHostId.operatorRealm.com" }
+  responseBodyRef = { "success":"true", "response":"Forced node is now 'ownHostId.operatorRealm.com'" }
+
+  # Send POST
+  response = admlc.postDict("/node", requestBody)
+
+  # Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+  # NORMALLY, AAR IS NOT THE KIND OF MESSAGE TO SEND AS DIAMETER SERVER BUT RAR, etc, BUT THIS IS VALID FOR OUR TESTING
+
+  # We will use a message which origin-host and destination-host fits the real configured ones in services, and also is ready
+  #  to be sent from local diameter server to the client:
+  diameterHex = resources("aarFromOwnToAF.hex")
+  #requestBody = resources("aaa-diameterHex.json.in", diameterHex=diameterHex.rstrip())
+  requestBodyDict = { "diameterHex":"{}".format(diameterHex.rstrip()) }
+
+  responseBodyRef = { "success":"true", "response":"Operation processed" }
+
+  # Send POST
+  response = admlc.postDict("/sendhex2c", requestBodyDict)
+
+  # Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
diff --git a/example/diameter/launcher/resources/rest_api/ct/flow-operations/sendmsg_test.py b/example/diameter/launcher/resources/rest_api/ct/flow-operations/sendmsg_test.py
new file mode 100644 (file)
index 0000000..f3afa4c
--- /dev/null
@@ -0,0 +1,68 @@
+import pytest
+
+def test_001_given_diameter_json_representation_i_want_to_send_message_to_diameter_entity_and_check_it_fails_for_auto_node(resources, admlc):
+
+  responseBodyRef = { "success":"true", "response":"Working node has been set to automatic" }
+
+  # Send POST
+  response = admlc.post("/node-auto")
+
+  # Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+  requestBody = resources("aar-diameterJson-request.json")
+  responseBodyRef = { "success":"false", "response":"Internal error (check ADML traces): invalid operated host" }
+
+  # Send POST
+  response = admlc.post("/sendmsg2e", requestBody)
+
+  ### Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+  # IT FAILS BECAUSE MESSAGE SENT HAS OCS3 AS ORIGIN HOST, AND THIS IS NOT REGISTERED, SO node-auto COULD NOT DEDUCE THE CORRECT RESOURCE
+
+@pytest.mark.xfail(reason="Server must receive client connection to be available. Component test is very fast and this could fail the first time. Wait and test again. TODO: wait for available status after 'services' loading.")
+def test_002_given_diameter_json_representation_i_want_to_send_message_to_diameter_entity_and_check_it_succeed_for_forced_node(resources, admlc):
+
+  requestBody = { "name":"afHost.afRealm.com" }
+  responseBodyRef = { "success":"true", "response":"Forced node is now 'afHost.afRealm.com'" }
+
+  # Send POST
+  response = admlc.postDict("/node", requestBody)
+
+  # Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+  requestBody = resources("aar-diameterJson-request.json")
+  responseBodyRef = { "success":"true", "response":"Operation processed" }
+
+  # Send POST
+  response = admlc.post("/sendmsg2e", requestBody)
+
+  ### Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+@pytest.mark.xfail(reason="Server must receive client connection to be available. Component test is very fast and this could fail the first time. Wait and test again. TODO: wait for available status after 'services' loading.")
+def test_003_given_diameter_json_representation_i_want_to_send_message_to_diameter_client_and_check_it_succeed_for_auto_node(resources, admlc):
+
+  responseBodyRef = { "success":"true", "response":"Working node has been set to automatic" }
+
+  # Send POST
+  response = admlc.post("/node-auto")
+
+  # Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+  # NORMALLY, AAR IS NOT THE KIND OF MESSAGE TO SEND AS DIAMETER SERVER BUT RAR, etc, BUT THIS IS VALID FOR OUR TESTING
+
+  # We will use a message which origin-host and destination-host fits the real configured ones in services, and also is ready
+  #  to be sent from local diameter server to the client:
+  requestBody = resources("aar-diameterJsonFromOwnToAF-request.json")
+  responseBodyRef = { "success":"true", "response":"Operation processed" }
+
+  # Send POST
+  response = admlc.post("/sendmsg2c", requestBody)
+
+  ### Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
index 226cdae..5831a45 100644 (file)
@@ -2,7 +2,7 @@ import pytest
 
 def test_001_given_diameter_json_representation_i_want_to_encode_it_through_adml_service(resources, admlc):
 
-  requestBody = resources("diameterJson-request.json")
+  requestBody = resources("aar-diameterJson-request.json")
 
   # reponse field in response body, should carry the diameter encoding for json message sent to ADML:
   diameterHex = resources("aar.hex")
index d24e51d..0e5e8fa 100644 (file)
@@ -3,7 +3,7 @@ import pytest
 def test_001_given_diameter_hex_representation_i_want_to_decode_it_through_adml_service(b64_encode, resources, admlc):
 
   diameterHex = resources("aaa.hex")
-  #requestBody = resources("diameterHex.json.in", diameterHex=diameterHex.rstrip())
+  #requestBody = resources("aaa-diameterHex.json.in", diameterHex=diameterHex.rstrip())
   requestBodyDict = { "diameterHex":"{}".format(diameterHex.rstrip()) }
 
   # reponse field in response body, should carry the base64 encoding for xml message decoded by ADML:
index 299f299..ef8a286 100644 (file)
@@ -3,7 +3,7 @@ import pytest
 
 def test_001_given_diameter_json_representation_i_want_to_load_it_through_adml_service(b64_encode, resources, admlc):
 
-  requestBody = resources("diameterJson-request.json")
+  requestBody = resources("aar-diameterJson-request.json")
 
   # reponse field in response body, should carry the base64 encoding for xml message loaded by ADML:
   xmlExpected = resources("aar.xml")
diff --git a/example/diameter/launcher/resources/rest_api/ct/resources/aaa-diameterHex.json.in b/example/diameter/launcher/resources/rest_api/ct/resources/aaa-diameterHex.json.in
new file mode 100644 (file)
index 0000000..138a771
--- /dev/null
@@ -0,0 +1,3 @@
+{{
+  "diameterHex":"{diameterHex}"
+}}
diff --git a/example/diameter/launcher/resources/rest_api/ct/resources/aaa-diameterJson-request.json b/example/diameter/launcher/resources/rest_api/ct/resources/aaa-diameterJson-request.json
new file mode 100644 (file)
index 0000000..e087719
--- /dev/null
@@ -0,0 +1,55 @@
+{
+  "diameterJson": {
+     "message": {
+        "@application-id": "16777236",
+        "@end-to-end-id": "315108",
+        "@hop-by-hop-id": "816067",
+        "@name": "AA-Answer",
+        "@version": "1",
+        "avp": [
+           {
+              "@data": "tc_01_FullAVPs;afNodeHostname.afNodeHostRealm.com;1;859608",
+              "@name": "Session-Id"
+           },
+           {
+              "@data": "16777236",
+              "@name": "Auth-Application-Id"
+           },
+           {
+              "@data": "sapcOwnHostId.operatorRealm.com",
+              "@name": "Origin-Host"
+           },
+           {
+              "@data": "operatorRealm.com",
+              "@name": "Origin-Realm"
+           },
+           {
+              "@alias": "DIAMETER_SUCCESS",
+              "@data": "2001",
+              "@name": "Result-Code"
+           },
+           {
+              "@name": "Supported-Features",
+              "avp": [
+                 {
+                    "@data": "10415",
+                    "@name": "Vendor-Id"
+                 },
+                 {
+                    "@data": "1",
+                    "@name": "Feature-List-ID"
+                 },
+                 {
+                    "@data": "19",
+                    "@name": "Feature-List"
+                 }
+              ]
+           },
+           {
+              "@data": "1427215015",
+              "@name": "Origin-State-Id"
+           }
+        ]
+     }
+  }
+}
diff --git a/example/diameter/launcher/resources/rest_api/ct/resources/aar-diameterJson-request.json b/example/diameter/launcher/resources/rest_api/ct/resources/aar-diameterJson-request.json
new file mode 100644 (file)
index 0000000..2dbc65d
--- /dev/null
@@ -0,0 +1,100 @@
+{
+  "diameterJson": {
+    "message": {
+      "@application-id": "16777236",
+      "@end-to-end-id": "0",
+      "@hop-by-hop-id": "0",
+      "@name": "AA-Request",
+      "@p-bit": "yes",
+      "@version": "1",
+      "avp": [
+        {
+          "@data": "ocs3;1332774430;1;1332774430",
+          "@name": "Session-Id"
+        },
+        {
+          "@data": "16777236",
+          "@name": "Auth-Application-Id"
+        },
+        {
+          "@data": "OCS3",
+          "@name": "Origin-Host"
+        },
+        {
+          "@data": "OCS3",
+          "@name": "Origin-Realm"
+        },
+        {
+          "@data": "OCS3",
+          "@name": "Destination-Realm"
+        },
+        {
+          "@data": "OCS3",
+          "@name": "Destination-Host"
+        },
+        {
+          "@hex-data": "313232",
+          "@name": "AF-Application-Identifier"
+        },
+        {
+          "@name": "Media-Component-Description",
+          "avp": [
+            {
+              "@data": "0",
+              "@name": "Media-Component-Number"
+            },
+            {
+              "@hex-data": "313232",
+              "@name": "AF-Application-Identifier"
+            },
+            {
+              "@data": "127",
+              "@name": "Max-Requested-Bandwidth-UL"
+            },
+            {
+              "@data": "133",
+              "@name": "Max-Requested-Bandwidth-DL"
+            },
+            {
+              "@alias": "ENABLED",
+              "@data": "2",
+              "@name": "Flow-Status"
+            },
+            {
+              "@alias": "DEFAULT",
+              "@data": "0",
+              "@name": "Reservation-Priority"
+            }
+          ]
+        },
+        {
+          "@alias": "FINAL_SERVICE_INFORMATION",
+          "@data": "0",
+          "@name": "Service-Info-Status"
+        },
+        {
+          "@name": "Subscription-Id",
+          "avp": [
+            {
+              "@alias": "END_USER_E164",
+              "@data": "0",
+              "@name": "Subscription-Id-Type"
+            },
+            {
+              "@data": "626037099",
+              "@name": "Subscription-Id-Data"
+            }
+          ]
+        },
+        {
+          "@hex-data": "3139322e3136382e302e31",
+          "@name": "Framed-IP-Address"
+        },
+        {
+          "@hex-data": "5741502e4d4f564953544152",
+          "@name": "Called-Station-Id"
+        }
+      ]
+    }
+  }
+}
diff --git a/example/diameter/launcher/resources/rest_api/ct/resources/aar-diameterJsonFromOwnToAF-request.json b/example/diameter/launcher/resources/rest_api/ct/resources/aar-diameterJsonFromOwnToAF-request.json
new file mode 100644 (file)
index 0000000..f9d08ca
--- /dev/null
@@ -0,0 +1,100 @@
+{
+  "diameterJson": {
+    "message": {
+      "@application-id": "16777236",
+      "@end-to-end-id": "0",
+      "@hop-by-hop-id": "0",
+      "@name": "AA-Request",
+      "@p-bit": "yes",
+      "@version": "1",
+      "avp": [
+        {
+          "@data": "ocs3;1332774430;1;1332774430",
+          "@name": "Session-Id"
+        },
+        {
+          "@data": "16777236",
+          "@name": "Auth-Application-Id"
+        },
+        {
+          "@data": "ownHostId.operatorRealm.com",
+          "@name": "Origin-Host"
+        },
+        {
+          "@data": "operatorRealm.com",
+          "@name": "Origin-Realm"
+        },
+        {
+          "@data": "afRealm.com",
+          "@name": "Destination-Realm"
+        },
+        {
+          "@data": "afHost.afRealm.com",
+          "@name": "Destination-Host"
+        },
+        {
+          "@hex-data": "313232",
+          "@name": "AF-Application-Identifier"
+        },
+        {
+          "@name": "Media-Component-Description",
+          "avp": [
+            {
+              "@data": "0",
+              "@name": "Media-Component-Number"
+            },
+            {
+              "@hex-data": "313232",
+              "@name": "AF-Application-Identifier"
+            },
+            {
+              "@data": "127",
+              "@name": "Max-Requested-Bandwidth-UL"
+            },
+            {
+              "@data": "133",
+              "@name": "Max-Requested-Bandwidth-DL"
+            },
+            {
+              "@alias": "ENABLED",
+              "@data": "2",
+              "@name": "Flow-Status"
+            },
+            {
+              "@alias": "DEFAULT",
+              "@data": "0",
+              "@name": "Reservation-Priority"
+            }
+          ]
+        },
+        {
+          "@alias": "FINAL_SERVICE_INFORMATION",
+          "@data": "0",
+          "@name": "Service-Info-Status"
+        },
+        {
+          "@name": "Subscription-Id",
+          "avp": [
+            {
+              "@alias": "END_USER_E164",
+              "@data": "0",
+              "@name": "Subscription-Id-Type"
+            },
+            {
+              "@data": "626037099",
+              "@name": "Subscription-Id-Data"
+            }
+          ]
+        },
+        {
+          "@hex-data": "3139322e3136382e302e31",
+          "@name": "Framed-IP-Address"
+        },
+        {
+          "@hex-data": "5741502e4d4f564953544152",
+          "@name": "Called-Station-Id"
+        }
+      ]
+    }
+  }
+}
diff --git a/example/diameter/launcher/resources/rest_api/ct/resources/aarFromOwnToAF.hex b/example/diameter/launcher/resources/rest_api/ct/resources/aarFromOwnToAF.hex
new file mode 100644 (file)
index 0000000..1b4e3db
--- /dev/null
@@ -0,0 +1 @@
+01000190c000010901000014000000000000000000000107400000246f6373333b313333323737343433303b313b31333332373734343330000001024000000c0100001400000108400000236f776e486f737449642e6f70657261746f725265616c6d2e636f6d0000000128400000196f70657261746f725265616c6d2e636f6d0000000000011b4000001361665265616c6d2e636f6d00000001254000001a6166486f73742e61665265616c6d2e636f6d0000000001f8c000000f000028af3132320000000205c000006c000028af00000206c0000010000028af00000000000001f8c000000f000028af3132320000000204c0000010000028af0000007f00000203c0000010000028af00000085000001ffc0000010000028af00000002000001ca80000010000032db000000000000020fc0000010000028af00000000000001bb40000028000001c24000000c00000000000001bc4000001136323630333730393900000000000008400000133139322e3136382e302e31000000001e400000145741502e4d4f564953544152
diff --git a/example/diameter/launcher/resources/rest_api/ct/resources/aarFromOwnToAF.xml b/example/diameter/launcher/resources/rest_api/ct/resources/aarFromOwnToAF.xml
new file mode 100644 (file)
index 0000000..15dc690
--- /dev/null
@@ -0,0 +1,24 @@
+<message version="1" name="AA-Request" p-bit="yes" application-id="16777236" hop-by-hop-id="0" end-to-end-id="0">
+   <avp name="Session-Id" data="ocs3;1332774430;1;1332774430"/>
+   <avp name="Auth-Application-Id" data="16777236"/>
+   <avp name="Origin-Host" data="ownHostId.operatorRealm.com"/>
+   <avp name="Origin-Realm" data="operatorRealm.com"/>
+   <avp name="Destination-Realm" data="afRealm.com"/>
+   <avp name="Destination-Host" data="afHost.afRealm.com"/>
+   <avp name="AF-Application-Identifier" hex-data="313232"/>
+   <avp name="Media-Component-Description">
+      <avp name="Media-Component-Number" data="0"/>
+      <avp name="AF-Application-Identifier" hex-data="313232"/>
+      <avp name="Max-Requested-Bandwidth-UL" data="127"/>
+      <avp name="Max-Requested-Bandwidth-DL" data="133"/>
+      <avp name="Flow-Status" data="2" alias="ENABLED"/>
+      <avp name="Reservation-Priority" data="0" alias="DEFAULT"/>
+   </avp>
+   <avp name="Service-Info-Status" data="0" alias="FINAL_SERVICE_INFORMATION"/>
+   <avp name="Subscription-Id">
+      <avp name="Subscription-Id-Type" data="0" alias="END_USER_E164"/>
+      <avp name="Subscription-Id-Data" data="626037099"/>
+   </avp>
+   <avp name="Framed-IP-Address" hex-data="3139322e3136382e302e31"/>
+   <avp name="Called-Station-Id" hex-data="5741502e4d4f564953544152"/>
+</message>
diff --git a/example/diameter/launcher/resources/rest_api/ct/resources/diameterHex.json.in b/example/diameter/launcher/resources/rest_api/ct/resources/diameterHex.json.in
deleted file mode 100644 (file)
index 138a771..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-{{
-  "diameterHex":"{diameterHex}"
-}}
diff --git a/example/diameter/launcher/resources/rest_api/ct/resources/diameterJson-request.json b/example/diameter/launcher/resources/rest_api/ct/resources/diameterJson-request.json
deleted file mode 100644 (file)
index 2dbc65d..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-{
-  "diameterJson": {
-    "message": {
-      "@application-id": "16777236",
-      "@end-to-end-id": "0",
-      "@hop-by-hop-id": "0",
-      "@name": "AA-Request",
-      "@p-bit": "yes",
-      "@version": "1",
-      "avp": [
-        {
-          "@data": "ocs3;1332774430;1;1332774430",
-          "@name": "Session-Id"
-        },
-        {
-          "@data": "16777236",
-          "@name": "Auth-Application-Id"
-        },
-        {
-          "@data": "OCS3",
-          "@name": "Origin-Host"
-        },
-        {
-          "@data": "OCS3",
-          "@name": "Origin-Realm"
-        },
-        {
-          "@data": "OCS3",
-          "@name": "Destination-Realm"
-        },
-        {
-          "@data": "OCS3",
-          "@name": "Destination-Host"
-        },
-        {
-          "@hex-data": "313232",
-          "@name": "AF-Application-Identifier"
-        },
-        {
-          "@name": "Media-Component-Description",
-          "avp": [
-            {
-              "@data": "0",
-              "@name": "Media-Component-Number"
-            },
-            {
-              "@hex-data": "313232",
-              "@name": "AF-Application-Identifier"
-            },
-            {
-              "@data": "127",
-              "@name": "Max-Requested-Bandwidth-UL"
-            },
-            {
-              "@data": "133",
-              "@name": "Max-Requested-Bandwidth-DL"
-            },
-            {
-              "@alias": "ENABLED",
-              "@data": "2",
-              "@name": "Flow-Status"
-            },
-            {
-              "@alias": "DEFAULT",
-              "@data": "0",
-              "@name": "Reservation-Priority"
-            }
-          ]
-        },
-        {
-          "@alias": "FINAL_SERVICE_INFORMATION",
-          "@data": "0",
-          "@name": "Service-Info-Status"
-        },
-        {
-          "@name": "Subscription-Id",
-          "avp": [
-            {
-              "@alias": "END_USER_E164",
-              "@data": "0",
-              "@name": "Subscription-Id-Type"
-            },
-            {
-              "@data": "626037099",
-              "@name": "Subscription-Id-Data"
-            }
-          ]
-        },
-        {
-          "@hex-data": "3139322e3136382e302e31",
-          "@name": "Framed-IP-Address"
-        },
-        {
-          "@hex-data": "5741502e4d4f564953544152",
-          "@name": "Called-Station-Id"
-        }
-      ]
-    }
-  }
-}
diff --git a/example/diameter/launcher/resources/rest_api/helpers/diameterJsonHelper/examples/aaa.xml b/example/diameter/launcher/resources/rest_api/helpers/diameterJsonHelper/examples/aaa.xml
new file mode 100644 (file)
index 0000000..8338a6a
--- /dev/null
@@ -0,0 +1,13 @@
+<message version="1" name="AA-Answer" application-id="16777236" hop-by-hop-id="816067" end-to-end-id="315108">
+   <avp name="Session-Id" data="tc_01_FullAVPs;afNodeHostname.afNodeHostRealm.com;1;859608"/>
+   <avp name="Auth-Application-Id" data="16777236"/>
+   <avp name="Origin-Host" data="sapcOwnHostId.operatorRealm.com"/>
+   <avp name="Origin-Realm" data="operatorRealm.com"/>
+   <avp name="Result-Code" data="2001" alias="DIAMETER_SUCCESS"/>
+   <avp name="Supported-Features">
+      <avp name="Vendor-Id" data="10415"/>
+      <avp name="Feature-List-ID" data="1"/>
+      <avp name="Feature-List" data="19"/>
+   </avp>
+   <avp name="Origin-State-Id" data="1427215015"/>
+</message>
index 0305727..a73b42a 100644 (file)
@@ -324,11 +324,12 @@ public:
 
 
   /**
-     Loads an xml file representing a diameter message base in a DTD document (#getDTD)
+     Loads an xml file/string representing a diameter message base in a DTD document (#getDTD)
 
-     @param xmlPathFile Complete path file to the xml document which represents the diameter message
+     @param xmlPathFile_or_string Complete path file or string representation for the xml document which represents the diameter message
+     @param pathfile_or_string boolean about the interpretation of the previous argument
   */
-  Message *createMessage(const std::string & xmlPathFile) throw(anna::RuntimeException);
+  Message *createMessage(const std::string & xmlPathFile_or_string, bool pathfile_or_string = true) throw(anna::RuntimeException);
 
 
   /**
index c358446..c500b97 100644 (file)
@@ -88,6 +88,16 @@ struct functions {
     U32 dummy2, dummy3;
     return (getSessionId(db, dummy1, dummy2, dummy3, dummy4));
   }
+
+
+  /**
+  * Gets the fixed Origin-Host AVP (DiameterIdentity) from datablock provided.
+  *
+  * @param db Provided Message DataBlock
+  *
+  * @return Origin-Host
+  */
+  static std::string getOriginHost(const anna::DataBlock &db) throw(anna::RuntimeException);
 };
 
 
index 9d23e57..268cb06 100644 (file)
@@ -85,9 +85,13 @@ Message* EngineImpl::createMessage(const CommandId *id) throw(anna::RuntimeExcep
 //------------------------------------------------------------------------------
 //-------------------------------------------------- EngineImpl::createMessage()
 //------------------------------------------------------------------------------
-Message *EngineImpl::createMessage(const std::string & xmlPathFile) throw(anna::RuntimeException) {
+Message *EngineImpl::createMessage(const std::string & xmlPathFile_or_string, bool pathfile_or_string) throw(anna::RuntimeException) {
   Message *result = createMessage();
-  result->loadXMLFile(xmlPathFile);
+  if (pathfile_or_string)
+    result->loadXMLFile(xmlPathFile_or_string);
+  else
+    result->loadXMLString(xmlPathFile_or_string);
+
   return result;
 }
 
index 20a0257..2036053 100644 (file)
@@ -150,3 +150,25 @@ std::string anna::diameter::helpers::base::functions::getSessionId(const anna::D
   return result;
 }
 
+
+std::string anna::diameter::helpers::base::functions::getOriginHost(const anna::DataBlock & db) throw(anna::RuntimeException) {
+  if(db.getSize() < Message::HeaderLength)
+    throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
+
+  const char *avpsDB = db.getData() + Message::HeaderLength;
+  int avpsLen = db.getSize() - Message::HeaderLength;
+  const char * originHostPtr = anna::diameter::codec::functions::findAVP(avpsDB, avpsLen, AVPID__Origin_Host);
+
+
+  if(originHostPtr == NULL)
+    throw anna::RuntimeException("Origin-Host AVP not found in DataBlock provided", ANNA_FILE_LOCATION);
+
+  // Decoded avp information:
+  AvpId _id;
+  char _flags;
+  int _length;
+  std::string result;
+  anna::diameter::codec::functions::decodeAVP(originHostPtr, _id, _flags, _length, result);
+  return result;
+}
+