From 431d322261ecfd6ef354abb392edbf8987e2407a Mon Sep 17 00:00:00 2001 From: Eduardo Ramos Testillano Date: Mon, 8 Jun 2015 00:06:29 +0200 Subject: [PATCH] Multistack launcher --- example/diameter/launcher/Launcher.cpp | 123 +++++++++++++----- example/diameter/launcher/Launcher.hpp | 27 ++-- .../diameter/launcher/MyDiameterEntity.cpp | 46 +++++++ example/diameter/launcher/Node.cpp | 110 ++++++++++++++++ example/diameter/launcher/Node.hpp | 48 +++++++ .../launcher/deployments/basic/hex_examples | 1 + .../launcher/deployments/basic/operation.sh | 34 +---- .../launcher/deployments/basic/xml_examples | 1 + .../deployments/ft-client/hex_examples | 2 +- .../deployments/ft-client/operation.sh | 2 +- .../deployments/ft-client/xml_examples | 2 +- example/diameter/launcher/main.cpp | 3 +- .../hex_examples/aar-bad.hex | 0 .../basic => resources}/hex_examples/aar.hex | 0 .../hex_examples/aar2-bad.hex | 0 .../hex_examples/aar3-bad.hex | 0 .../hex_examples/readme.txt | 0 .../hex_examples/tspCCA.hex | 0 .../hex_examples/tspCCR.hex | 0 .../resources/scripts/operation_curl.sh | 25 ++++ .../resources/scripts/operation_signal.sh | 31 +++++ .../resources/scripts/operation_tps.sh | 66 ++++++++++ .../{ => scripts}/pcap2diameterHex.sh | 0 .../basic => resources}/xml_examples/aaa.xml | 0 .../basic => resources}/xml_examples/aar.xml | 0 .../basic => resources}/xml_examples/aar2.xml | 0 .../basic => resources}/xml_examples/ccr.xml | 0 include/anna/diameter/codec/EngineImpl.hpp | 14 ++ source/diameter/codec/EngineImpl.cpp | 4 + source/diameter/codec/Message.cpp | 8 +- 30 files changed, 461 insertions(+), 86 deletions(-) create mode 100644 example/diameter/launcher/Node.cpp create mode 100644 example/diameter/launcher/Node.hpp create mode 120000 example/diameter/launcher/deployments/basic/hex_examples mode change 100755 => 120000 example/diameter/launcher/deployments/basic/operation.sh create mode 120000 example/diameter/launcher/deployments/basic/xml_examples rename example/diameter/launcher/{deployments/basic => resources}/hex_examples/aar-bad.hex (100%) rename example/diameter/launcher/{deployments/basic => resources}/hex_examples/aar.hex (100%) rename example/diameter/launcher/{deployments/basic => resources}/hex_examples/aar2-bad.hex (100%) rename example/diameter/launcher/{deployments/basic => resources}/hex_examples/aar3-bad.hex (100%) rename example/diameter/launcher/{deployments/basic => resources}/hex_examples/readme.txt (100%) rename example/diameter/launcher/{deployments/basic => resources}/hex_examples/tspCCA.hex (100%) rename example/diameter/launcher/{deployments/basic => resources}/hex_examples/tspCCR.hex (100%) create mode 100755 example/diameter/launcher/resources/scripts/operation_curl.sh create mode 100755 example/diameter/launcher/resources/scripts/operation_signal.sh create mode 100755 example/diameter/launcher/resources/scripts/operation_tps.sh rename example/diameter/launcher/resources/{ => scripts}/pcap2diameterHex.sh (100%) rename example/diameter/launcher/{deployments/basic => resources}/xml_examples/aaa.xml (100%) rename example/diameter/launcher/{deployments/basic => resources}/xml_examples/aar.xml (100%) rename example/diameter/launcher/{deployments/basic => resources}/xml_examples/aar2.xml (100%) rename example/diameter/launcher/{deployments/basic => resources}/xml_examples/ccr.xml (100%) diff --git a/example/diameter/launcher/Launcher.cpp b/example/diameter/launcher/Launcher.cpp index 59f5202..f5fac98 100644 --- a/example/diameter/launcher/Launcher.cpp +++ b/example/diameter/launcher/Launcher.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include @@ -24,6 +23,41 @@ #define SIGUSR2_TASKS_OUTPUT_FILENAME "./sigusr2.tasks.output" #define DIAMETER_CODEC_ENGINE_NAME_PREFIX "MyCodecEngine" +const char *ServicesDTD = "\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +"; + + Launcher::Launcher() : anna::comm::Application("launcher", "DiameterLauncher", "1.1"), a_communicator(NULL) { a_myDiameterEngine = new MyDiameterEngine(); a_myDiameterEngine->setRealm("ADL.ericsson.com"); @@ -40,6 +74,8 @@ Launcher::Launcher() : anna::comm::Application("launcher", "DiameterLauncher", " a_diameterLocalServer = NULL; a_cerPathfile = "cer.xml"; a_dwrPathfile = "dwr.xml"; + a_workingStackId = 0; + // Burst a_burstCycle = 1; a_burstRepeat = false; @@ -341,36 +377,49 @@ throw(anna::RuntimeException) { // Stack: a_codecEngine = new anna::diameter::codec::Engine(DIAMETER_CODEC_ENGINE_NAME_PREFIX); anna::diameter::stack::Engine &stackEngine = anna::diameter::stack::Engine::instantiate(); - anna::diameter::stack::Dictionary * d = stackEngine.createDictionary(0 /* stack id; its value don't mind, is not used (ADL is monostack) */); - // Analyze comma-separated list: - anna::Tokenizer lst; - std::string dictionaryParameter = cl.getValue("dictionary"); - lst.apply(dictionaryParameter, ","); - - if(lst.size() >= 1) { // always true (at least one, because --dictionary is mandatory) - anna::Tokenizer::const_iterator tok_min(lst.begin()); - anna::Tokenizer::const_iterator tok_max(lst.end()); - anna::Tokenizer::const_iterator tok_iter; - std::string pathFile; - d->allowUpdates(); - - for(tok_iter = tok_min; tok_iter != tok_max; tok_iter++) { - pathFile = anna::Tokenizer::data(tok_iter); - d->load(pathFile); + std::string stacks = cl.getValue("stacks"); + bool multistack = false; + try { + anna::Tokenizer stacksTok; + stacksTok.apply(stacks, "#"); + anna::Tokenizer::const_iterator stacks_it, stack_it; + + for(stacks_it = stacksTok.begin(); stacks_it != stacksTok.end(); stacks_it++) { + std::string stack = anna::Tokenizer::data(stacks_it); + anna::Tokenizer stackTok; + stackTok.apply(stack, ","); + + if(stackTok.size() == 1) { + if(stacksTok.size() != 1) + throw anna::RuntimeException("Application Id value is mandatory when more than one stack is going to be configured", ANNA_FILE_LOCATION); + + anna::diameter::stack::Dictionary * d = stackEngine.createDictionary(0 /* no matter */, stack); // the stack is the dictionary + codecEngine->setDictionary(d); + break; + } + + if(stackTok.size() != 2) + throw anna::RuntimeException("Each stack must be in the form ','", ANNA_FILE_LOCATION); + + multistack = true; + stack_it = stackTok.begin(); + unsigned int stackId = atoll(anna::Tokenizer::data(stack_it)); + stack_it++; + std::string file = anna::Tokenizer::data(stack_it); + anna::diameter::stack::Dictionary * d = stackEngine.createDictionary(stackId, file); } - } - getCodecEngine()->setDictionary(d); // XXXX esto cambiara... - LOGDEBUG(anna::Logger::debug(getCodecEngine()->asString(), ANNA_FILE_LOCATION)); + // Auto stack selection based on Application-ID: + if (multistack) getCodecEngine()->selectStackWithApplicationId(true); - if(lst.size() > 1) { - std::string all_in_one = "./dictionary-all-in-one.xml"; - std::ofstream out(all_in_one.c_str(), std::ifstream::out); - std::string buffer = d->asXMLString(); - out.write(buffer.c_str(), buffer.size()); - out.close(); - std::cout << "Written accumulated '" << all_in_one << "' (provide it next time to be more comfortable)." << std::endl; + std::cout << "Stacks provided: " << std::endl; + std::cout << anna::functions::tab(stackEngine.asString(false /* light */)); + std::cout << std::endl; + } catch(anna::RuntimeException &ex) { + _exit(ex.asString()); } + //LOGDEBUG(anna::Logger::debug(codecEngine->asString(), ANNA_FILE_LOCATION)); + /////////////////////////////// // Diameter library COUNTERS // @@ -952,7 +1001,7 @@ std::string Launcher::help() const throw() { result += "\n vi /var/tmp/anna.context."; result += "\n"; result += "\nA complete xml report will show all the context information (counters, alarms, statistics,"; - result += "\n handlers, diameter dictionary, etc.), and a powerful log module could dump all the events"; + result += "\n handlers, diameter stacks, etc.), and a powerful log module could dump all the events"; result += "\n processed and flow information. Statistics could be analized at context dump and optionally"; result += "\n written to disk as sample files (useful for graphs and spreadsheet reports) with all the"; result += "\n measurements."; @@ -967,8 +1016,8 @@ std::string Launcher::help() const throw() { result += "\n posibilities, many of which could be modified on the air through the management interface"; result += "\n (we will talk later about this great feature). Some of the more common parameters are:"; result += "\n"; - result += "\nAs mandatory, the stack definition given through the xml dictionary:"; - result += "\n --dictionary "; + result += "\nAs mandatory, the stacks enabled given through the application-id and the xml dictionary:"; + result += "\n --stacks "; result += "\n"; result += "\nActing as a diameter server (accepting i.e. 10 connections), you would have:"; result += "\n --diameterServer localhost:3868 --diameterServerSessions 10 --entityServerSessions 0"; @@ -998,6 +1047,11 @@ std::string Launcher::help() const throw() { result += "\n"; result += "\nhelp This help. Startup information-level traces also dump this help."; result += "\n"; + result += "\n--------------------------------------------------------------------------------------- Stack selection"; + result += "\n"; + result += "\nstack| Select current working stack id (node selection). Many operations"; + result += " could use this value if proceed, in multistack configurations."; + result += "\n"; result += "\n------------------------------------------------------------------------------------ Parsing operations"; result += "\n"; result += "\ncode|| Encodes source file (pathfile) into target file (pathfile)."; @@ -1075,14 +1129,14 @@ std::string Launcher::help() const throw() { result += "\n to the peer which sent the request. If user wants to test a specific answer without changing it,"; result += "\n use sendxml/sendhex operations better than programming."; result += "\n"; - result += "\nBalance ('-balance' command line parameter) could be used to forward server socket receptions through"; + result += "\nBalance ('--balance' command line parameter) could be used to forward server socket receptions through"; result += "\n entity servers by mean a round-robin algorithm. Both diameter server socket and entity targets should"; result += "\n have been configured, that is to say: launcher acts as client and server. If no balance is used, an"; result += "\n standard delivery is performed: first primary entity server, secondary when fails, etc."; result += "\n"; result += "\n--------------------------------------------------------------------------- Processing types (log tags)"; result += "\n"; - result += "\nUsed as log file extensions (when '-splitLog' is provided on command line) and context preffixes on log"; + result += "\nUsed as log file extensions (when '--splitLog' is provided on command line) and context preffixes on log"; result += "\n details when unique log file is dumped:"; result += "\n"; result += "\n [sent2e/send2eError] Send to entity (success/error)"; @@ -1115,7 +1169,7 @@ std::string Launcher::help() const throw() { result += "\n burst|push| Sends specific non-aynchronous load."; result += "\n burst|pop| Skip send burst messages in order to reduce over-the-air requests."; result += "\n Popping all OTA requests implies burst stop because no more answer"; - result += "\n will arrive to the process. Burst output file (-burstLog command"; + result += "\n will arrive to the process. Burst output file (--burstLog command"; result += "\n line parameter) shows popped messages with crosses (x). Each cross"; result += "\n represents one received answer for which no new request is sent."; result += "\n burst|stop Stops the burst cycle. You can resume pushing 1 load amount."; @@ -1135,7 +1189,7 @@ std::string Launcher::help() const throw() { result += "\n------------------------------------------------------------------------- Operations via HTTP interface"; result += "\n"; result += "\nAll the operations described above can be used through the optional HTTP interface. You only have"; - result += "\n to define the http server at the command line with something like: '-httpServer localhost:9000'."; + result += "\n to define the http server at the command line with something like: '--httpServer localhost:9000'."; result += "\nTo send the task, we shall build the http request body with the operation string. Some examples"; result += "\n using curl client could be:"; result += "\n"; @@ -1685,3 +1739,4 @@ throw() { anna::statistics::Engine::instantiate().asXML(result); return result; } + diff --git a/example/diameter/launcher/Launcher.hpp b/example/diameter/launcher/Launcher.hpp index d7e4bba..f5f1eb9 100644 --- a/example/diameter/launcher/Launcher.hpp +++ b/example/diameter/launcher/Launcher.hpp @@ -50,14 +50,15 @@ class Launcher : public anna::comm::Application { anna::timex::Engine* a_timeEngine; MyCounterRecorder *a_counterRecorder; MyCounterRecorderClock *a_counterRecorderClock; - std::string a_cerPathfile; - std::string a_dwrPathfile; + std::string a_cerPathfile; // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + std::string a_dwrPathfile; // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + std::string a_workingNode; // this is the node name (usually equals to the Origin-Realm value // Burst feature int a_burstCycle; bool a_burstRepeat; bool a_burstActive; - std::map < int /* dummy, p.e. used for order number */, anna::diameter::comm::Message* > a_burstMessages; + std::map < int /* dummy, p.e. used for order number */, anna::diameter::comm::Message* > a_burstMessages; // XXXXXXXXXXXXXXXXXXXXX int a_burstLoadIndx; std::map::const_iterator a_burstDeliveryIt; int a_otaRequest; @@ -67,7 +68,7 @@ class Launcher : public anna::comm::Application { anna::Recycler a_commMessages; anna::comm::ServerSocket* a_httpServerSocket; // HTTP - MyLocalServer* a_diameterLocalServer; // DIAMETER + MyLocalServer* a_diameterLocalServer; // DIAMETER XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX void checkTimeMeasure(const char * commandLineParameter, bool optional = true) throw(anna::RuntimeException); void initialize() throw(anna::RuntimeException); // HTTP void run() throw(anna::RuntimeException); @@ -75,22 +76,24 @@ class Launcher : public anna::comm::Application { public: Launcher(); - anna::diameter::codec::Engine *getCodecEngine() const throw() { return a_codecEngine; } // XXXXXXXXXXXXXXXXX El del nodo de trabajo + anna::diameter::codec::Engine *getCodecEngine() const throw() { return a_codecEngine; } + std::string getWorkingNode() const throw() { return a_workingNode; } + void setWorkingNode(const std::string &node) throw() { a_workingNode = node; } MyCommunicator *getCommunicator() throw() { return a_communicator; } MyDiameterEngine* getMyDiameterEngine() const throw() { return (a_myDiameterEngine); } - void baseProtocolSetupAsClient() throw(anna::RuntimeException); - MyDiameterEntity *getEntity() throw() { return a_entity; } - MyLocalServer* getDiameterLocalServer() throw() { return a_diameterLocalServer; } + void baseProtocolSetupAsClient(void) throw(anna::RuntimeException); + MyDiameterEntity *getEntity() throw() { return a_entity; } // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + MyLocalServer* getDiameterLocalServer() throw() { return a_diameterLocalServer; } // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX void eventOperation(const std::string &, std::string &) throw(anna::RuntimeException); bool logEnabled() const throw() { return (((a_logFile == "") || (a_logFile == "null")) ? false : true); } - void writeLogFile(const anna::DataBlock & db, const std::string &logExtension, const std::string &detail, anna::diameter::codec::Engine *codecEngine) const throw(); - void writeLogFile(const anna::diameter::codec::Message & decodedMessage, const std::string &logExtension, const std::string &detail) const throw(); + void writeLogFile(const anna::DataBlock & db, const std::string &logExtension, const std::string &detail) const throw(); // XXXXXXXXXXXXXXXXXXXXX + void writeLogFile(const anna::diameter::codec::Message & decodedMessage, const std::string &logExtension, const std::string &detail) const throw(); // XXXXXXXXXXXXXXXXXX void writeBurstLogFile(const std::string &buffer) throw(); bool burstLogEnabled() const throw() { return (((a_burstLogFile == "") || (a_burstLogFile == "null")) ? false : true); } - void startDiameterServer(int) throw(anna::RuntimeException); + void startDiameterServer(int) throw(anna::RuntimeException); // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX void forceCountersRecord() throw(anna::RuntimeException) { if (a_counterRecorderClock) a_counterRecorderClock->tick(); } - anna::xml::Node* asXML(anna::xml::Node* parent) const throw(); + anna::xml::Node* asXML(anna::xml::Node* parent) const throw(); // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX void resetStatistics() throw() { a_myDiameterEngine->resetStatistics(); } void resetCounters() throw(); void signalUSR2() throw(anna::RuntimeException); diff --git a/example/diameter/launcher/MyDiameterEntity.cpp b/example/diameter/launcher/MyDiameterEntity.cpp index d16974b..6a27607 100644 --- a/example/diameter/launcher/MyDiameterEntity.cpp +++ b/example/diameter/launcher/MyDiameterEntity.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include // Process #include "MyDiameterEntity.hpp" @@ -45,6 +47,50 @@ void MyDiameterEntity::eventRequestRetransmission(const anna::diameter::comm::Cl } +int MyDiameterEntity::readSocketId(const anna::diameter::comm::Message* message, int maxClientSessions) const throw() { + CommandLine& cl(anna::CommandLine::instantiate()); + std::string sessionBasedModelsType = (cl.exists("sessionBasedModelsClientSocketSelection") ? cl.getValue("sessionBasedModelsClientSocketSelection") : "SessionIdLowPart"); + + if(sessionBasedModelsType == "RoundRobin") return -1; // IEC also would return -1 + + try { + // Service-Context-Id: + anna::diameter::helpers::dcca::ChargingContext::_v chargingContext; + std::string scid = anna::diameter::helpers::dcca::functions::getServiceContextId(message->getBody(), chargingContext); + + switch(chargingContext) { + case anna::diameter::helpers::dcca::ChargingContext::Data: + case anna::diameter::helpers::dcca::ChargingContext::Voice: + case anna::diameter::helpers::dcca::ChargingContext::Content: { + // Session-Id: ';;[;="">]' + std::string sid = anna::diameter::helpers::base::functions::getSessionId(message->getBody()); + std::string diameterIdentity, optional; + anna::U32 high, low; + anna::diameter::helpers::base::functions::decodeSessionId(sid, diameterIdentity, high, low /* context-teid */, optional); + + if(sessionBasedModelsType == "SessionIdLowPart") return (low % maxClientSessions); + + if(sessionBasedModelsType == "SessionIdHighPart") return (high % maxClientSessions); + + if(sessionBasedModelsType == "SessionIdOptionalPart") return (atoi(optional.c_str()) % maxClientSessions); + } + case anna::diameter::helpers::dcca::ChargingContext::SMS: + case anna::diameter::helpers::dcca::ChargingContext::MMS: + case anna::diameter::helpers::dcca::ChargingContext::Unknown: + default: + return -1; // IEC model and Unknown traffic types + } + } catch(anna::RuntimeException &ex) { + LOGDEBUG( + std::string msg = ex.getText(); + msg += " | Round-robin between sessions will be used to send"; + anna::Logger::debug(msg, ANNA_FILE_LOCATION); + ); + } + + return -1; +} + void MyDiameterEntity::eventRequest(anna::diameter::comm::ClientSession *clientSession, const anna::DataBlock &message) throw(anna::RuntimeException) { LOGMETHOD(anna::TraceMethod tm("launcher::MyDiameterEntity", "eventRequest", ANNA_FILE_LOCATION)); diff --git a/example/diameter/launcher/Node.cpp b/example/diameter/launcher/Node.cpp new file mode 100644 index 0000000..2d87d8a --- /dev/null +++ b/example/diameter/launcher/Node.cpp @@ -0,0 +1,110 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +// Standard +#include + +// Project +//#include + +// Process +#include "Node.hpp" + + +void Node::clear () throw() { + for (reacting_answers_const_iterator it = a_deques.begin(); it != a_deques.end(); it++) { + anna::diameter::codec::Engine *engine = anna::functions::component (ANNA_FILE_LOCATION); + engine->releaseMessage(*(it->second->begin())); + delete(it->second); + } + a_deques.clear(); +} + +void Node::dump () throw() { + std::string outfilename, xmlString; + for(reacting_answers_const_iterator it = a_deques.begin(); it != a_deques.end(); it++) { + int sequence = 1; + for(codec_messages_deque_const_iterator itm = it->second->begin(); itm != it->second->end(); itm++) { + // programmed_answer.. + outfilename = "programmed_answer."; + outfilename += anna::functions::asString(it->first); + outfilename += "."; + outfilename += anna::functions::asString(sequence++); + outfilename += ".xml"; + std::ofstream outfile(outfilename.c_str(), std::ifstream::out); + xmlString = (*itm)->asXMLString(); + outfile.write(xmlString.c_str(), xmlString.size()); + outfile.close(); + } + } +} + +void Node::addMessage(int code, anna::diameter::codec::Message *message) throw() { + reacting_answers_const_iterator it = a_deques.find(code); + if (it != a_deques.end()) { + it->second->push_back(message); + } + else { + codec_messages_deque *deque = new codec_messages_deque; + a_deques[code] = deque; + deque->push_back(message); + } +} + +anna::diameter::codec::Message* Node::getMessage(int code) const throw() { //get the front message (begin()), returns NULL if deque is empty + anna::diameter::codec::Message *result = NULL; + reacting_answers_const_iterator it = a_deques.find(code); + if (it != a_deques.end()) { + if (!it->second->empty()) result = *(it->second->begin()); + } + return result; +} + +void Node::nextMessage(int code) throw() { //pops the deque and release the message (when deque is not empty: deque::empty) + reacting_answers_const_iterator it = a_deques.find(code); + if (it != a_deques.end()) { + if (!it->second->empty()) { + anna::diameter::codec::Engine *engine = anna::functions::component (ANNA_FILE_LOCATION); + if (a_rotate) { + addMessage(code, *(it->second->begin())); + } + else { + engine->releaseMessage(*(it->second->begin())); + } + it->second->pop_front(); + } + } +} + +std::string Node::asString(const char *queueName) const throw() { + std::string result = ""; + std::string aux = "FIFO QUEUE '"; + aux += queueName; + aux += "', Rotation "; + aux += a_rotate ? "enabled":"disabled"; + result += anna::functions::highlightJustify(aux); + if(a_deques.size() != 0) { + for(reacting_answers_const_iterator it = a_deques.begin(); it != a_deques.end(); it++) { + if (it->second->size() != 0) { + aux = "Answer code "; + aux += anna::functions::asString(it->first); + result += anna::functions::highlightJustify(aux, anna::functions::TextHighlightMode::OverAndUnderline, + anna::functions::TextJustifyMode::Left, '-'); + for(codec_messages_deque_const_iterator itm = it->second->begin(); itm != it->second->end(); itm++) { + result += (*itm)->asXMLString(); + result += "\n"; + } + result += "\n"; + } + } + } + else { + result = "No ocurrences found\n\n"; + } + return result; +} diff --git a/example/diameter/launcher/Node.hpp b/example/diameter/launcher/Node.hpp new file mode 100644 index 0000000..95b6af2 --- /dev/null +++ b/example/diameter/launcher/Node.hpp @@ -0,0 +1,48 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef example_diameter_launcher_Node_hpp +#define example_diameter_launcher_Node_hpp + +// Standard +#include + +// Process +#include "MyDiameterEntity.hpp" +#include "MyLocalServer.hpp" + + +class Node { + + std::string a_name; + unsigned int a_applicationId; + std::string a_cer; // path file + std::string a_dwr; // path file + MyDiameterEntity *a_entity; + MyLocalServer *a_diameterServer; + bool a_balance; // Balance over entity servers instead of doing standard behaviour (first primary, secondary if fails, etc.). Default: false. + bool a_ignoreErrors; // default: false + + +public: + Node() { a_rotate = false; } + ~Node() { clear(); } + + + bool rotate() const throw() { return a_rotate; } + void rotate(bool r) throw() { a_rotate = r; } + + void clear () throw(); + void dump () throw(); + void addMessage(int code, anna::diameter::codec::Message *message) throw(); + anna::diameter::codec::Message* getMessage(int code) const throw(); + void nextMessage(int code) throw(); + std::string asString(const char *queueName) const throw(); +}; + +#endif diff --git a/example/diameter/launcher/deployments/basic/hex_examples b/example/diameter/launcher/deployments/basic/hex_examples new file mode 120000 index 0000000..0a74a2d --- /dev/null +++ b/example/diameter/launcher/deployments/basic/hex_examples @@ -0,0 +1 @@ +../../resources/hex_examples/ \ No newline at end of file diff --git a/example/diameter/launcher/deployments/basic/operation.sh b/example/diameter/launcher/deployments/basic/operation.sh deleted file mode 100755 index 5f3afc0..0000000 --- a/example/diameter/launcher/deployments/basic/operation.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -############# -# FUNCTIONS # -############# -_exit () { - echo - echo -e $1 - echo - exit 1 -} - -############# -# EXECUTION # -############# -cd `dirname $0` -echo -# Get the PID: -[ ! -f .pid ] && _exit "Can't found '`pwd`/.pid'.\nTry to pgrep your process name and dump pid to that file." -PID=`cat .pid` - -# Send operation: -[ "$1" = "" ] && _exit "Use: $0 ; i.e.: $0 help" -echo $1 > sigusr2.tasks.input -kill -s SIGUSR2 $PID - -sleep 1 -echo -echo -echo "You could see results on '`pwd`/sigusr2.tasks.output' file." -echo -echo - diff --git a/example/diameter/launcher/deployments/basic/operation.sh b/example/diameter/launcher/deployments/basic/operation.sh new file mode 120000 index 0000000..c9b45ae --- /dev/null +++ b/example/diameter/launcher/deployments/basic/operation.sh @@ -0,0 +1 @@ +../../resources/scripts/operation_signal.sh \ No newline at end of file diff --git a/example/diameter/launcher/deployments/basic/xml_examples b/example/diameter/launcher/deployments/basic/xml_examples new file mode 120000 index 0000000..c18402a --- /dev/null +++ b/example/diameter/launcher/deployments/basic/xml_examples @@ -0,0 +1 @@ +../../resources/xml_examples \ No newline at end of file diff --git a/example/diameter/launcher/deployments/ft-client/hex_examples b/example/diameter/launcher/deployments/ft-client/hex_examples index 9eee58b..74e9662 120000 --- a/example/diameter/launcher/deployments/ft-client/hex_examples +++ b/example/diameter/launcher/deployments/ft-client/hex_examples @@ -1 +1 @@ -../basic/hex_examples \ No newline at end of file +../../resources/hex_examples \ No newline at end of file diff --git a/example/diameter/launcher/deployments/ft-client/operation.sh b/example/diameter/launcher/deployments/ft-client/operation.sh index a61ab5e..c9b45ae 120000 --- a/example/diameter/launcher/deployments/ft-client/operation.sh +++ b/example/diameter/launcher/deployments/ft-client/operation.sh @@ -1 +1 @@ -../basic/operation.sh \ No newline at end of file +../../resources/scripts/operation_signal.sh \ No newline at end of file diff --git a/example/diameter/launcher/deployments/ft-client/xml_examples b/example/diameter/launcher/deployments/ft-client/xml_examples index 903e6fe..c18402a 120000 --- a/example/diameter/launcher/deployments/ft-client/xml_examples +++ b/example/diameter/launcher/deployments/ft-client/xml_examples @@ -1 +1 @@ -../basic/xml_examples \ No newline at end of file +../../resources/xml_examples \ No newline at end of file diff --git a/example/diameter/launcher/main.cpp b/example/diameter/launcher/main.cpp index b27f4ee..f0d780d 100644 --- a/example/diameter/launcher/main.cpp +++ b/example/diameter/launcher/main.cpp @@ -44,7 +44,7 @@ int main(int argc, const char** argv) { commandLine.add("entityServerSessions", anna::CommandLine::Argument::Optional, "Diameter entity server sessions (0: diameter entity disabled). Default value of 1"); commandLine.add("balance", anna::CommandLine::Argument::Optional, "Balance over entity servers instead of doing standard behaviour (first primary, secondary if fails, etc.)", false); commandLine.add("sessionBasedModelsClientSocketSelection", anna::CommandLine::Argument::Optional, "By default, round-robin will be applied for IEC model (SMS/MMS), and Session-Id Low Part will be analyzed for ECUR/SCUR model (data, voice and content). You could change ECUR/SCUR analysis behaviour providing 'SessionIdHighPart', 'SessionIdOptionalPart' (atoi applied; usually subscriber id data, i.e. MSISDN or IMSI) and 'RoundRobin'."); - commandLine.add("dictionary", anna::CommandLine::Argument::Mandatory, "Diameter dictionary pathfiles (could be one or more ocurrences in a comma separated list, in order to accumulate loads). For example: avps_etsi.xml,avps_ietf.xml,avps_tgpp.xml,commands_qosControl.xml"); + commandLine.add("stacks", anna::CommandLine::Argument::Mandatory, ": this is a list of #-separated stacks defined by a comma-separated pair . If only one stack is provided, application-id could be omitted and then, all the messages will be decoded with the dictionary regardless the value of the application-id (the stack will be registered with id=0)."); commandLine.add("ignoreFlags", anna::CommandLine::Argument::Optional, "Ignore flags on validation (at the moment only bits M & P from AVPs, because V bit is too important; no operation flags could be checked). Also force compact xml presentation ignoring flags during dictionary elements identification", false); commandLine.add("ignoreErrors", anna::CommandLine::Argument::Optional, "Local server skips requests errors analysis which would prepare automatic answers for them when a problem is found. If no answer is programmed and entity is configured, a failed request would be forwarded (delegates at the end point) even if this parameter is missing", false); commandLine.add("allowedInactivityTime", anna::CommandLine::Argument::Optional, "Milliseconds for the maximum allowed inactivity time on server sessions born over the local server before being reset. If missing, default value of 90000 will be assigned"); @@ -70,3 +70,4 @@ int main(int argc, const char** argv) { return 0; } + diff --git a/example/diameter/launcher/deployments/basic/hex_examples/aar-bad.hex b/example/diameter/launcher/resources/hex_examples/aar-bad.hex similarity index 100% rename from example/diameter/launcher/deployments/basic/hex_examples/aar-bad.hex rename to example/diameter/launcher/resources/hex_examples/aar-bad.hex diff --git a/example/diameter/launcher/deployments/basic/hex_examples/aar.hex b/example/diameter/launcher/resources/hex_examples/aar.hex similarity index 100% rename from example/diameter/launcher/deployments/basic/hex_examples/aar.hex rename to example/diameter/launcher/resources/hex_examples/aar.hex diff --git a/example/diameter/launcher/deployments/basic/hex_examples/aar2-bad.hex b/example/diameter/launcher/resources/hex_examples/aar2-bad.hex similarity index 100% rename from example/diameter/launcher/deployments/basic/hex_examples/aar2-bad.hex rename to example/diameter/launcher/resources/hex_examples/aar2-bad.hex diff --git a/example/diameter/launcher/deployments/basic/hex_examples/aar3-bad.hex b/example/diameter/launcher/resources/hex_examples/aar3-bad.hex similarity index 100% rename from example/diameter/launcher/deployments/basic/hex_examples/aar3-bad.hex rename to example/diameter/launcher/resources/hex_examples/aar3-bad.hex diff --git a/example/diameter/launcher/deployments/basic/hex_examples/readme.txt b/example/diameter/launcher/resources/hex_examples/readme.txt similarity index 100% rename from example/diameter/launcher/deployments/basic/hex_examples/readme.txt rename to example/diameter/launcher/resources/hex_examples/readme.txt diff --git a/example/diameter/launcher/deployments/basic/hex_examples/tspCCA.hex b/example/diameter/launcher/resources/hex_examples/tspCCA.hex similarity index 100% rename from example/diameter/launcher/deployments/basic/hex_examples/tspCCA.hex rename to example/diameter/launcher/resources/hex_examples/tspCCA.hex diff --git a/example/diameter/launcher/deployments/basic/hex_examples/tspCCR.hex b/example/diameter/launcher/resources/hex_examples/tspCCR.hex similarity index 100% rename from example/diameter/launcher/deployments/basic/hex_examples/tspCCR.hex rename to example/diameter/launcher/resources/hex_examples/tspCCR.hex diff --git a/example/diameter/launcher/resources/scripts/operation_curl.sh b/example/diameter/launcher/resources/scripts/operation_curl.sh new file mode 100755 index 0000000..5cff3c7 --- /dev/null +++ b/example/diameter/launcher/resources/scripts/operation_curl.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +############# +# FUNCTIONS # +############# +_exit () { + echo -e "\n$1\n" + exit 1 +} + +############# +# EXECUTION # +############# +cd `dirname $0` +echo +# Get the HTTP Server: +[ ! -f .httpServer ] && _exit "Can't found '`pwd`/.httpServer' to use with curl tool." +SERVER=`cat .httpServer` + +# Send operation: +[ "$1" = "" ] && _exit "Usage: $0 ; i.e.: $0 help" +#> curl_log.txt +#TRACE="--trace-ascii curl_log.txt" +curl -m 1 --data "$1" $SERVER + diff --git a/example/diameter/launcher/resources/scripts/operation_signal.sh b/example/diameter/launcher/resources/scripts/operation_signal.sh new file mode 100755 index 0000000..c462a7e --- /dev/null +++ b/example/diameter/launcher/resources/scripts/operation_signal.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +############# +# FUNCTIONS # +############# +_exit () { + echo -e "\n$1\n" + exit 1 +} + +############# +# EXECUTION # +############# +cd `dirname $0` +echo +# Get the PID: +[ ! -f .pid ] && _exit "Can't found '`pwd`/.pid'.\nTry to pgrep your process name and dump pid to that file." +PID=`cat .pid` + +# Send operation: +[ "$1" = "" ] && _exit "Usage: $0 ; i.e.: $0 help" +echo $1 > sigusr2.tasks.input +kill -s SIGUSR2 $PID + +sleep 1 +echo +echo +echo "You could see results on '`pwd`/sigusr2.tasks.output' file." +echo +echo + diff --git a/example/diameter/launcher/resources/scripts/operation_tps.sh b/example/diameter/launcher/resources/scripts/operation_tps.sh new file mode 100755 index 0000000..29f41eb --- /dev/null +++ b/example/diameter/launcher/resources/scripts/operation_tps.sh @@ -0,0 +1,66 @@ +#!/bin/bash + echo " sendXS [amount: default -1 (no limit)]:" + echo " send messages from burst list with the TPS provided. User could hot change" + echo " speed by mean 'echo > .tps' at another shell. You can stop the load" + echo " removing that hidden file or using CTRL+C from the shell where you launched" + echo " the burst command. Real tps is dumped on '.real_tps' file during testing." + echo " You could limit the amount of messages sent by mean the second parameter." + echo " No limit is established by default (-1 or negative value)." +> curl_log.txt +TRACE="--trace-ascii curl_log.txt" +SERVER=`cat .httpServer` + +use () { + + echo "Use: $0 [2c]" + echo + echo "Sends 'xml_file' to the diameter server or to the client when '2c' parameter is provided." + echo + exit +} + + sendXS) [[ "$2" = "" ]] && uso + limit=$3 + [[ "$limit" = "" ]] && limit=-1 + entero $2 + entero $limit + TPS=0 + count=0 + amount=1 + echo $2 > .tps + while test -f .tps + do + [[ "$count" = "$limit" ]] && break + BEFORE_ns=`date +%s%N` + READ_TPS=`cat .tps` + # Hot change could make .tps still unavailable: + [[ "$READ_TPS" = "" ]] && READ_TPS=$TPS + # Volvemos a calcular medias (REAL_TPS) cada 10 segundos o cuando cambia el TPS en caliente: + [[ "$READ_TPS" != "$TPS" ]] && { BEGIN_ns=$BEFORE_ns ; count=0 ; } + [[ $count = $((10*TPS)) ]] && { BEGIN_ns=$BEFORE_ns ; count=0 ; } + TPS=$READ_TPS + [[ "$TPS" = "0" ]] && salir "Test stopped due to 0-tps value read" + # Background: + _curl "burst|send|$amount" & + count=$((count+amount)) + AFTER_ns=`date +%s%N` + # Real tps: + REAL_TPS=$(calc "1000000000 * $count / ($AFTER_ns - $BEGIN_ns)") + echo $REAL_TPS > .real_tps + + COEF=1 + [[ $(calc "$TPS > $REAL_TPS") = "1" ]] && COEF=$(calc "$REAL_TPS / $TPS") + K=$(calc "$COEF ^ 10") + amount=$(calc "scale=0;1/$K") + usleep $(calc "$K * 1000000/$TPS") + done + ;; +echo +[[ "$1" = "" ]] && use +[[ ! -f "$1" ]] && { echo "ERROR: file '$1' not found" ; echo; echo; exit ; } +echo +operation="sendxml|$1" +[[ "$2" = "2c" ]] && operation="sendxml2c|$1" + +curl -m 1 --data "$operation" $TRACE ${SERVER} + diff --git a/example/diameter/launcher/resources/pcap2diameterHex.sh b/example/diameter/launcher/resources/scripts/pcap2diameterHex.sh similarity index 100% rename from example/diameter/launcher/resources/pcap2diameterHex.sh rename to example/diameter/launcher/resources/scripts/pcap2diameterHex.sh diff --git a/example/diameter/launcher/deployments/basic/xml_examples/aaa.xml b/example/diameter/launcher/resources/xml_examples/aaa.xml similarity index 100% rename from example/diameter/launcher/deployments/basic/xml_examples/aaa.xml rename to example/diameter/launcher/resources/xml_examples/aaa.xml diff --git a/example/diameter/launcher/deployments/basic/xml_examples/aar.xml b/example/diameter/launcher/resources/xml_examples/aar.xml similarity index 100% rename from example/diameter/launcher/deployments/basic/xml_examples/aar.xml rename to example/diameter/launcher/resources/xml_examples/aar.xml diff --git a/example/diameter/launcher/deployments/basic/xml_examples/aar2.xml b/example/diameter/launcher/resources/xml_examples/aar2.xml similarity index 100% rename from example/diameter/launcher/deployments/basic/xml_examples/aar2.xml rename to example/diameter/launcher/resources/xml_examples/aar2.xml diff --git a/example/diameter/launcher/deployments/basic/xml_examples/ccr.xml b/example/diameter/launcher/resources/xml_examples/ccr.xml similarity index 100% rename from example/diameter/launcher/deployments/basic/xml_examples/ccr.xml rename to example/diameter/launcher/resources/xml_examples/ccr.xml diff --git a/include/anna/diameter/codec/EngineImpl.hpp b/include/anna/diameter/codec/EngineImpl.hpp index bb7c6f5..1884083 100644 --- a/include/anna/diameter/codec/EngineImpl.hpp +++ b/include/anna/diameter/codec/EngineImpl.hpp @@ -178,6 +178,7 @@ private: anna::xml::DTDMemory a_dtd; ValidationDepth::_v a_validationDepth; ValidationMode::_v a_validationMode; + bool a_singleFailedAVP; bool a_ignoreFlags; FixMode::_v a_fixMode; bool a_selectStackWithApplicationId; // default behaviour: let the user switch the stack (false for this boolean) @@ -322,7 +323,20 @@ public: */ FixMode::_v getFixMode() const throw() { return a_fixMode; } + /** + * Sets single FailedAVP. True by default. If false, and more than one wrong avp are found during message + * decoding and or validation, a new Failed-AVP will be added to the dynamic answer provided. The standard + * talks about only one but it is open to do this. + * + * \param single Single Failed-AVP boolean. + */ + void setSingleFailedAVP(bool single = true) throw() { a_singleFailedAVP = single; } + /** + * Returns single Failed-AVP boolean. + * \return Failed-AVP could be one (true) or more (false) in answer message. + */ + bool getSingleFailedAVP() const throw() { return a_singleFailedAVP; } /** DTD document for xml message parsing diff --git a/source/diameter/codec/EngineImpl.cpp b/source/diameter/codec/EngineImpl.cpp index 5d24eb9..c25bec0 100644 --- a/source/diameter/codec/EngineImpl.cpp +++ b/source/diameter/codec/EngineImpl.cpp @@ -116,6 +116,7 @@ EngineImpl::EngineImpl(const char* className) : a_dictionary(NULL), a_validationDepth(ValidationDepth::FirstError), a_validationMode(ValidationMode::AfterDecoding), + a_singleFailedAVP(true), a_ignoreFlags(false), a_selectStackWithApplicationId(false), a_fixMode(FixMode::BeforeEncoding) { @@ -191,6 +192,8 @@ std::string EngineImpl::asString(void) const throw() { result += asText(a_validationDepth); result += "\nValidationMode: "; result += asText(a_validationMode); + result += "\nSingle Failed-AVP: "; + result += a_singleFailedAVP ? "yes" : "no"; result += "\nIgnore flags: "; result += a_ignoreFlags ? "yes" : "no"; result += "\nFixMode: "; @@ -209,6 +212,7 @@ throw() { anna::xml::Node* result = parent->createChild("diameter.codec.EngineImpl"); result->createAttribute("ValidationDepth", asText(a_validationDepth)); result->createAttribute("ValidationMode", asText(a_validationMode)); + result->createAttribute("SingleFailedAVP", a_singleFailedAVP ? "yes" : "no"); result->createAttribute("IgnoreFlags", a_ignoreFlags ? "yes" : "no"); result->createAttribute("FixMode", asText(a_fixMode)); anna::xml::Node* dictionary = result->createChild("EngineImpl.ActivatedDictionary"); diff --git a/source/diameter/codec/Message.cpp b/source/diameter/codec/Message.cpp index b9c4fbd..35db129 100644 --- a/source/diameter/codec/Message.cpp +++ b/source/diameter/codec/Message.cpp @@ -471,11 +471,13 @@ void Message::setFailedAvp(const parent_t &parent, AvpId wrong, const char *wron // most of the command codes as *[Failed-AVP], i think this is not a deliberate ambiguity. // Probably the RFC wants to give freedom to the application layer, but it is recommended to // have only one child (wrong avp) inside a unique message Failed-AVP to ease the Result-Code - // correspondence. Anyway, this behaviour could be easily opened commenting condition block (*). + // correspondence. Anyway, this behaviour could be easily opened by mean 'setSingleFailedAVP(false)' Avp *theFailedAvp = getAvp(helpers::base::AVPID__Failed_AVP, 1, anna::Exception::Mode::Ignore); if (theFailedAvp) { - LOGDEBUG(anna::Logger::debug("Failed-AVP has already been added. RFC 6733 Section 7.5 recommends to store only the first error found", ANNA_FILE_LOCATION)); - return; + if (getEngine()->getSingleFailedAVP()) { + LOGDEBUG(anna::Logger::debug("Failed-AVP has already been added. RFC 6733 Section 7.5 recommends to store only the first error found", ANNA_FILE_LOCATION)); + return; + } } // Section 7.5 RFC 6733: A Diameter message SHOULD contain one Failed-AVP AVP -- 2.20.1