From 8a597c7ccbe2986f505fd70258e4b59ecef4166f Mon Sep 17 00:00:00 2001 From: "Eduardo Ramos Testillano (eramedu)" Date: Fri, 15 May 2020 17:42:29 +0200 Subject: [PATCH] Implement dynamic procedure at REST interface --- dynamic/launcher/default/Procedure.cpp | 3 + dynamic/launcher/default/Procedure.hpp | 2 + dynamic/launcher/gx/00001/Procedure.cpp | 35 ++++++++++- dynamic/launcher/gx/00001/Procedure.hpp | 2 + example/diameter/launcher/EventOperation.cpp | 58 +++++++++---------- example/diameter/launcher/EventOperation.hpp | 36 ++---------- example/diameter/launcher/MyHandler.cpp | 35 ++++------- example/diameter/launcher/resources/HELP.md | 23 +++++++- .../launcher/resources/rest_api/ct/ct.sh | 31 +++++----- .../rest_api/ct/dynamic-procedure/__init__.py | 0 .../ct/dynamic-procedure/dynamic_test.py | 30 ++++++++++ .../rest_api/ct/hot-changes/services_test.py | 3 +- .../launcher/resources/rest_api/ct/pytest.ini | 11 +++- .../ct/resources/arguments-request.json | 10 ++++ .../ct/resources/servicesGxJson-request.json | 25 ++++++++ ...quest.json => servicesRxJson-request.json} | 0 16 files changed, 198 insertions(+), 106 deletions(-) create mode 100644 example/diameter/launcher/resources/rest_api/ct/dynamic-procedure/__init__.py create mode 100644 example/diameter/launcher/resources/rest_api/ct/dynamic-procedure/dynamic_test.py create mode 100644 example/diameter/launcher/resources/rest_api/ct/resources/arguments-request.json create mode 100644 example/diameter/launcher/resources/rest_api/ct/resources/servicesGxJson-request.json rename example/diameter/launcher/resources/rest_api/ct/resources/{servicesJson-request.json => servicesRxJson-request.json} (100%) diff --git a/dynamic/launcher/default/Procedure.cpp b/dynamic/launcher/default/Procedure.cpp index 598912e..6649e62 100644 --- a/dynamic/launcher/default/Procedure.cpp +++ b/dynamic/launcher/default/Procedure.cpp @@ -24,3 +24,6 @@ void Procedure::execute(const std::string &args, std::string &response) throw(an } } +void Procedure::execute(const nlohmann::json &args, std::string &response) throw(anna::RuntimeException) { + execute(args.dump(), response); +} diff --git a/dynamic/launcher/default/Procedure.hpp b/dynamic/launcher/default/Procedure.hpp index b8096bc..4d85411 100644 --- a/dynamic/launcher/default/Procedure.hpp +++ b/dynamic/launcher/default/Procedure.hpp @@ -11,6 +11,7 @@ // Project #include +#include class Procedure { @@ -20,6 +21,7 @@ class Procedure { Procedure(anna::comm::Application *app) : a_app(app) {;} virtual void execute(const std::string &args, std::string &response) throw(anna::RuntimeException); + virtual void execute(const nlohmann::json &args, std::string &response) throw(anna::RuntimeException); }; #endif diff --git a/dynamic/launcher/gx/00001/Procedure.cpp b/dynamic/launcher/gx/00001/Procedure.cpp index 4bd974f..042efee 100644 --- a/dynamic/launcher/gx/00001/Procedure.cpp +++ b/dynamic/launcher/gx/00001/Procedure.cpp @@ -28,8 +28,19 @@ namespace { void usage (std::string &response) { response += "\n\nInvalid arguments. Provide these ones:"; response += "\n"; + response += "\nSIGUSR2 Interface:"; response += "\n||||[|CCR-T xml file]"; response += "\n"; + response += "\nREST Interface:"; + response += "\n{"; + response += "\n \"seqI\":\"\""; + response += "\n ,\"seqF\":\"\""; + response += "\n ,\"msecsTimeout\":\"\""; + response += "\n ,\"digits\":\"\""; + response += "\n ,\"ccrI\":\"\""; + response += "\n [,\"ccrT\":\"\"]"; + response += "\n}"; + response += "\n"; response += "\nSequences are parsed when needed, over AVPs or internal values:"; response += "\n"; response += "\nSession-Id: ;;[;]"; @@ -278,8 +289,30 @@ void Procedure::execute(const std::string &args, std::string &response) throw(a } } // loop - response = "Completed provision for pid "; response += anna::functions::asString(a_app->getPid()); response += "; range ["; + response = "Completed provision: range ["; response += seq_i; response += ", "; response += seq_f; response += "]; scenary: "; response += "CCR-Initial"; if (haveTermination) response += " + CCR-Termination"; } +void Procedure::execute(const nlohmann::json &args, std::string &response) throw(anna::RuntimeException) { + + // Build the arguments string and call the previous centralized logic procedure execution: + // ||||[|CCR-T xml file] + const char *arg_names[6] = { "seqI", "seqF", "msecsTimeout", "digits", "ccrI", "ccrT" }; + std::string args_string, arg, pipe("|"); + + for (int i = 0; i < 6; i++) + { + auto it = args.find(arg_names[i]); + arg = (it != args.end() && it->is_string()) ? *it : ""; + if (arg != "") args_string += arg + pipe; + } + + // Remove last 'pipe': + if (args_string != "") + args_string = args_string.substr(0, args_string.size()-1); + + execute(args_string, response); +} + + diff --git a/dynamic/launcher/gx/00001/Procedure.hpp b/dynamic/launcher/gx/00001/Procedure.hpp index d6afe5b..7c833f9 100644 --- a/dynamic/launcher/gx/00001/Procedure.hpp +++ b/dynamic/launcher/gx/00001/Procedure.hpp @@ -11,6 +11,7 @@ // Project #include +#include class Procedure { @@ -20,6 +21,7 @@ class Procedure { Procedure(anna::comm::Application *app) : a_app(app) {;} virtual void execute(const std::string &args, std::string &response) throw(anna::RuntimeException); + virtual void execute(const nlohmann::json &args, std::string &response) throw(anna::RuntimeException); }; #endif diff --git a/example/diameter/launcher/EventOperation.cpp b/example/diameter/launcher/EventOperation.cpp index 8d8a97d..a3ee7ba 100644 --- a/example/diameter/launcher/EventOperation.cpp +++ b/example/diameter/launcher/EventOperation.cpp @@ -12,48 +12,24 @@ // Process #include #include +#include +#include +#include +#include -// Project -#include - - -//// Standard -//#include // std::istringstream -//#include // std::cout +// Standard #include -//#include // ceil -//#include #include // chdir -//#include -// -//// Project + +// Project +#include #include #include -//#include -//#include -//#include -//#include -//#include -//#include -//#include #include #include #include -//#include -//#include #include -//#include -//#include #include -// -//// Process -//#include -//#include -//#include -#include -#include -#include -//#include ///////////////////// @@ -1236,3 +1212,21 @@ bool EventOperation::test__dump_stdout(std::string &response, bool enable) { return true; // OK } +bool EventOperation::test__dynamic(std::string &response, const nlohmann::json &arguments) { + + Launcher& my_app = static_cast (anna::app::functions::getApp()); + + Procedure p(&my_app); + try { + p.execute(arguments, response); + } + catch(anna::RuntimeException &ex) { + ex.trace(); + response += ex.asString(); + return false; + } + + return true; // OK +} + + diff --git a/example/diameter/launcher/EventOperation.hpp b/example/diameter/launcher/EventOperation.hpp index 1f0a4ab..dd54020 100644 --- a/example/diameter/launcher/EventOperation.hpp +++ b/example/diameter/launcher/EventOperation.hpp @@ -9,42 +9,11 @@ #ifndef example_diameter_launcher_EventOperation_hpp #define example_diameter_launcher_EventOperation_hpp -// Project - // STL #include -//// Standard -//#include // std::istringstream -//#include // std::cout -//#include // ceil -//#include -//#include // chdir -//#include -// // Project -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -// -//// Process -//#include -//#include -//#include -//#include -//#include -//#include +#include class EventOperation { @@ -150,6 +119,9 @@ public: const std::string & state = "all" /* initialized|in-progress|failed|success|[all]|none */, bool enable = true); bool test__report_hex(std::string &response, bool enable = true); bool test__dump_stdout(std::string &response, bool enable = true); + + // Dynamic procedure + bool test__dynamic(std::string &response, const nlohmann::json &arguments); }; #endif diff --git a/example/diameter/launcher/MyHandler.cpp b/example/diameter/launcher/MyHandler.cpp index d2433c8..9086582 100644 --- a/example/diameter/launcher/MyHandler.cpp +++ b/example/diameter/launcher/MyHandler.cpp @@ -368,31 +368,6 @@ bool MyHandler::doPOST(const std::string &uri, const nlohmann::json &j, std::str auto it = j.find("condition"); if (it != j.end() && it->is_object()) { -/* - auto j2 = it->get(); - - // [code]|[bitR]|[hopByHop]|[applicationId]|[sessionId]|[resultCode]|[msisdn]|[imsi]|[serviceContextId] - auto it_code = j2.find("code"); - auto it_bitR = j2.find("bitR"); - auto it_hopByHop = j2.find("hopByHop"); - auto it_applicationId = j2.find("applicationId"); - auto it_sessionId = j2.find("sessionId"); - auto it_resultCode = j2.find("resultCode"); - auto it_msisdn = j2.find("msisdn"); - auto it_imsi = j2.find("imsi"); - auto it_serviceContextId = j2.find("serviceContextId"); - - std::string p1 = (it_code != j2.end() && it_code->is_string()) ? *it_code : ""; - std::string p2 = (it_bitR != j2.end() && it_bitR->is_string()) ? *it_bitR : ""; - std::string p3 = (it_hopByHop != it->end() && it_hopByHop->is_string()) ? *it_hopByHop : ""; - std::string p4 = (it_applicationId != it->end() && it_applicationId->is_string()) ? *it_applicationId : ""; - std::string p5 = (it_sessionId != it->end() && it_sessionId->is_string()) ? *it_sessionId : ""; - std::string p6 = (it_resultCode != it->end() && it_resultCode->is_string()) ? *it_resultCode : ""; - std::string p7 = (it_msisdn != it->end() && it_msisdn->is_string()) ? *it_msisdn : ""; - std::string p8 = (it_imsi != it->end() && it_imsi->is_string()) ? *it_imsi : ""; - std::string p9 = (it_serviceContextId != it->end() && it_serviceContextId->is_string()) ? *it_serviceContextId : ""; -*/ - // [code]|[bitR]|[hopByHop]|[applicationId]|[sessionId]|[resultCode]|[msisdn]|[imsi]|[serviceContextId] auto it_code = it->find("code"); auto it_bitR = it->find("bitR"); @@ -561,6 +536,16 @@ bool MyHandler::doPOST(const std::string &uri, const nlohmann::json &j, std::str response += "invalid 'action' string field (allowed: enable|disable)"; } + // Dynamic procedure + else if ((opType == "/dynamic")) { + auto it = j.find("arguments"); + if (it != j.end() && it->is_object()) { + result = eop.test__dynamic(response, *it); + } + else + response += "missing 'arguments' object field"; + } + return result; } diff --git a/example/diameter/launcher/resources/HELP.md b/example/diameter/launcher/resources/HELP.md index abb842a..732e938 100644 --- a/example/diameter/launcher/resources/HELP.md +++ b/example/diameter/launcher/resources/HELP.md @@ -1523,7 +1523,7 @@ Value '-1' means 'no limit' (full parallel). Be careful with resources consumpti } ``` -### EXECUTION ACTIONS +### FSM TESTING EXECUTION ACTIONS #### POST /test-ttps @@ -1903,5 +1903,26 @@ As it could be very large, it will be dumped on provided target directory, '*/tm } ``` +### DYNAMIC PROCEDURE +Used for system test. Arguments are determined by the way in a specific dynamic library is designed/documented. + +#### POST /dynamic + +**Request body**: + +``` +{ + "dynamic": +} +``` + +**Response body**: + +``` +{ + "result":"", + "response":"" +} +``` diff --git a/example/diameter/launcher/resources/rest_api/ct/ct.sh b/example/diameter/launcher/resources/rest_api/ct/ct.sh index f51f0d1..0f9fd18 100755 --- a/example/diameter/launcher/resources/rest_api/ct/ct.sh +++ b/example/diameter/launcher/resources/rest_api/ct/ct.sh @@ -7,13 +7,12 @@ REPO_DIR="$(git rev-parse --show-toplevel 2>/dev/null)" [ -z "$REPO_DIR" ] && { echo "You must execute under a valid git repository !" ; exit 1 ; } -SERVICES=${REPO_DIR}/example/diameter/launcher/resources/rest_api/ct/resources/servicesJson-request.json +SERVICES=${REPO_DIR}/example/diameter/launcher/resources/rest_api/ct/resources/servicesRxJson-request.json ENDPOINT=http://localhost:8074 PORT=$(echo ${ENDPOINT} | cut -d: -f3) -C_NAME=anna-adml-http +SVC_NAME=anna-adml-http # Entrypoint arguments (ft/st): EXTRA_ARGUMENTS= # default is ft (function test: debug traces and traffic logs) -EXTRA_ARGUMENTS=st VARIANT=${1:-Release} @@ -34,22 +33,28 @@ nghttp --version 2>/dev/null echo "Requirement found !" echo -echo "Rebuild ADML HTTP service image (y/n) [n]:" +echo "Rebuild ADML HTTP service image (y/n) [y]:" read opt -[ -z "${opt}" ] && opt=n +[ -z "${opt}" ] && opt=y if [ "${opt}" = "y" ] then ${REPO_DIR}/tools/build-anna-adml-http ${VARIANT} fi version=$(${REPO_DIR}/tools/version) -[ "${VARIANT}" = "Debug" ] && { version=${version}-debug ; C_NAME=${C_NAME}-debug ; } -echo "Restart ADML HTTP service image (version '${version}') ..." -docker kill ${C_NAME} &>/dev/null -cid=$(docker run --rm -d -p ${PORT}:${PORT} --name ${C_NAME} anna-adml-http:${version} ${EXTRA_ARGUMENTS} >/dev/null) -[ $? -ne 0 ] && exit 1 - -echo "Container id: ${cid} (deployed as '${C_NAME}')" +cname=${SVC_NAME} +[ "${VARIANT}" = "Debug" ] && { version=${version}-debug ; cname=${cname}-debug ; } +echo "Restart ADML HTTP service image (version '${version}') (y/n) [y]:" +read opt +[ -z "${opt}" ] && opt=y +if [ "${opt}" = "y" ] +then + docker kill ${SVC_NAME} &>/dev/null + docker kill ${SVC_NAME}-debug &>/dev/null + cid=$(docker run --rm -d -p ${PORT}:${PORT} --name ${cname} anna-adml-http:${version} ${EXTRA_ARGUMENTS} >/dev/null) + [ $? -ne 0 ] && exit 1 + echo "Container id: ${cid} (deployed as '${cname}')" +fi echo echo "Configuring services (${SERVICES}) ..." @@ -68,7 +73,7 @@ echo echo -n "Waiting for server-client connection ..." while true do - docker exec -it ${C_NAME} bash -c "netstat -p \$(pgrep ADML) --numeric-ports | grep -w 3868 | grep -w ESTABLISHED" >/dev/null + docker exec -it ${cname} bash -c "netstat -p \$(pgrep ADML) --numeric-ports | grep -w 3868 | grep -w ESTABLISHED" >/dev/null [ $? -eq 0 ] && break echo -n . sleep 1 diff --git a/example/diameter/launcher/resources/rest_api/ct/dynamic-procedure/__init__.py b/example/diameter/launcher/resources/rest_api/ct/dynamic-procedure/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/example/diameter/launcher/resources/rest_api/ct/dynamic-procedure/dynamic_test.py b/example/diameter/launcher/resources/rest_api/ct/dynamic-procedure/dynamic_test.py new file mode 100644 index 0000000..6876e21 --- /dev/null +++ b/example/diameter/launcher/resources/rest_api/ct/dynamic-procedure/dynamic_test.py @@ -0,0 +1,30 @@ +import pytest + + +@pytest.mark.dynamic +@pytest.mark.xfail(reason="This will fail if already provisioned (if you want success here, restart the ADML HTTP Service)") +def test_001_given_servicesGx_json_representation_i_want_to_load_it_through_adml_service(resources, admlc): + + # Load services for Gx interface (client/server nodes & Gx dictionary): + requestBody = resources("servicesGxJson-request.json") + responseBodyRef = { "success":"true", "response":"loaded services correctly" } + + # Send POST + response = admlc.post("/services", requestBody) + + ### Verify response + admlc.assert_response__status_body_headers(response, 200, responseBodyRef) + +@pytest.mark.dynamic +def test_002_provision_dynamic_operation_for_gx_at_adml(resources, admlc): + + # Dynamic procedure: + requestBody = resources("arguments-request.json") + responseBodyRef = { "success":"true", "response":"Completed provision: range [0000, 9000]; scenary: CCR-Initial + CCR-Termination" } + + # Send POST + response = admlc.post("/dynamic", requestBody) + + # Verify response + admlc.assert_response__status_body_headers(response, 200, responseBodyRef) + diff --git a/example/diameter/launcher/resources/rest_api/ct/hot-changes/services_test.py b/example/diameter/launcher/resources/rest_api/ct/hot-changes/services_test.py index 5c90efc..4d8d18e 100644 --- a/example/diameter/launcher/resources/rest_api/ct/hot-changes/services_test.py +++ b/example/diameter/launcher/resources/rest_api/ct/hot-changes/services_test.py @@ -5,7 +5,8 @@ import pytest @pytest.mark.xfail(reason="This will fail if already provisioned (if you want success here, restart the ADML HTTP Service)") def test_001_given_services_json_representation_i_want_to_load_it_through_adml_service(resources, admlc): - requestBody = resources("servicesJson-request.json") + # Load services for Rx interface (client/server nodes & Gx dictionary): + requestBody = resources("servicesRxJson-request.json") responseBodyRef = { "success":"true", "response":"loaded services correctly" } # Send POST diff --git a/example/diameter/launcher/resources/rest_api/ct/pytest.ini b/example/diameter/launcher/resources/rest_api/ct/pytest.ini index e2fc311..cd7b596 100644 --- a/example/diameter/launcher/resources/rest_api/ct/pytest.ini +++ b/example/diameter/launcher/resources/rest_api/ct/pytest.ini @@ -1,5 +1,5 @@ [pytest] -addopts = -v --junitxml=/tmp/junit.xml -n 0 +addopts = -v --junitxml=/tmp/junit.xml -n 0 -m "not dynamic" log_format=%(asctime)s|%(name)s|%(filename)s:%(lineno)d|%(levelname)s|%(message)s log_date_format = %Y-%m-%d %H:%M:%S @@ -12,3 +12,12 @@ log_level=INFO junit_suite_name = "ADML REST API" junit_logging = out-err junit_family = xunit1 + +# Naming conventions +#python_files = check_*.py +#python_classes = Check +#python_functions = *_check + +# Markers +markers = + dynamic: this test requires ADML dynamic test library enabled (/opt/adml/dynlibs) diff --git a/example/diameter/launcher/resources/rest_api/ct/resources/arguments-request.json b/example/diameter/launcher/resources/rest_api/ct/resources/arguments-request.json new file mode 100644 index 0000000..78f591b --- /dev/null +++ b/example/diameter/launcher/resources/rest_api/ct/resources/arguments-request.json @@ -0,0 +1,10 @@ +{ + "arguments": { + "seqI":"0000", + "seqF":"9000", + "msecsTimeout":"5000", + "digits":"4", + "ccrI":"/opt/adml/dynlibs/gx/00001/CCR-I.xml", + "ccrT":"/opt/adml/dynlibs/gx/00001/CCR-T.xml" + } +} diff --git a/example/diameter/launcher/resources/rest_api/ct/resources/servicesGxJson-request.json b/example/diameter/launcher/resources/rest_api/ct/resources/servicesGxJson-request.json new file mode 100644 index 0000000..521b88f --- /dev/null +++ b/example/diameter/launcher/resources/rest_api/ct/resources/servicesGxJson-request.json @@ -0,0 +1,25 @@ +{ + "servicesJson": { + "services": { + "node": [ + { + "@applicationId": "16777238", + "@entity": "localhost:3869", + "@originHost": "ggsnNodeHostname.nodeHostRealm.com" + }, + { + "@applicationId": "16777238", + "@diameterServer": "localhost:3869", + "@diameterServerSessions": "1", + "@originHost": "ownHostIdGx.operatorRealm.com" + } + ], + "stack": { + "@dictionary": "stacks/DictionaryGx.16777238.xml", + "@fixMode": "Always", + "@id": "16777238", + "@ignoreFlagsOnValidation": "yes" + } + } + } +} diff --git a/example/diameter/launcher/resources/rest_api/ct/resources/servicesJson-request.json b/example/diameter/launcher/resources/rest_api/ct/resources/servicesRxJson-request.json similarity index 100% rename from example/diameter/launcher/resources/rest_api/ct/resources/servicesJson-request.json rename to example/diameter/launcher/resources/rest_api/ct/resources/servicesRxJson-request.json -- 2.20.1