From c56124ff93e8bceec159748dfe5ba8d56c62e3de Mon Sep 17 00:00:00 2001 From: "Eduardo Ramos Testillano (eramedu)" Date: Sun, 10 May 2020 22:18:42 +0200 Subject: [PATCH] Add second work package for REST API implementation 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. --- example/diameter/launcher/EventOperation.cpp | 245 +++++++++++++++--- example/diameter/launcher/EventOperation.hpp | 12 +- example/diameter/launcher/Launcher.cpp | 173 +++++++------ example/diameter/launcher/Launcher.hpp | 3 + example/diameter/launcher/MyHandler.cpp | 52 ++-- .../launcher/deployments/adml/start.sh | 1 + example/diameter/launcher/resources/HELP.md | 4 +- .../ct/flow-operations/answermsg2c_test.py | 80 ++++++ .../ct/flow-operations/answermsg2e_test.py | 80 ++++++ .../ct/flow-operations/sendhex_test.py | 81 ++++++ .../ct/flow-operations/sendmsg_test.py | 68 +++++ .../ct/parsing-operations/code_test.py | 2 +- .../ct/parsing-operations/decode_test.py | 2 +- .../ct/parsing-operations/loadmsg_test.py | 2 +- ...terHex.json.in => aaa-diameterHex.json.in} | 0 .../resources/aaa-diameterJson-request.json | 55 ++++ ...est.json => aar-diameterJson-request.json} | 0 .../aar-diameterJsonFromOwnToAF-request.json | 100 +++++++ .../rest_api/ct/resources/aarFromOwnToAF.hex | 1 + .../rest_api/ct/resources/aarFromOwnToAF.xml | 24 ++ .../diameterJsonHelper/examples/aaa.xml | 13 + include/anna/diameter/codec/EngineImpl.hpp | 7 +- .../anna/diameter/helpers/base/functions.hpp | 10 + source/diameter/codec/EngineImpl.cpp | 8 +- source/diameter/helpers/base/functions.cpp | 22 ++ 25 files changed, 888 insertions(+), 157 deletions(-) create mode 100644 example/diameter/launcher/resources/rest_api/ct/flow-operations/answermsg2c_test.py create mode 100644 example/diameter/launcher/resources/rest_api/ct/flow-operations/answermsg2e_test.py create mode 100644 example/diameter/launcher/resources/rest_api/ct/flow-operations/sendhex_test.py create mode 100644 example/diameter/launcher/resources/rest_api/ct/flow-operations/sendmsg_test.py rename example/diameter/launcher/resources/rest_api/ct/resources/{diameterHex.json.in => aaa-diameterHex.json.in} (100%) create mode 100644 example/diameter/launcher/resources/rest_api/ct/resources/aaa-diameterJson-request.json rename example/diameter/launcher/resources/rest_api/ct/resources/{diameterJson-request.json => aar-diameterJson-request.json} (100%) create mode 100644 example/diameter/launcher/resources/rest_api/ct/resources/aar-diameterJsonFromOwnToAF-request.json create mode 100644 example/diameter/launcher/resources/rest_api/ct/resources/aarFromOwnToAF.hex create mode 100644 example/diameter/launcher/resources/rest_api/ct/resources/aarFromOwnToAF.xml create mode 100644 example/diameter/launcher/resources/rest_api/helpers/diameterJsonHelper/examples/aaa.xml diff --git a/example/diameter/launcher/EventOperation.cpp b/example/diameter/launcher/EventOperation.cpp index be0c168..935940d 100644 --- a/example/diameter/launcher/EventOperation.cpp +++ b/example/diameter/launcher/EventOperation.cpp @@ -35,13 +35,14 @@ //#include //#include //#include -//#include +#include //#include //#include //#include #include //#include //#include +#include // //// Process //#include @@ -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 (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 (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() : ""; // 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 (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 (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() : ""; // 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 (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 (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 (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 (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 // ///////////////// diff --git a/example/diameter/launcher/EventOperation.hpp b/example/diameter/launcher/EventOperation.hpp index 59ede33..be117cf 100644 --- a/example/diameter/launcher/EventOperation.hpp +++ b/example/diameter/launcher/EventOperation.hpp @@ -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__ diff --git a/example/diameter/launcher/Launcher.cpp b/example/diameter/launcher/Launcher.cpp index d0151cd..e52b5d2 100644 --- a/example/diameter/launcher/Launcher.cpp +++ b/example/diameter/launcher/Launcher.cpp @@ -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() : ""; // 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() : ""; // 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() : ""; // 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 != "") { diff --git a/example/diameter/launcher/Launcher.hpp b/example/diameter/launcher/Launcher.hpp index dab3415..c8f34e6 100644 --- a/example/diameter/launcher/Launcher.hpp +++ b/example/diameter/launcher/Launcher.hpp @@ -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 *); diff --git a/example/diameter/launcher/MyHandler.cpp b/example/diameter/launcher/MyHandler.cpp index ed49f4f..49205ed 100644 --- a/example/diameter/launcher/MyHandler.cpp +++ b/example/diameter/launcher/MyHandler.cpp @@ -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__ diff --git a/example/diameter/launcher/deployments/adml/start.sh b/example/diameter/launcher/deployments/adml/start.sh index b8f2e9f..b53a90e 100755 --- a/example/diameter/launcher/deployments/adml/start.sh +++ b/example/diameter/launcher/deployments/adml/start.sh @@ -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 !" diff --git a/example/diameter/launcher/resources/HELP.md b/example/diameter/launcher/resources/HELP.md index 8b453fa..fec3b72 100644 --- a/example/diameter/launcher/resources/HELP.md +++ b/example/diameter/launcher/resources/HELP.md @@ -1162,7 +1162,7 @@ or ``` { "result":"", - "response":"" + "response":"" } ``` @@ -1193,7 +1193,7 @@ or ``` { "result":"", - "response":"" + "response":"" } ``` 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 index 0000000..63e7a21 --- /dev/null +++ b/example/diameter/launcher/resources/rest_api/ct/flow-operations/answermsg2c_test.py @@ -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 index 0000000..6f97b84 --- /dev/null +++ b/example/diameter/launcher/resources/rest_api/ct/flow-operations/answermsg2e_test.py @@ -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 index 0000000..a07b7c4 --- /dev/null +++ b/example/diameter/launcher/resources/rest_api/ct/flow-operations/sendhex_test.py @@ -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 index 0000000..f3afa4c --- /dev/null +++ b/example/diameter/launcher/resources/rest_api/ct/flow-operations/sendmsg_test.py @@ -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) + diff --git a/example/diameter/launcher/resources/rest_api/ct/parsing-operations/code_test.py b/example/diameter/launcher/resources/rest_api/ct/parsing-operations/code_test.py index 226cdae..5831a45 100644 --- a/example/diameter/launcher/resources/rest_api/ct/parsing-operations/code_test.py +++ b/example/diameter/launcher/resources/rest_api/ct/parsing-operations/code_test.py @@ -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") diff --git a/example/diameter/launcher/resources/rest_api/ct/parsing-operations/decode_test.py b/example/diameter/launcher/resources/rest_api/ct/parsing-operations/decode_test.py index d24e51d..0e5e8fa 100644 --- a/example/diameter/launcher/resources/rest_api/ct/parsing-operations/decode_test.py +++ b/example/diameter/launcher/resources/rest_api/ct/parsing-operations/decode_test.py @@ -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: diff --git a/example/diameter/launcher/resources/rest_api/ct/parsing-operations/loadmsg_test.py b/example/diameter/launcher/resources/rest_api/ct/parsing-operations/loadmsg_test.py index 299f299..ef8a286 100644 --- a/example/diameter/launcher/resources/rest_api/ct/parsing-operations/loadmsg_test.py +++ b/example/diameter/launcher/resources/rest_api/ct/parsing-operations/loadmsg_test.py @@ -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/diameterHex.json.in b/example/diameter/launcher/resources/rest_api/ct/resources/aaa-diameterHex.json.in similarity index 100% rename from example/diameter/launcher/resources/rest_api/ct/resources/diameterHex.json.in rename to example/diameter/launcher/resources/rest_api/ct/resources/aaa-diameterHex.json.in 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 index 0000000..e087719 --- /dev/null +++ b/example/diameter/launcher/resources/rest_api/ct/resources/aaa-diameterJson-request.json @@ -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/diameterJson-request.json b/example/diameter/launcher/resources/rest_api/ct/resources/aar-diameterJson-request.json similarity index 100% rename from example/diameter/launcher/resources/rest_api/ct/resources/diameterJson-request.json rename to example/diameter/launcher/resources/rest_api/ct/resources/aar-diameterJson-request.json 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 index 0000000..f9d08ca --- /dev/null +++ b/example/diameter/launcher/resources/rest_api/ct/resources/aar-diameterJsonFromOwnToAF-request.json @@ -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 index 0000000..1b4e3db --- /dev/null +++ b/example/diameter/launcher/resources/rest_api/ct/resources/aarFromOwnToAF.hex @@ -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 index 0000000..15dc690 --- /dev/null +++ b/example/diameter/launcher/resources/rest_api/ct/resources/aarFromOwnToAF.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + 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 index 0000000..8338a6a --- /dev/null +++ b/example/diameter/launcher/resources/rest_api/helpers/diameterJsonHelper/examples/aaa.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/include/anna/diameter/codec/EngineImpl.hpp b/include/anna/diameter/codec/EngineImpl.hpp index 0305727..a73b42a 100644 --- a/include/anna/diameter/codec/EngineImpl.hpp +++ b/include/anna/diameter/codec/EngineImpl.hpp @@ -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); /** diff --git a/include/anna/diameter/helpers/base/functions.hpp b/include/anna/diameter/helpers/base/functions.hpp index c358446..c500b97 100644 --- a/include/anna/diameter/helpers/base/functions.hpp +++ b/include/anna/diameter/helpers/base/functions.hpp @@ -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); }; diff --git a/source/diameter/codec/EngineImpl.cpp b/source/diameter/codec/EngineImpl.cpp index 9d23e57..268cb06 100644 --- a/source/diameter/codec/EngineImpl.cpp +++ b/source/diameter/codec/EngineImpl.cpp @@ -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; } diff --git a/source/diameter/helpers/base/functions.cpp b/source/diameter/helpers/base/functions.cpp index 20a0257..2036053 100644 --- a/source/diameter/helpers/base/functions.cpp +++ b/source/diameter/helpers/base/functions.cpp @@ -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; +} + -- 2.20.1