From: Eduardo Ramos Testillano (eramedu) Date: Mon, 27 Apr 2020 00:13:57 +0000 (+0200) Subject: Add deployment for ADML agent with http interface X-Git-Url: https://git.teslayout.com/public/public/public/?p=anna.git;a=commitdiff_plain;h=2f2cc741b21502b2bd37060a1059084a54cb8b6e Add deployment for ADML agent with http interface Reuse ADML agent for aots-setup through symlink. Centralize stacks with stacks examples. Some modifications needed in legacy scripts to reference new dictionary names. Eval is needed in new start script in order to integrate the httpServer calculation. * Upgrade compiler to C++14 (to use std::quoted from iomanip) * Improve HTTP handler to answer a json body with result (true/false) and data (description for the operation): { "result": "true", "data": "Success" } * Fix core dump due to uninitialized 'sessionBasedModelsTypeEnum' * New 'state' operation to now the state in real time for a test id. --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 0032d57..e35da4b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,7 +70,7 @@ message(STATUS "CMAKE_CXX_COMPILER_ID = ${CMAKE_CXX_COMPILER_ID}") if(CMAKE_CXX_COMPILER_ID MATCHES GNU) #execute_process(COMMAND g++ --version >/dev/null 2>/dev/null) set(CMAKE_CXX_COMPILER "/usr/bin/g++") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wno-deprecated -Wwrite-strings -Wno-unknown-pragmas -Wno-sign-compare -Wno-maybe-uninitialized -Wno-unused -Wno-reorder") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -Wno-deprecated -Wwrite-strings -Wno-unknown-pragmas -Wno-sign-compare -Wno-maybe-uninitialized -Wno-unused -Wno-reorder") set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3") set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG") set(CMAKE_CXX_FLAGS_RELEASE "-O3") @@ -80,7 +80,7 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES Clang) #execute_process(COMMAND clang++ --version >/dev/null 2>/dev/null) add_definitions(-DIS_CLANG) set(CMAKE_CXX_COMPILER "/usr/bin/clang++") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wno-deprecated -Wwrite-strings -Wno-unknown-pragmas -Wno-sign-compare -Wno-maybe-uninitialized -Wno-unused -Wno-reorder -Wno-parentheses-equality") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -Wno-deprecated -Wwrite-strings -Wno-unknown-pragmas -Wno-sign-compare -Wno-maybe-uninitialized -Wno-unused -Wno-reorder -Wno-parentheses-equality") set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3") set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG") set(CMAKE_CXX_FLAGS_RELEASE "-O3") diff --git a/example/diameter/launcher/Launcher.cpp b/example/diameter/launcher/Launcher.cpp index 2175263..420a498 100644 --- a/example/diameter/launcher/Launcher.cpp +++ b/example/diameter/launcher/Launcher.cpp @@ -330,7 +330,7 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode, bool eventOp // Checking command line parameters std::string sessionBasedModelsType; - anna::diameter::comm::Entity::SessionBasedModelsType::_v sessionBasedModelsTypeEnum; + anna::diameter::comm::Entity::SessionBasedModelsType::_v sessionBasedModelsTypeEnum = anna::diameter::comm::Entity::SessionBasedModelsType::SessionIdLowPart; if(sessionBasedModelsClientSocketSelection) { sessionBasedModelsType = sessionBasedModelsClientSocketSelection->getValue(); if (sessionBasedModelsType == "RoundRobin") { @@ -1463,6 +1463,8 @@ std::string Launcher::help() const throw() { result += "\n Test cases reports are not dumped on process context (too many information in general)."; result += "\n The report contains context information in every moment: this operation acts as a snapshot."; result += "\n"; + result += "\n test|state[|id] Get test case state for id provided, current 'in-process' test case when missing."; + result += "\n"; result += "\n test|interact|amount[|id] Makes interactive a specific test case id. The amount is the margin of execution steps"; result += "\n to be done. Normally, we will execute 'test|interact|0[|]', which means that"; result += "\n the test case is selected to be interactive, but no step is executed. Then you have to"; @@ -1567,7 +1569,7 @@ std::string Launcher::help() const throw() { result += "\n"; result += "\nThe alternative using SIGUSR2 signal requires the creation of the task(s) file which will be read at"; result += "\n signal event:"; - result += "\n echo \"<\" > "; result += getSignalUSR2InputFile(); + result += "\n echo \"\" > "; result += getSignalUSR2InputFile(); result += "\n then"; result += "\n kill -12 "; result += "\n or"; @@ -1619,9 +1621,12 @@ void Launcher::logStatisticsSamples(const std::string &conceptsList) throw() { } -void Launcher::eventOperation(const std::string &operation, std::string &response_content) throw(anna::RuntimeException) { +bool Launcher::eventOperation(const std::string &operation, std::string &response_content) throw(anna::RuntimeException) { + + bool result = true; + LOGMETHOD(anna::TraceMethod tm("Launcher", "eventOperation", ANNA_FILE_LOCATION)); - if (operation == "") return; // ignore + if (operation == "") return result; // ignore LOGDEBUG(anna::Logger::debug(anna::functions::asString("Operation: %s", operation.c_str()), ANNA_FILE_LOCATION)); // Default response: @@ -1651,15 +1656,15 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons catch(anna::RuntimeException &ex) { ex.trace(); response_content = ex.asString(); - return; + return false; } - return; + return true; // OK } // Help: if(operation == "help") { response_content = help(); - return; + return true; // OK } // Reset performance data: @@ -1667,26 +1672,26 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons resetCounters(); resetStatistics(); response_content = "All process counters & statistic information have been reset"; - return; + return true; // OK } // Counters dump on demand: if(operation == "forceCountersRecord") { forceCountersRecord(); response_content = "Current counters have been dump to disk"; - return; + return true; // OK } // OAM & statistics: if(operation == "show-oam") { anna::xml::Node root("root"); response_content = anna::xml::Compiler().apply(oamAsXML(&root)); - return; + return true; // OK } if(operation == "show-stats") { anna::xml::Node root("root"); response_content = anna::xml::Compiler().apply(statsAsXML(&root)); - return; + return true; // OK } /////////////////////////////////////////////////////////////////// @@ -1761,25 +1766,27 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons std::string contextFile = ((numParams == 1) ? param1 : anna::functions::asString("/var/tmp/anna.context.%05d", getPid())); writeContext(contextFile); response_content = anna::functions::asString("Context dumped on file '%s'", contextFile.c_str()); - return; + return true; // OK } if(opType == "log-statistics-samples") { logStatisticsSamples(param1); response_content = anna::functions::asString("Log statistics samples for '%s' concepts", param1.c_str()); - return; + return true; // OK } // Change execution directory: if(opType == "change-dir") { if (param1 == "") param1 = a_initialWorkingDirectory; - if (chdir(param1.c_str()) == 0) + result = (chdir(param1.c_str()) == 0); + + if (result) response_content = "New execution directory configured: "; else response_content = "Cannot assign provided execution directory: "; response_content += param1; - return; + return result; } if(opType == "services") { @@ -1790,10 +1797,10 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons catch(anna::RuntimeException &ex) { ex.trace(); response_content = anna::functions::asString("Loaded services from file '%s' with errors", servicesFile.c_str()); - return; + return false; } response_content = anna::functions::asString("Loaded services from file '%s'", servicesFile.c_str()); - return; + return true; // OK } // Host switch: @@ -1810,12 +1817,12 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons response_content = "Working node is automatic"; } } - return; + return true; // OK } if(opType == "node_auto") { a_workingNode = NULL; response_content = "Working node has been set to automatic"; - return; + return true; // OK } // Operated host from possible forced-working node: @@ -2159,9 +2166,33 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons if (testCase) { response_content = testCase->asXMLString(); - return; + return true; // OK + } + else { + result = false; + if (id == -1) { + opt_response_content = "no current test case detected (testing started ?)"; + } + else { + opt_response_content = "cannot found test id ("; + opt_response_content += anna::functions::asString(id); + opt_response_content += ")"; + } + } + } + else if(param1 == "state") { + if (numParams > 2) + throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION); + + int id = ((param2 != "") ? atoi(param2.c_str()) : -1); + anna::testing::TestCase *testCase = testManager.findTestCase(id); + + if (testCase) { + response_content = anna::testing::TestCase::asText(testCase->getState()); + return testCase->isSuccess(); } else { + result = false; if (id == -1) { opt_response_content = "no current test case detected (testing started ?)"; } @@ -2198,6 +2229,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons opt_response_content += anna::functions::asString(id); } else { + result = false; opt_response_content = "cannot found test id ("; opt_response_content += anna::functions::asString(id); opt_response_content += ")"; @@ -2228,6 +2260,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons opt_response_content = param2; opt_response_content += " reset have been sent to all programmed tests: "; opt_response_content += anyReset ? "some/all have been reset" : "nothing was reset"; } else { + result = false; opt_response_content = "cannot found test id ("; opt_response_content += anna::functions::asString(id); opt_response_content += ")"; @@ -2269,19 +2302,19 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons } else if(param1 == "junit") { response_content = testManager.junitAsXMLString(); - return; + return true; // OK } else if(param1 == "summary-counts") { response_content = testManager.summaryCounts(); - return; + return true; // OK } else if(param1 == "summary-states") { response_content = testManager.summaryStates(); - return; + return true; // OK } else if(param1 == "summary") { response_content = testManager.asXMLString(); - return; + return true; // OK } else { int id = atoi(param1.c_str()); @@ -2488,7 +2521,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons } else if(opType == "loadxml") { codecMsg.loadXML(param1); response_content = codecMsg.asXMLString(); - return; + return true; // OK } else if(opType == "diameterServerSessions") { int diameterServerSessions = atoi(param1.c_str()); getOperatedServer()->setMaxConnections(diameterServerSessions); @@ -2496,7 +2529,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons } else if(opType == "answerxml2c") { if(param1 == "") { // programmed answers FIFO's to stdout response_content = getOperatedServer()->getReactingAnswers()->asString("ANSWERS TO CLIENT"); - return; + return true; // OK } else if (param1 == "rotate") { getOperatedServer()->getReactingAnswers()->rotate(true); } else if (param1 == "exhaust") { @@ -2522,7 +2555,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons if(param1 == "") { // programmed answers FIFO's to stdout response_content = getOperatedEntity()->getReactingAnswers()->asString("ANSWERS TO ENTITY"); - return; + return true; // OK } else if (param1 == "rotate") { getOperatedEntity()->getReactingAnswers()->rotate(true); } else if (param1 == "exhaust") { @@ -2554,6 +2587,8 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons response_content += " => "; response_content += opt_response_content; } + + return result; } anna::xml::Node* Launcher::asXML(anna::xml::Node* parent) const diff --git a/example/diameter/launcher/Launcher.hpp b/example/diameter/launcher/Launcher.hpp index c97fe7b..a74ef39 100644 --- a/example/diameter/launcher/Launcher.hpp +++ b/example/diameter/launcher/Launcher.hpp @@ -104,7 +104,7 @@ public: MyCommunicator *getCommunicator() throw() { return a_communicator; } - void eventOperation(const std::string &, std::string &) throw(anna::RuntimeException); + bool eventOperation(const std::string &, std::string &) throw(anna::RuntimeException); // returns success/failed void forceCountersRecord() throw(anna::RuntimeException) { if (a_counterRecorderClock) a_counterRecorderClock->tick(); } void logStatisticsSamples(const std::string &conceptsList) throw(); diff --git a/example/diameter/launcher/MyHandler.cpp b/example/diameter/launcher/MyHandler.cpp index 9f893a7..9ac1c44 100644 --- a/example/diameter/launcher/MyHandler.cpp +++ b/example/diameter/launcher/MyHandler.cpp @@ -8,6 +8,8 @@ // Standard #include +#include +#include // Project #include @@ -34,18 +36,20 @@ throw(anna::RuntimeException) { // Operation: std::string response_content; + bool opOk{}; + try { Launcher& my_app = static_cast (anna::app::functions::getApp()); - my_app.eventOperation(body_content, response_content); + opOk = my_app.eventOperation(body_content, response_content); } catch(RuntimeException &ex) { ex.trace(); + opOk = false; } anna::http::Response* response = allocateResponse(); response->setStatusCode(200); // http://en.wikipedia.org/wiki/List_of_HTTP_status_codes - anna::DataBlock db_content(true); - db_content = response_content; - response->setBody(db_content); + +// EXAMPLES TO SET HEADERS: // response->find(anna::http::Header::Type::Date)->setValue("Mon, 30 Jan 2006 14:36:18 GMT"); // anna::http::Header* keepAlive = response->find("Keep-Alive"); // @@ -54,6 +58,20 @@ throw(anna::RuntimeException) { // // keepAlive->setValue("Verificacion del cambio 1.0.7"); + // Content-Type header: + anna::http::Header* contentType = response->find("Content-Type"); + if (contentType == NULL) + contentType = response->createHeader("Content-Type"); + contentType->setValue("application/json"); + + // Json Body for response: + std::stringstream ss; + + ss << R"({ "result":")" << (opOk ? "true":"false") << R"(", "data": )" << std::quoted(response_content) << R"( })"; + anna::DataBlock db_content(true); + db_content = ss.str(); + response->setBody(db_content); + try { clientSocket.send(*response); } catch(Exception& ex) { diff --git a/example/diameter/launcher/deploy-aots-adml.sh b/example/diameter/launcher/deploy-aots-adml.sh new file mode 100755 index 0000000..4dc5aa6 --- /dev/null +++ b/example/diameter/launcher/deploy-aots-adml.sh @@ -0,0 +1,116 @@ +#!/bin/bash + +############# +# VARIABLES # +############# +SCR_DIR=`readlink -f $0 | xargs dirname` +PROJECT_ROOT=$(readlink -f $SCR_DIR/../../..) + +############# +# FUNCTIONS # +############# +_exit () { + local msg="$1" + local rc=$2 + [ -z "$rc" ] && rc=1 + + # Exit message: + [ -n "$msg" ] && echo -e "\n${msg}\n" + + exit $rc +} + +############# +# EXECUTION # +############# + +echo +echo "------------------------------------------------------" +echo " AOTS ADML Agent installation " +echo "------------------------------------------------------" +echo +[ -d $PROJECT_ROOT/build/Release ] && VARIANT=Release +[ -d $PROJECT_ROOT/build/Debug ] && VARIANT=Debug +[ -z "$VARIANT" ] && _exit "Cannot locate neither 'Release' nor 'Debug' variant !" +build_type_letter=$(echo $VARIANT | cut -c1 | tr '[:upper:]' '[:lower:]') + +version__dflt=v"`date +'%y'`.`date +'%m'`.`date +'%d'`$build_type_letter" +INSTALL__dflt=$HOME/3rdParty/anna-aots-adml-builds/v"`date +'%y'`.`date +'%m'`.`date +'%d'`$build_type_letter" + +echo "Choose the target path for installation [$INSTALL__dflt]:" +echo " (enter a non-existent directory)" +read INSTALL +[ -z "$INSTALL" ] && INSTALL=$INSTALL__dflt +INSTALL=`readlink -m $INSTALL` +[ -d $INSTALL ] && _exit "The target installation directory ($INSTALL) already exists ! (if you want to reinstall, remove it first)" + +echo +echo "Stage 1: Deploying resources ......................" +echo +mkdir -p $INSTALL +cp -rL $SCR_DIR/deployments/aots-adml/* $INSTALL + +# VARIABLES ###################################### +# Sources: +LDIR=${PROJECT_ROOT}/example/diameter/launcher +BIN_DIR=${PROJECT_ROOT}/build/$VARIANT/bin +LIB_DIR=${PROJECT_ROOT}/build/$VARIANT/lib + +# Targets: +ADML=${INSTALL} +DTDs=${ADML}/DTDs +DYNLIBS=${ADML}/dynlibs +################################################## + +echo +echo "Variant: $VARIANT" +echo + +# Empty directories: +mkdir -p ${ADML}/counters +mkdir -p ${ADML}/test-reports + +# Scripts: +cp ${PROJECT_ROOT}/example/diameter/launcher/resources/scripts/operation_curl.sh ${ADML}/operation.sh + +# Templates: +mkdir $DTDs +cp ${PROJECT_ROOT}/include/anna/diameter/codec/message.dtd ${DTDs} +cp ${PROJECT_ROOT}/include/anna/diameter/stack/dictionary.dtd ${DTDs} +cp ${PROJECT_ROOT}/example/diameter/launcher/resources/services_examples/services.dtd ${DTDs} + +# Main Launcher and dynamic libraries: +cp ${BIN_DIR}/anna_diameter_launcher ${ADML}/ADML +cp -r $LIB_DIR/dynamic/launcher ${DYNLIBS} +cp $LIB_DIR/libanna_testing_shared.so ${DYNLIBS} + +# Get stuff from leaf directories: +cd $LIB_DIR/dynamic/launcher +leafs=( $(find . -type d -links 2) ) +cd - >/dev/null + +cd ${PROJECT_ROOT}/dynamic/launcher +for dir in ${leafs[@]} +do + cp $dir/*.xml ${DYNLIBS}/$dir 2>/dev/null + cp $dir/dynamic.suffix ${DYNLIBS}/$dir 2>/dev/null + cp -r $dir/services ${DYNLIBS}/$dir 2>/dev/null +done +cd - >/dev/null + +# ADML dynamic libs selection script: +cp ${LDIR}/resources/scripts/select_dynlib.sh ${DYNLIBS}/select.sh + +# Default dynamic library: +cd ${DYNLIBS} +ln -sf default/libanna_launcher_procedure_default_shared.so +cd - >/dev/null + +# ADML Agent shall use http server: +httpOpt="--httpServer \`grep -v ^# .httpServer\`" +echo "${httpOpt}" >> ${ADML}/args.ft +echo "${httpOpt}" >> ${ADML}/args.st +echo "localhost:8000" > ${ADML}/.httpServer + +_exit "Done!" 0 + diff --git a/example/diameter/launcher/deploy-aots.sh b/example/diameter/launcher/deploy-aots.sh index 8a7f8b7..1e7f471 100755 --- a/example/diameter/launcher/deploy-aots.sh +++ b/example/diameter/launcher/deploy-aots.sh @@ -35,20 +35,20 @@ echo build_type_letter=$(echo $VARIANT | cut -c1 | tr '[:upper:]' '[:lower:]') version__dflt=v"`date +'%y'`.`date +'%m'`.`date +'%d'`$build_type_letter" -INSTALL_AOTS__dflt=$HOME/3rdParty/anna-aots-builds/v"`date +'%y'`.`date +'%m'`.`date +'%d'`$build_type_letter" +INSTALL__dflt=$HOME/3rdParty/anna-aots-builds/v"`date +'%y'`.`date +'%m'`.`date +'%d'`$build_type_letter" -echo "Choose the target path for installation [$INSTALL_AOTS__dflt]:" +echo "Choose the target path for installation [$INSTALL__dflt]:" echo " (enter a non-existent directory)" -read INSTALL_AOTS -[ -z "$INSTALL_AOTS" ] && INSTALL_AOTS=$INSTALL_AOTS__dflt -INSTALL_AOTS=`readlink -m $INSTALL_AOTS` -[ -d $INSTALL_AOTS ] && _exit "The target installation directory ($INSTALL_AOTS) already exists ! (if you want to reinstall, remove it first)" +read INSTALL +[ -z "$INSTALL" ] && INSTALL=$INSTALL__dflt +INSTALL=`readlink -m $INSTALL` +[ -d $INSTALL ] && _exit "The target installation directory ($INSTALL) already exists ! (if you want to reinstall, remove it first)" echo echo "Stage 1: Deploying resources ......................" echo -mkdir -p $INSTALL_AOTS -cp -r $SCR_DIR/deployments/aots/* $INSTALL_AOTS +mkdir -p $INSTALL +cp -rL $SCR_DIR/deployments/aots-setup/* $INSTALL # VARIABLES ###################################### # Sources: @@ -57,7 +57,7 @@ BIN_DIR=${PROJECT_ROOT}/build/$VARIANT/bin LIB_DIR=${PROJECT_ROOT}/build/$VARIANT/lib # Targets: -ADML=${INSTALL_AOTS}/agents/ADML +ADML=${INSTALL}/agents/ADML DTDs=${ADML}/DTDs DYNLIBS=${ADML}/dynlibs ################################################## diff --git a/example/diameter/launcher/deploy-setups.sh b/example/diameter/launcher/deploy-setups.sh index bdc5bd9..3c8b47b 100755 --- a/example/diameter/launcher/deploy-setups.sh +++ b/example/diameter/launcher/deploy-setups.sh @@ -109,12 +109,12 @@ case $type in echo s | ./configure.sh >/dev/null sed -i 's/3868/3869/' services.xml # Gx dictionary: - ln -sf stack_examples/other/16777238.xml dictionary.xml + ln -sf stack_examples/other/DictionaryGx.16777238.xml dictionary.xml cd - >/dev/null cd $target/ADML-serverRx echo s | ./configure.sh >/dev/null # Rx dictionary: - ln -sf stack_examples/other/16777236.xml dictionary.xml + ln -sf stack_examples/other/DictionaryRx.16777236.xml dictionary.xml cd - >/dev/null ;; diff --git a/example/diameter/launcher/deployments/aots-adml/ACTIONS.md b/example/diameter/launcher/deployments/aots-adml/ACTIONS.md new file mode 100644 index 0000000..280b214 --- /dev/null +++ b/example/diameter/launcher/deployments/aots-adml/ACTIONS.md @@ -0,0 +1,84 @@ +# ADML actions + +## send_xml_to_entity + +Sends a diameter message in xml format (see DTDs/message.dtd) as client to the server(s) configured. + +Arguments: xml[, answers_to] +* xml: the diameter message in xml format. +* answers_to: optional step number of a 'wait_xml_from_entity' for a request message. + Steps are the actions numbered from 1 to N +Example(1): +``` +- action: AF/send_xml_to_entity + xml: aar-initial.xml +``` + +Example(2): +``` +- action: AF/wait_xml_from_entity + arguments: + xml: dpr.xml + +- action: AF/send_xml_to_entity + arguments: + xml: dpa.xml + answers_to: 1 +``` + +## wait_xml_from_entity + +Waits a diameter message in xml format (see DTDs/message.dtd) as client from the server(s) connected. +Step blocks until match reception (regular expressions could be used). + +Arguments: xml +* xml: the diameter message in xml format. + +Example: +``` +- action: AFPC/wait_xml_from_entity + arguments: + xml: aaa.xml +``` + +## send_xml_to_client + +Sends a diameter message in xml format (see DTDs/message.dtd) as server to any client(s) connected. + +Arguments: xml[, answers_to] +* xml: the diameter message in xml format. +* answers_to: optional step number of a 'wait_xml_from_client' for a request message. + Steps are the actions numbered from 1 to N +Example(1): +``` +- action: AF/send_xml_to_client + arguments: + xml: dpr.xml +``` + +Example(2): +``` +- action: AFPC/wait_xml_from_client + xml: dpr.xml + arguments: + +- action: AFPC/send_xml_to_client + xml: dpa.xml + arguments: +``` + +## wait_xml_from_client + +Waits a diameter message in xml format (see DTDs/message.dtd) as server from the client(s) connected. +Step blocks until match reception (regular expressions could be used). + +Arguments: xml +* xml: the diameter message in xml format. + +Example: +``` +- action: AFPC/wait_xml_from_client + arguments: + xml: dpr.xml +``` + diff --git a/example/diameter/launcher/deployments/aots-adml/HINTS.md b/example/diameter/launcher/deployments/aots-adml/HINTS.md new file mode 100644 index 0000000..a9314e3 --- /dev/null +++ b/example/diameter/launcher/deployments/aots-adml/HINTS.md @@ -0,0 +1,28 @@ +# ADML hints + +## AF-Application-Identifier + +Diameter RFC assigned the format 'OctetString' to this AVP although it is almost always intended to be human-readable. 'UTF8String' format could be more appropiate, but probably the intention was to avoid possible limits. + +ADML message codec shows readable data when possible using the field 'data'. When this is not possible, the 'hex-data' field will encode the information. The 'OctetString' format is hexadecimal by nature (string of octets), then always 'hex-data' will be shown for this kind of AVPs. + +For example: + + +You could decode using hex dump utility: 'xxd' + +$> echo "75726e25334175726e2d787878253341336770702d736572766963652e696d732e696373692e6d6d74656c" | xxd -p -r +$> urn%3Aurn-xxx%3A3gpp-service.ims.icsi.mmtel + +But the thing that you may probably need, is to encode a desired value into xml messages while designing a test case. For this situation, use the opossite (drop reverse flag '-r' to xxd): + +$> echo -n "urn%3Aurn-xxx%3A3gpp-service.ims.icsi.mmtel" | xxd -p | tr -d '\n' +$> 75726e25334175726e2d787878253341336770702d736572766963652e696d732e696373692e6d6d74656c + +or use 'od' (files dump) utility: + +$> echo -n "urn%3Aurn-xxx%3A3gpp-service.ims.icsi.mmtel" | od -A n -t x1 | tr -d ' ' | tr -d '\n' +$> 75726e25334175726e2d787878253341336770702d736572766963652e696d732e696373692e6d6d74656c + +The '-n' in the echo is neccessary to avoid encoding of carriage return (0x0a) for the string provided. + diff --git a/example/diameter/launcher/deployments/aots-adml/args.ft b/example/diameter/launcher/deployments/aots-adml/args.ft new file mode 100644 index 0000000..fa3411d --- /dev/null +++ b/example/diameter/launcher/deployments/aots-adml/args.ft @@ -0,0 +1,21 @@ +# ADML will start with base protocol dictionary and no nodes on services.xml: +--services services.xml + +# Counters directory: +--cntDir counters +# Counters record procedure period in milliseconds. +# If missing, default value of 300000 (5 minutes) will be assigned. +# Zero value, disable the dump procedure. +#--cntRecordPeriod 10000 + +# System test: +--tmDir test-reports +# To override traffic logs configuration in order to be disabled for all the +# loaded nodes, launch the process by mean: './start.sh st' + +# Statistics: +--logStatisticSamples all + +# Add to take debug traces once started +--trace debug + diff --git a/example/diameter/launcher/deployments/aots-adml/args.st b/example/diameter/launcher/deployments/aots-adml/args.st new file mode 100644 index 0000000..733daaf --- /dev/null +++ b/example/diameter/launcher/deployments/aots-adml/args.st @@ -0,0 +1,22 @@ +# ADML will start with base protocol dictionary and no nodes on services.xml: +--services services.xml + +# Counters directory: +--cntDir counters +# Counters record procedure period in milliseconds. +# If missing, default value of 300000 (5 minutes) will be assigned. +# Zero value, disable the dump procedure. +#--cntRecordPeriod 10000 + +# System test: +--tmDir test-reports +# To override traffic logs configuration in order to be disabled for all the +# loaded nodes, launch the process by mean: './start.sh st' + +# Statistics: +--logStatisticSamples all + +# Add to take debug traces once started +# --trace debug +--disableLogs + diff --git a/example/diameter/launcher/deployments/aots-adml/services.xml.example b/example/diameter/launcher/deployments/aots-adml/services.xml.example new file mode 100644 index 0000000..a54cf09 --- /dev/null +++ b/example/diameter/launcher/deployments/aots-adml/services.xml.example @@ -0,0 +1,29 @@ + + + + + + + + diff --git a/example/diameter/launcher/deployments/aots-adml/stacks b/example/diameter/launcher/deployments/aots-adml/stacks new file mode 120000 index 0000000..b2773bc --- /dev/null +++ b/example/diameter/launcher/deployments/aots-adml/stacks @@ -0,0 +1 @@ +../../resources/stack_examples \ No newline at end of file diff --git a/example/diameter/launcher/deployments/aots-adml/start.sh b/example/diameter/launcher/deployments/aots-adml/start.sh new file mode 100755 index 0000000..d2b371e --- /dev/null +++ b/example/diameter/launcher/deployments/aots-adml/start.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# $1: args type could be 'ft' (function test) or 'st' (system test). By default: ft +function pgrep_live { + pids=$(pgrep "$1"); + [ "$pids" ] || return; + ps -o s= -o pid= $pids | sed -n 's/^[^ZT][[:space:]]\+//p'; +} +cd `dirname $0` +ARGS=args.$1 +[ -z "$1" ] && ARGS=args.ft +STARTED=`pgrep_live ADML` +[ -n "$STARTED" ] && { echo "Already started !"; echo "PID $STARTED" ; exit 1 ; } +0> launcher.trace +for file in `ls *.launcher.log 2>/dev/null`; do 0> $file; done +mkdir -p counters test-reports +rm -f counters/* test-reports/* *.csv +export LD_LIBRARY_PATH=$PWD/dynlibs +eval ./ADML `grep -v ^# $ARGS` & +echo $! > .pid +echo "Done !" + diff --git a/example/diameter/launcher/deployments/aots-adml/stop.sh b/example/diameter/launcher/deployments/aots-adml/stop.sh new file mode 100755 index 0000000..a4f1121 --- /dev/null +++ b/example/diameter/launcher/deployments/aots-adml/stop.sh @@ -0,0 +1,26 @@ +#!/bin/bash +function pgrep_live { + pids=$(pgrep "$1"); + [ "$pids" ] || return; + ps -o s= -o pid= $pids | sed -n 's/^[^ZT][[:space:]]\+//p'; +} +cd `dirname $0` +PID=$(pgrep_live ^ADML) +[ -z "$PID" ] && exit 0 +kill $PID + +# Protection to force kill (with SIGKILL) if ADML still alive after certain time: +seconds2force=3 +count=$((10*seconds2force)) +kill0=0 +while [ $kill0 -eq 0 -a $count -gt 0 ] +do + sleep 0.1 + count=$((count-1)) + kill -0 $PID 2>/dev/null + kill0=$? + echo -n . +done +[ $kill0 -eq 0 ] && { echo " sigkill to ADML pid $PID !" ; kill -9 $PID ; } +rm -f .pid + diff --git a/example/diameter/launcher/deployments/aots-setup/README.md b/example/diameter/launcher/deployments/aots-setup/README.md new file mode 100644 index 0000000..35570c1 --- /dev/null +++ b/example/diameter/launcher/deployments/aots-setup/README.md @@ -0,0 +1,495 @@ +# Anna Agents-Oriented Testing Setup + +## Agents configuration + +``` +$> python3 loader.py -h +usage: loader.py [-h] -f FILE + +Anna Agents-Oriented Testing Setup Loader + +optional arguments: + -h, --help show this help message and exit + -f FILE, --file FILE Agents yaml configuration file +``` + +The python script 'loader.py' will preload and configure all the needed agents. +Also, ADML ones will start with the configured endpoints, ready for work. + +``` +python3 loader.py --file /agents.yml +``` + +See example at './tests.example/agents.yml'. + +It is recommended to have that file within the tests directory compatible with such layout. +Anyway, you could have a common agents definition for multiple directories wherever you want to store them. +Next procedure (execution) will get a working directory which will be the parent for all the tests processed. + +Also, you could load several agents definitions. + +The format for agents file specification is: + +For KAFKA: + +``` +: + description: + template: KAFKA + topic: +``` + +For ADML (diameter): + +``` +: + description: + template: ADML + application_id: + dictionary: + type: client | server + origin_host: + origin_realm: + address: +``` + +For HTTPMOCK: + +``` +: + description: + template: HTTPMOCK + address: +``` + +Example: + +``` +AF: + description: AF Diameter Rx Node + template: ADML + application_id: 16777236 + dictionary: stacks/DictionaryRx.16777236.xml + type: client + origin_host: machine.source.server.realm.com + origin_realm: source.server.realm.com + address: 127.0.0.1:3868 + +SMPC: + description: SMPC N7 Node + template: KAFKA + topic: n7topic + +DB: + description: database + template HTTPMOCK + address: dbnode:9090 +``` + + +## Test launcher + +``` +$> python3 launcher.py -h +usage: launcher.py [-h] -t TESTS_DIR [-k] [-s] [-i] [-d] [1/708] + +Anna Agents-Oriented Testing Setup Launcher + +optional arguments: + -h, --help show this help message and exit + -t TESTS_DIR, --tests-dir TESTS_DIR + Tests parent directory where to find .yml files (from + the next directories level) + -k, --keep-list-if-exists + Keeps intact the list of test cases (/launcher.list), creates it if missing + -s, --stop-adml-at-the-end + At the end, ADML keeps running to ease debugging. You + could force stop with this option + -i, --interactive Interactive execution to ease debugging of test cases + -d, --dry-run Used to test and debug provision, no execution is + launched + -p IP_LIMIT, --ip-limit IP_LIMIT + In-Progress limit is the number of coexisting In- + Progress State test cases. Defaults to 1 (sequential), + -1 would be 'no limit'). + -r TTPS, --ttps TTPS Rate of test cases launched (test ticks per second). + By default 50 (recommended for monothread version). +``` + +The python script 'launcher.py' will execute the test cases under the provided directory. +Test cases at the directory provided itself will be ignored (searchs are performed at second level in order to avoid catching agents definitions). +Then we recommend this structure: + +``` +/agents.yml +//.../.yml +//...//.../.yml +``` + +The organization of test cases, chapters, test suites, or whatever desired concept is completely free for user preferences. Anyway, we recommend the use of numeric prefixes to control the default order of execution (alphabetical order). + +To execute the tests, just provide the global parent directory. +NO TEST CASES SHALL BE LOCATED THERE BY CONVENTION (indeed, you could have them, but they would be ignored). + +``` +python3 launcher.py --tests-dir +``` + +The procedure will look for the file ```''/launcher.list'``` in order to plan the execution. +That list will be created/refreshed always, except if you pass the option '-k|--keep-list-if-exists' (in case you want to play with edited lists). +The algorithm to create the list is based in a recursive search of yaml files in alphabetical order under the tests directory provided, in a similar way than the output of: + +``` +find -mindepth 2 -name "*.yml" | sort -t'/' +``` + +### Interactive mode + +One interesting feature from AOTS is the possibility to interact test cases even to deep level (step by step). +You could enable the interactive mode by mean: + +``` +python3 launcher.py --tests-dir --interactive +``` + +A menu will be shown after test cases programming to give you a complete control of the ADML operations: + +``` + MAIN INTERACTIVE MENU + ===================== + (prefix option with 'h' to get detailed help) + + General: + 0. Exit/Quit + + Position: + 1. Go to + 2. Look + + Test cases execution: + 3. Start with test rate + 4. Start next N test cases + 5. In-progress limit + + Low level execution: test case steps + 6. Execute next N steps + + Status & cycling: + 7. Reset + 8. Pool repeats + 9. Collect + 10. Auto reset + 11. Reports configuration +``` + +### User-defined lists + +You could edit such list and remove or comment (#) entries (each line is a test case) in order to "disable" such test cases. +You could also mess the order with something like: + +``` +sort -R /launcher.list +``` + +Don't forget to pass the option '-k|--keep-list-if-exists' or the list will be overwritten during launcher procedure execution. + +### Test cases + +Files used by parsers within the test cases (xml diameter messages, json kafka ones, etc.), are relative to the test case file path location, anyway you could set absolute paths for the stuff used. + +Recomendations: + +* Alternative 1: create a directory called ```'$(basename .yml)'``` + in order to keep order: + +``` + /tc1-initial-with-ipv4.yml + /tc1-initial-with-ipv4/AAA.xml + /tc1-initial-with-ipv4/smpc-consumed.json + /tc2-initial-with-ipv6.yml + /tc2-initial-with-ipv6/AAA.xml + /tc2-initial-with-ipv6/smpc-consumed.json + ... +``` + +* Alternative 2: create a unique directory for the definition and all the test case stuff: + +``` + /tc1/initial-with-ipv4.yml + /tc1/AAA.xml + /tc1/smpc-consumed.json + /tc2/initial-with-ipv6.yml + /tc2/AAA.xml + /tc2/smpc-consumed.json + ... +``` + +The last one seems to be better and cleaner alternative. + +### Test case definition + +A test case is a yaml list of steps. Each step is a dictionary object with mandatory keys called 'action' and 'arguments'. +An action could be generic (no agent id specified) or agent-related. +Arguments depends on the type of action and are documented at ```'agents/