Improvements from anna fork
authorEduardo Ramos Testillano <eduardo.ramos.testillano@ericsson.com>
Sat, 11 Apr 2020 23:49:21 +0000 (01:49 +0200)
committerEduardo Ramos Testillano (eramedu) <eduardo.ramos.testillano@ericsson.com>
Fri, 17 Apr 2020 19:27:51 +0000 (21:27 +0200)
* Rename steps that are diameter-specific
  Diameter steps should be renamed to clarify the real context of use.

* Change ADML point of generation for launcher.traces

  Change from the physical executable location to the
  execution point (current working directory)

* Improve final detection algorithm

  Improves resolution to detect the end of the
  testing.
  Uses the number of finished test cases
  (success + failed) instead the initialized
  ones (which is less safer because initial
  conditions).

* Add dynamic ip-limit control

  Add the possibility to control de ip-limit from
  test case by mean a new generic action: ip_limit

* Manage terminate signal from ADML application

* Improve counters
  Implement signalTermination handler and force counter records.
  If counters dumps are disabled (--cntRecordPeriod 0) the file
  won't be created anyway.

  Also, improve the mask for counters file creation instead of
  modifying it on close (avoid possible stop problems). We set
  644 for counters.

* Implement junit report generation
  Add new methods and implement new asXML getter for junit report.

* AOTS (Agents-Oriented Testing Setup): evolution from original
  idea of diameter agents but extended to any kind of resource.

Change-Id: I4dc3ac41b86709439ffa2aa98aa011d956d8df2f

52 files changed:
INSTALL_AOTS.md [new file with mode: 0644]
README.md
dynamic/launcher/gx/00001/Procedure.cpp
example/diameter/launcher/Launcher.cpp
example/diameter/launcher/Launcher.hpp
example/diameter/launcher/MyCounterRecorder.cpp
example/diameter/launcher/deploy-aots.sh [new file with mode: 0755]
example/diameter/launcher/deployments/aots/README.md [new file with mode: 0644]
example/diameter/launcher/deployments/aots/agents/ADML/ACTIONS.md [new file with mode: 0644]
example/diameter/launcher/deployments/aots/agents/ADML/HINTS.md [new file with mode: 0644]
example/diameter/launcher/deployments/aots/agents/ADML/args.ft [new file with mode: 0644]
example/diameter/launcher/deployments/aots/agents/ADML/args.st [new file with mode: 0644]
example/diameter/launcher/deployments/aots/agents/ADML/services.xml.example [new file with mode: 0644]
example/diameter/launcher/deployments/aots/agents/ADML/stacks/DictionaryGx.16777238.xml [new file with mode: 0755]
example/diameter/launcher/deployments/aots/agents/ADML/stacks/DictionaryRx.16777236.xml [new file with mode: 0755]
example/diameter/launcher/deployments/aots/agents/ADML/stacks/DictionarySy.16777302.xml [new file with mode: 0755]
example/diameter/launcher/deployments/aots/agents/ADML/stacks/diameter_base.0.xml [new file with mode: 0755]
example/diameter/launcher/deployments/aots/agents/ADML/start.sh [new file with mode: 0755]
example/diameter/launcher/deployments/aots/agents/ADML/stop.sh [new file with mode: 0755]
example/diameter/launcher/deployments/aots/agents/HTTPMOCK/ACTIONS.md [new file with mode: 0644]
example/diameter/launcher/deployments/aots/agents/HTTPMOCK/Provision.sh [new file with mode: 0755]
example/diameter/launcher/deployments/aots/agents/KAFKA/ACTIONS.md [new file with mode: 0644]
example/diameter/launcher/deployments/aots/agents/KAFKA/Admin.py [new file with mode: 0644]
example/diameter/launcher/deployments/aots/agents/KAFKA/Consumer.py [new file with mode: 0644]
example/diameter/launcher/deployments/aots/agents/KAFKA/Producer.py [new file with mode: 0644]
example/diameter/launcher/deployments/aots/launcher.py [new file with mode: 0755]
example/diameter/launcher/deployments/aots/loader.py [new file with mode: 0755]
example/diameter/launcher/deployments/aots/tests.example/agents.yml [new file with mode: 0644]
example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch01-ipv4/1.Initial.yml [new file with mode: 0644]
example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch01-ipv4/2.Initial2.yml [new file with mode: 0644]
example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch01-ipv4/AAA.xml [new file with mode: 0644]
example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch01-ipv4/AAR.xml [new file with mode: 0644]
example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch01-ipv4/AAR2.xml [new file with mode: 0644]
example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch01-ipv4/expected.json.template [new file with mode: 0644]
example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch02-ipv6/1.Initial.yml [new file with mode: 0644]
example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch02-ipv6/2.Initial2.yml [new file with mode: 0644]
example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch02-ipv6/3.Initial3.yml [new file with mode: 0644]
example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch02-ipv6/AAA.xml [new file with mode: 0644]
example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch02-ipv6/AAR-ipv6.xml [new file with mode: 0644]
example/diameter/launcher/deployments/aots/tests.example/ts02-errors/ch01-protocolErrors/1.MissingRealm.yml [new file with mode: 0644]
example/diameter/launcher/deployments/aots/tests.example/ts02-errors/ch01-protocolErrors/AAA-bad.xml [new file with mode: 0644]
example/diameter/launcher/deployments/aots/tests.example/ts02-errors/ch01-protocolErrors/AAR-bad.xml [new file with mode: 0644]
example/diameter/launcher/main.cpp
include/anna/testing/TestCase.hpp
include/anna/testing/TestCondition.hpp [deleted file]
include/anna/testing/TestDiameterCondition.hpp [new file with mode: 0644]
include/anna/testing/TestManager.hpp
include/anna/testing/TestStep.hpp
source/testing/TestCase.cpp
source/testing/TestCondition.cpp
source/testing/TestManager.cpp
source/testing/TestStep.cpp

diff --git a/INSTALL_AOTS.md b/INSTALL_AOTS.md
new file mode 100644 (file)
index 0000000..983b1e6
--- /dev/null
@@ -0,0 +1,10 @@
+# Agents-Oriented Testing Setup (AOTS)
+
+## Deployment
+
+Execute 'example/diameter/launcher/deploy-aots.sh' and follow instructions.
+
+## Documentation
+
+See README.md file at installation target directory.
+
index 0c0cfd4..5bf77ea 100644 (file)
--- a/README.md
+++ b/README.md
@@ -44,7 +44,7 @@ You could specify type of build, 'Debug' or 'Release', for example:
 
 ### OpenSSL:
      > sudo apt-get install libssl-dev
 
 ### OpenSSL:
      > sudo apt-get install libssl-dev
+
 ### Gnome XML:
      > sudo apt-get install libxml2-dev
 
 ### Gnome XML:
      > sudo apt-get install libxml2-dev
 
@@ -83,7 +83,7 @@ You could specify type of build, 'Debug' or 'Release', for example:
      > cd /usr/src/gtest
      > sudo cmake CMakeLists.txt
      > sudo make
      > cd /usr/src/gtest
      > sudo cmake CMakeLists.txt
      > sudo make
+
      Copy or symlink libgtest.a and libgtest_main.a to your /usr/lib folder:
      > sudo cp *.a /usr/lib
 
      Copy or symlink libgtest.a and libgtest_main.a to your /usr/lib folder:
      > sudo cp *.a /usr/lib
 
@@ -120,3 +120,7 @@ Optionally you could specify another prefix for installation:
 
      > cat install_manifest.txt | sudo xargs rm
 
 
      > cat install_manifest.txt | sudo xargs rm
 
+## AOTS (Agents-Oriented Testing Setup)
+
+See INSTALL_AOTS.md
+
index 20af42c..99a87fe 100644 (file)
@@ -262,19 +262,19 @@ void Procedure::execute(const std::string &args, std::string &response)  throw(a
     }
 
     // Step 2: sendxml2e: CCR-Initial
     }
 
     // Step 2: sendxml2e: CCR-Initial
-    tc->addSendxml2e(ccri.code(), originHost, -1 /* 'wait for request' step number for answers */);
+    tc->addSendDiameterXml2e(ccri.code(), originHost, -1 /* 'wait for request' step number for answers */);
 
     // Step 3: waitfe: CCA with same session id
     // PARAM: 1     2            3      4          5           6             7           8          9       10         11
     //             wait<fe/fc>|[code]|[bitR]|[hopByHop]|[applicationId]|[sessionId]|[resultCode]|[msisdn]|[imsi]|[serviceContextId]
 
     // Step 3: waitfe: CCA with same session id
     // PARAM: 1     2            3      4          5           6             7           8          9       10         11
     //             wait<fe/fc>|[code]|[bitR]|[hopByHop]|[applicationId]|[sessionId]|[resultCode]|[msisdn]|[imsi]|[serviceContextId]
-    tc->addWait(true /* from entity */, "272", "0", "", "", sessionId, "2001", "", "", "");
+    tc->addWaitDiameter(true /* from entity */, "272", "0", "", "", sessionId, "2001", "", "", "");
 
     if (haveTermination) {
       // Step 4: sendxml2e: CCR-Termination
 
     if (haveTermination) {
       // Step 4: sendxml2e: CCR-Termination
-      tc->addSendxml2e(ccrt.code(), originHost, -1 /* 'wait for request' step number for answers */);
+      tc->addSendDiameterXml2e(ccrt.code(), originHost, -1 /* 'wait for request' step number for answers */);
 
       // Step 5: waitfe: CCA with same session id
 
       // Step 5: waitfe: CCA with same session id
-      tc->addWait(true /* from entity */, "272", "0", "", "", sessionId, "2001", "", "", "");
+      tc->addWaitDiameter(true /* from entity */, "272", "0", "", "", sessionId, "2001", "", "", "");
     }
   } // loop
 
     }
   } // loop
 
index 94d8f66..2175263 100644 (file)
@@ -867,6 +867,15 @@ void Launcher::resetCounters() throw() {
   anna::diameter::codec::OamModule::instantiate().resetCounters();
 }
 
   anna::diameter::codec::OamModule::instantiate().resetCounters();
 }
 
+void Launcher::signalTerminate() throw(anna::RuntimeException) {
+  LOGMETHOD(anna::TraceMethod tm("Launcher", "signalTerminate", ANNA_FILE_LOCATION));
+
+  forceCountersRecord();
+
+  a_communicator->terminate ();
+  comm::Application::signalTerminate ();
+}
+
 void Launcher::signalUSR2() throw(anna::RuntimeException) {
 
   std::string inputFile = getSignalUSR2InputFile();
 void Launcher::signalUSR2() throw(anna::RuntimeException) {
 
   std::string inputFile = getSignalUSR2InputFile();
@@ -1220,7 +1229,17 @@ std::string Launcher::help() const throw() {
   result += "\n                                 values (Session-Id, Subscriber-Id, etc.).";
   result += "\n";
   result += "\n                           <command>: commands to be executed for the test id provided. Each command programmed";
   result += "\n                                 values (Session-Id, Subscriber-Id, etc.).";
   result += "\n";
   result += "\n                           <command>: commands to be executed for the test id provided. Each command programmed";
-  result += "\n                                      constitutes a test case 'step', numbered from 1 to N.";
+  result += "\n                                      constitutes a test case 'step', numbered from 1 to N, with an exception for";
+  result += "\n                                      'description' which is used to describe the test case:";
+  result += "\n";
+  result += "\n                              description|<description>  Sets a test case description. Test cases by default are";
+  result += "\n                                                          constructed with description 'Testcase_<id>'.";
+  result += "\n";
+  result += "\n                              ip-limit[|amount]          In-progress limit of test cases controlled from this test.";
+  result += "\n                                                         No new test cases will be launched over this value (test";
+  result += "\n                                                         manager tick work will be ignored). Zero-value is equivalent";
+  result += "\n                                                         to stop the clock tick, -1 is used to specify 'no limit' which";
+  result += "\n                                                         is the default. For missing amount, value of 1 is applied.";
   result += "\n";
   result += "\n                              timeout|<msecs>            Sets an asynchronous timer to restrict the maximum timeout";
   result += "\n                                                          until last test step. Normally, this command is invoked";
   result += "\n";
   result += "\n                              timeout|<msecs>            Sets an asynchronous timer to restrict the maximum timeout";
   result += "\n                                                          until last test step. Normally, this command is invoked";
@@ -1433,10 +1452,10 @@ std::string Launcher::help() const throw() {
   result += "\n                                 next programmed test cases (1 by default). This event works regardless the timer tick";
   result += "\n                                 function, but it is normally used with the test manager tick stopped.";
   result += "\n";
   result += "\n                                 next programmed test cases (1 by default). This event works regardless the timer tick";
   result += "\n                                 function, but it is normally used with the test manager tick stopped.";
   result += "\n";
-  result += "\n   test|ip-limit[|amount]        In-progress limit of test cases. No new test cases will be launched over this value";
-  result += "\n                                 (test Manager tick work will be ignored). Zero-value is equivalent to stop the clock.";
-  result += "\n                                 tick, -1 is used to specify 'no limit' which is the default. If missing amount, the";
-  result += "\n                                 limit and current amount of in-progress test cases will be shown.";
+  result += "\n   test|ip-limit[|amount]        In-progress limit of test cases established from global context. This value will be";
+  result += "\n                                 overwritten if used at test case level when the corresponding test step is executed.";
+  result += "\n                                 Anyway, the meaning is the same in both contexts. But now, when the amount is missing,";
+  result += "\n                                 the limit and current amount of in-progress test cases will be shown.";
   result += "\n";
   result += "\n   test|goto|<id>                Updates current test pointer position.";
   result += "\n";
   result += "\n";
   result += "\n   test|goto|<id>                Updates current test pointer position.";
   result += "\n";
@@ -1444,13 +1463,14 @@ 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 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|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|<test case id>', which means that";
+  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[|<test case id>]', which means that";
   result += "\n                                 the test case is selected to be interactive, but no step is executed. Then you have to";
   result += "\n                                 interact with positive amounts (usually 1), executing the provided number of steps if";
   result += "\n                                 they are ready and fulfill the needed conditions. The value of 0, implies no execution";
   result += "\n                                 steps margin, which could be useful to 'freeze' a test in the middle of its execution.";
   result += "\n                                 You could also provide -1 to make it non-interactive resuming it from the current step.";
   result += "\n                                 the test case is selected to be interactive, but no step is executed. Then you have to";
   result += "\n                                 interact with positive amounts (usually 1), executing the provided number of steps if";
   result += "\n                                 they are ready and fulfill the needed conditions. The value of 0, implies no execution";
   result += "\n                                 steps margin, which could be useful to 'freeze' a test in the middle of its execution.";
   result += "\n                                 You could also provide -1 to make it non-interactive resuming it from the current step.";
+  result += "\n                                 By default, current test case id is selected for interaction.";
   result += "\n";
   result += "\n   test|reset|<[soft]/hard>[|id] Reset the test case for id provided, all the tests when missing. It could be hard/soft:";
   result += "\n                                 - hard: you probably may need to stop the load rate before. This operation initializes";
   result += "\n";
   result += "\n   test|reset|<[soft]/hard>[|id] Reset the test case for id provided, all the tests when missing. It could be hard/soft:";
   result += "\n                                 - hard: you probably may need to stop the load rate before. This operation initializes";
@@ -1468,8 +1488,19 @@ std::string Launcher::help() const throw() {
   result += "\n                                 been configured for the test case, hard reset could prevent stuck on the next cycle for";
   result += "\n                                 those test cases still in progress.";
   result += "\n";
   result += "\n                                 been configured for the test case, hard reset could prevent stuck on the next cycle for";
   result += "\n                                 those test cases still in progress.";
   result += "\n";
+  result += "\n   test|initialized              Shows the number of initialized test cases. Zero-value means that everything was processed";
+  result += "                                   or not initiated yet.\n";
+  result += "\n";
+  result += "\n   test|finished                 Shows the number of finished (successful or failed) test cases.";
+  result += "\n";
   result += "\n   test|clear                    Clears all the programmed test cases and stop testing (if in progress).";
   result += "\n";
   result += "\n   test|clear                    Clears all the programmed test cases and stop testing (if in progress).";
   result += "\n";
+  result += "\n   test|junit                    Shows the junit report in the moment of execution.";
+  result += "\n";
+  result += "\n   test|summary-counts           Test manager counts report. Counts by state and prints total verdict.";
+  result += "\n";
+  result += "\n   test|summary-states           Test manager states report.";
+  result += "\n";
   result += "\n   test|summary                  Test manager general report (number of test cases, counts by state, global configuration,";
   result += "\n                                 forced in-progress limitation, reports visibility, etc.). Be careful when you have reports";
   result += "\n                                 enabled because the programmed test cases dumps could be heavy (anyway you could enable the";
   result += "\n   test|summary                  Test manager general report (number of test cases, counts by state, global configuration,";
   result += "\n                                 forced in-progress limitation, reports visibility, etc.). Be careful when you have reports";
   result += "\n                                 enabled because the programmed test cases dumps could be heavy (anyway you could enable the";
@@ -1492,6 +1523,8 @@ std::string Launcher::help() const throw() {
   result += "\n";
   result += "\n   test|report-hex[|[yes]|no]    Reports could include the diameter messages in hexadecimal format. Disabled by default.";
   result += "\n";
   result += "\n";
   result += "\n   test|report-hex[|[yes]|no]    Reports could include the diameter messages in hexadecimal format. Disabled by default.";
   result += "\n";
+  result += "\n   test|dump-stdout[|[yes]|no]   Test manager information is dumped into stdout.";
+  result += "\n";
   result += "\n";
   result += "\n------------------------------------------------------------------------------------- Dynamic procedure";
   result += "\n";
   result += "\n";
   result += "\n------------------------------------------------------------------------------------- Dynamic procedure";
   result += "\n";
@@ -2094,6 +2127,14 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
       testManager.setDumpHex((param2 == "yes"));
       opt_response_content += (testManager.getDumpHex() ? "report includes hexadecimal messages" : "report excludes hexadecimal messages");
     }
       testManager.setDumpHex((param2 == "yes"));
       opt_response_content += (testManager.getDumpHex() ? "report includes hexadecimal messages" : "report excludes hexadecimal messages");
     }
+    else if(param1 == "dump-stdout") {
+      if (numParams > 2)
+        throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
+
+      if(param2 == "") param2 = "yes";
+      testManager.setDumpStdout((param2 == "yes"));
+      opt_response_content += (testManager.getDumpHex() ? "test manager dumps progress into stdout" : "test manager does not dump progress into stdout");
+    }
     else if(param1 == "goto") {
       if (numParams > 2)
         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
     else if(param1 == "goto") {
       if (numParams > 2)
         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
@@ -2132,14 +2173,14 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
       }
     }
     else if (param1 == "interact") {
       }
     }
     else if (param1 == "interact") {
-      if (numParams != 3)
+      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 amount = atoi(param2.c_str());
       if (amount < -1)
         throw anna::RuntimeException("Interactive amount must be -1 (to disable interactive mode) or a positive number.", ANNA_FILE_LOCATION);
 
         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
 
       int amount = atoi(param2.c_str());
       if (amount < -1)
         throw anna::RuntimeException("Interactive amount must be -1 (to disable interactive mode) or a positive number.", ANNA_FILE_LOCATION);
 
-      int id = atoi(param3.c_str());
+      int id = ((param3 != "") ? atoi(param3.c_str()) : -1);
       anna::testing::TestCase *testCase = testManager.findTestCase(id);
       if (testCase) {
         if (amount == -1) {
       anna::testing::TestCase *testCase = testManager.findTestCase(id);
       if (testCase) {
         if (amount == -1) {
@@ -2203,6 +2244,18 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
       testManager.setAutoResetHard(param2 == "hard");
       opt_response_content += anna::functions::asString("Auto-reset configured to '%s'", param2.c_str());
     }
       testManager.setAutoResetHard(param2 == "hard");
       opt_response_content += anna::functions::asString("Auto-reset configured to '%s'", param2.c_str());
     }
+    else if(param1 == "initialized") {
+      if (numParams > 1)
+        throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
+
+      opt_response_content = anna::functions::asString("%lu", testManager.getInitializedCount());
+    }
+    else if(param1 == "finished") {
+      if (numParams > 1)
+        throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
+
+      opt_response_content = anna::functions::asString("%lu", testManager.getFinishedCount());
+    }
     else if(param1 == "clear") {
       if (numParams > 1)
         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
     else if(param1 == "clear") {
       if (numParams > 1)
         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
@@ -2214,6 +2267,18 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
         opt_response_content = "there are not programmed test cases to be removed";
       }
     }
         opt_response_content = "there are not programmed test cases to be removed";
       }
     }
+    else if(param1 == "junit") {
+      response_content = testManager.junitAsXMLString();
+      return;
+    }
+    else if(param1 == "summary-counts") {
+      response_content = testManager.summaryCounts();
+      return;
+    }
+    else if(param1 == "summary-states") {
+      response_content = testManager.summaryStates();
+      return;
+    }
     else if(param1 == "summary") {
       response_content = testManager.asXMLString();
       return;
     else if(param1 == "summary") {
       response_content = testManager.asXMLString();
       return;
@@ -2225,6 +2290,8 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
 
       // PARAM: 1     2            3      4          5           6             7           8          9       10         11
       // test|<id>|<command>
 
       // PARAM: 1     2            3      4          5           6             7           8          9       10         11
       // test|<id>|<command>
+      //             description|<description>
+      //             ip-limit[|<iplimit>]
       //             timeout|    <msecs>
       //             sendxml2e|  <file>[|<step number>]
       //             sendxml2c|  <file>[|<step number>]
       //             timeout|    <msecs>
       //             sendxml2e|  <file>[|<step number>]
       //             sendxml2c|  <file>[|<step number>]
@@ -2236,7 +2303,19 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
       if(param2 == "") throw anna::RuntimeException("Missing command for test id operation", ANNA_FILE_LOCATION);
 
       // Commands:
       if(param2 == "") throw anna::RuntimeException("Missing command for test id operation", ANNA_FILE_LOCATION);
 
       // Commands:
-      if (param2 == "timeout") {
+      if (param2 == "description") {
+        if (numParams > 3)
+          throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
+        if(param3 == "") throw anna::RuntimeException("Missing description for test case", ANNA_FILE_LOCATION);
+        testManager.getTestCase(id)->setDescription(param3); // creates / reuses
+      }
+      else if (param2 == "ip-limit") {
+        if (numParams > 3)
+          throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
+        unsigned int limit = (param3 == "") ? 1 : atoi(param3.c_str());
+        testManager.getTestCase(id)->addIpLimit(limit); // creates / reuses
+      }
+      else if (param2 == "timeout") {
         if (numParams > 3)
           throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
         if(param3 == "") throw anna::RuntimeException("Missing milliseconds for 'timeout' command in test id operation", ANNA_FILE_LOCATION);
         if (numParams > 3)
           throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
         if(param3 == "") throw anna::RuntimeException("Missing milliseconds for 'timeout' command in test id operation", ANNA_FILE_LOCATION);
@@ -2257,9 +2336,9 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
         int stepNumber = ((param4 != "") ? atoi(param4.c_str()):-1);
 
         if (param2 == "sendxml2e")
         int stepNumber = ((param4 != "") ? atoi(param4.c_str()):-1);
 
         if (param2 == "sendxml2e")
-          testManager.getTestCase(id)->addSendxml2e(codecMsg.code(), getOperatedHost(), stepNumber); // creates / reuses
+          testManager.getTestCase(id)->addSendDiameterXml2e(codecMsg.code(), getOperatedHost(), stepNumber); // creates / reuses
         else
         else
-          testManager.getTestCase(id)->addSendxml2c(codecMsg.code(), getOperatedHost(), stepNumber); // creates / reuses
+          testManager.getTestCase(id)->addSendDiameterXml2c(codecMsg.code(), getOperatedHost(), stepNumber); // creates / reuses
       }
       else if (param2 == "delay") {
         if (numParams > 3)
       }
       else if (param2 == "delay") {
         if (numParams > 3)
@@ -2273,7 +2352,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
           throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
         if (param3 != "" || param4 != "" || param5 != "" || param6 != "" || param7 != "" || param8 != "" || param9 != "" || param10 != "" || param11 != "") {
           bool fromEntity = (param2.substr(4,2) == "fe");
           throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
         if (param3 != "" || param4 != "" || param5 != "" || param6 != "" || param7 != "" || param8 != "" || param9 != "" || param10 != "" || param11 != "") {
           bool fromEntity = (param2.substr(4,2) == "fe");
-          testManager.getTestCase(id)->addWait(fromEntity, param3, param4, param5, param6, param7, param8, param9, param10, param11);
+          testManager.getTestCase(id)->addWaitDiameter(fromEntity, param3, param4, param5, param6, param7, param8, param9, param10, param11);
         }
         else {
           throw anna::RuntimeException(anna::functions::asString("Missing condition for '%s' command in test id operation", param2.c_str()), ANNA_FILE_LOCATION);
         }
         else {
           throw anna::RuntimeException(anna::functions::asString("Missing condition for '%s' command in test id operation", param2.c_str()), ANNA_FILE_LOCATION);
@@ -2302,7 +2381,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
         }
 
         bool fromEntity = (param2.substr(4,2) == "fe");
         }
 
         bool fromEntity = (param2.substr(4,2) == "fe");
-        testManager.getTestCase(id)->addWaitRegexpHex(fromEntity, regexp);
+        testManager.getTestCase(id)->addWaitDiameterRegexpHex(fromEntity, regexp);
       }
       else if ((param2 == "waitfe-xml")||(param2 == "waitfc-xml")) {
         if (numParams > 4)
       }
       else if ((param2 == "waitfe-xml")||(param2 == "waitfc-xml")) {
         if (numParams > 4)
@@ -2362,7 +2441,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
         }
 
         bool fromEntity = (param2.substr(4,2) == "fe");
         }
 
         bool fromEntity = (param2.substr(4,2) == "fe");
-        testManager.getTestCase(id)->addWaitRegexpXml(fromEntity, regexp);
+        testManager.getTestCase(id)->addWaitDiameterRegexpXml(fromEntity, regexp);
       }
       else if (param2 == "sh-command") {
         // Allow pipes in command:
       }
       else if (param2 == "sh-command") {
         // Allow pipes in command:
index 1070e40..c97fe7b 100644 (file)
@@ -113,6 +113,7 @@ public:
   void resetStatistics() throw();
   void resetCounters() throw();
   void signalUSR2() throw(anna::RuntimeException);
   void resetStatistics() throw();
   void resetCounters() throw();
   void signalUSR2() throw(anna::RuntimeException);
+  void signalTerminate() throw(anna::RuntimeException);
   std::string help() const throw();
   anna::xml::Node* oamAsXML(anna::xml::Node* parent) const throw();
   anna::xml::Node* statsAsXML(anna::xml::Node* parent) const throw();
   std::string help() const throw();
   anna::xml::Node* oamAsXML(anna::xml::Node* parent) const throw();
   anna::xml::Node* statsAsXML(anna::xml::Node* parent) const throw();
index dd32652..fd29422 100644 (file)
@@ -34,7 +34,7 @@ void MyCounterRecorder::open() throw(anna::RuntimeException) {
          anna::Logger::debug(msg, ANNA_FILE_LOCATION);
        );
 
          anna::Logger::debug(msg, ANNA_FILE_LOCATION);
        );
 
-       if((a_stream = ::open(a_fileName.c_str(), O_WRONLY | O_CREAT, S_IWUSR)) == -1)
+       if((a_stream = ::open(a_fileName.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH /*0644*/)) == -1)
          throw RuntimeException(anna::functions::asString("Error opening file '%s'; errno = %d", a_fileName.c_str(), errno), ANNA_FILE_LOCATION);
 
        sprintf(str, "%04d-%02d-%02d %02d:%02d|%04d-%02d-%02d %02d:%02d",
          throw RuntimeException(anna::functions::asString("Error opening file '%s'; errno = %d", a_fileName.c_str(), errno), ANNA_FILE_LOCATION);
 
        sprintf(str, "%04d-%02d-%02d %02d:%02d|%04d-%02d-%02d %02d:%02d",
@@ -61,7 +61,7 @@ void MyCounterRecorder::close() throw() {
          a_stream = -1;
        }
 
          a_stream = -1;
        }
 
-chmod(a_fileName.c_str(), S_IWUSR | S_IRUSR);
+//chmod(a_fileName.c_str(), S_IWUSR | S_IRUSR);
 a_previousTime = ::time(NULL);
 }
 
 a_previousTime = ::time(NULL);
 }
 
diff --git a/example/diameter/launcher/deploy-aots.sh b/example/diameter/launcher/deploy-aots.sh
new file mode 100755 (executable)
index 0000000..8a7f8b7
--- /dev/null
@@ -0,0 +1,110 @@
+#!/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 "      AGENTS-ORIENTED TESTING SETUP 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_AOTS__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 " (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)"
+
+echo
+echo "Stage 1: Deploying resources ......................"
+echo
+mkdir -p $INSTALL_AOTS
+cp -r $SCR_DIR/deployments/aots/* $INSTALL_AOTS
+
+# 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_AOTS}/agents/ADML
+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_signal.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
+
+_exit "Done!" 0
+
diff --git a/example/diameter/launcher/deployments/aots/README.md b/example/diameter/launcher/deployments/aots/README.md
new file mode 100644 (file)
index 0000000..35570c1
--- /dev/null
@@ -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 <tests directory>/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:
+
+```
+<node label>:
+  description: <description>
+  template: KAFKA
+  topic: <kafka topic>
+```
+
+For ADML (diameter):
+
+```
+<node label>:
+  description: <description>
+  template: ADML
+  application_id: <value>
+  dictionary: <xml dictionary path (absolute or relative to ./agents/ADML)>
+  type: client | server
+  origin_host: <origin host>
+  origin_realm: <origin realm>
+  address: <ip/server:port[,ip2/server2:port,...]>
+```
+
+For HTTPMOCK:
+
+```
+<node label>:
+  description: <description>
+  template: HTTPMOCK
+  address: <ip/server:port>
+```
+
+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 (<test-
+                        dir>/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:
+
+```
+<tests directory>/agents.yml
+<tests directory>/<test suite>/.../<test case>.yml
+<tests directory>/<test suite(i)>/.../<dir(j)>/.../<test case(k)>.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 <tests directory>
+```
+
+The procedure will look for the file ```'<tests directory>'/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 <tests directory> -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 <tests directory> --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 <tests directory>/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 <test case file> .yml)'```
+  in order to keep order:
+
+```
+     <dir>/tc1-initial-with-ipv4.yml
+     <dir>/tc1-initial-with-ipv4/AAA.xml
+     <dir>/tc1-initial-with-ipv4/smpc-consumed.json
+     <dir>/tc2-initial-with-ipv6.yml
+     <dir>/tc2-initial-with-ipv6/AAA.xml
+     <dir>/tc2-initial-with-ipv6/smpc-consumed.json
+     ...
+```
+
+* Alternative 2: create a unique directory for the definition and all the test case stuff:
+
+```
+     <dir>/tc1/initial-with-ipv4.yml
+     <dir>/tc1/AAA.xml
+     <dir>/tc1/smpc-consumed.json
+     <dir>/tc2/initial-with-ipv6.yml
+     <dir>/tc2/AAA.xml
+     <dir>/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/<template>/ACTIONS.md'``` file.
+Generic actions are documented below ([Generic actions](#generic-actions)).
+
+```
+- action: [<agent id>/]<action id>
+  arguments:
+    <argument 1>: <value 1>
+    ...
+    <argument m>: <value m>
+  ...
+```
+
+Example:
+
+```
+# Step 1: Establish a timeout for the whole test case
+- action: timeout_ms
+  arguments:
+    value: 1000
+
+# Step 2: Clean kafka message bus for the SMPC topic
+- action: SMPC/admin
+  arguments:
+    operation: clean_topic
+
+# Step 3: Send the AA-Request to the server
+- action: AF/send_xml_to_entity
+  arguments:
+    xml: AAR.xml
+
+# Step 4: Wait for AA-Answer for the previous request sent
+- action: AF/wait_xml_from_entity
+  arguments:
+    xml: AAA.xml
+    answers_to: 3
+
+# Step 4: Check the message bus for a kafka notify reception
+- action: SMPC/consume_json
+  arguments:
+    json: expected.json
+    auto_offset_reset: earliest
+
+```
+
+It is good to comment each step with the position number (1..N). Take into account that lists are processed in order. Some steps could reference to other steps and this position could be useful for that purpose (see step 4 in the example above).
+
+### Generic actions
+
+#### sh_command
+
+Sets a shell command script execution.
+
+Arguments: value
+* value: command and arguments
+
+Example:
+```
+- action: sh_command
+  arguments:
+    value: cat AAR.xml
+```
+
+#### timeout_ms
+
+Sets timeout (milliseconds) to complete the whole test case, or get a Failed state if not completed.
+This could be configured at any step point, where will be started as time measuring reference.
+It is recommended in order to ensure that test will not be in-progress eternally.
+The launcher executor will calculate 15 seconds per test case by default just in case this
+timeout feature is not used.
+
+Arguments: value
+* value: numeric value for the lapse of time in milliseconds.
+
+Example:
+```
+- action: timeout_ms
+  arguments:
+    value: 3500
+```
+
+#### delay_ms
+
+Sets delay (milliseconds) to sleep the test case at the corresponding delay step.
+
+Arguments: value
+* value: numeric value for the lapse of time in milliseconds.
+
+Example:
+```
+- action: delay_ms
+  arguments:
+    value: 200
+```
+
+#### ip_limit
+
+Controller In-Progress limit. A value of 1 means that only 1 test case with
+status "in progress" is allowed. That is to say, ip_limit=1 is the same as
+sequential execution of test cases.
+
+This value can be set for the whole launcher execution by mean '-p|--ip-limit'
+argument, but anyway, the value set in a test case definition has priority
+because it will remain for the following test cases executed if the action
+is not used to update the value again. Summing up, the command line for
+launcher only sets the initial ip-limit value (1 by default).
+
+Also we can restore the command-line value in any moment just configuring
+"launcher" for this action value.
+
+A typical use case for this is when a chapter or a whole test suite need a
+initial (or intermediate) administration step to do global actions which
+could impact in the real test cases if everything is executed in parallel.
+For example, remove kafka bus or create first dummy message, could impact
+in the real test cases which are consuming data from the kafka message bus.
+Then, we will define a first test-suite case with the proper administrative
+operations setting ip-limit to 1, doing needed operations and updating this
+limit again (to -1, no limit, or any other). The reserved word "launcher"
+helps to restore the value which the user provided through the launcher
+procedure (in this way, if user wanted sequential, the tests will be
+sequentially executed).
+
+To ensure that an intermediate test case is the only executed we should
+first set the ip-limit to 1, then perform a delay step with the maximum
+timeout of the suite test cases, and then, perform the administrative
+operations. Finally, as it is unpredictable to know which test case is the
+following, in the same administrative test case, we end defining another
+ip_limit action to restore the parallel value (ip limit > 1 or "launcher").
+Also, those administrative operations should be blocking until completed.
+
+Arguments: value
+* value: in progress limit amount (-1: no limit, 0: stops the ttps clock). Reserved 'launcher' sets the command-line launcher received value.
+
+Example:
+```
+- action: ip_limit
+  arguments:
+    value: 1
+```
+
+#### modify_xml_avp_data
+
+Replace operations over ANNA diameter message xml representation.
+Replaces data (or hex-data) fields for specific avps before processing.
+XPath is used to select the node(s) to be processed. The value provided
+will replace the data or hex-data field. A list is used to allow one or
+more replace operations over the same xml file.
+
+Arguments: xml, new_xml, xpath_value_list
+* xml: the diameter message in xml format. Could be a symlink to common template.
+* new_xml: new diameter message in xml format. Should be the one referred at test case.
+* xpath_value_list: list of items with fields: xpath, value
+
+Example:
+```
+- action: modify_xml_avp_data
+  arguments:
+    xml: AAR.xml.template
+    new_xml: AAR.xml
+    xpath_value_list:
+    - xpath: "avp[@name='Session-Id']"
+      value: "mytest;afNodeHostname.nodeHostRealm.com;1;005"
+    - xpath: "avp[@name='Framed-IP-Address']"
+      value: "c52a0b20"
+```
+
+Important note: better don't use for diameter messages used in wait conditions.
+The reason is that wait conditions acts using the xml content as regular
+expression to be matched with serialized message received at the endpoint.
+The thing is that diameter xml representation compared with python parser
+representation will be probably different and regular expression won't be
+valid to match. Summing up, this kind of messages must be statically
+created with the expected content. Anyway, fields like hop-by-hop, end-to-end
+and Origin-State-Id AVP values, will be automatically matched regardless its
+value (regular expression is internally modified to replace them by '[0-9]+').
+
+#### modify_json_key_value
+
+Replaces value fields for specific keys before processing.
+
+Arguments: json, new_json, kpath_value_list
+* json: the json file. Could be a symlink to common template.
+* new_json: new json file. Must be the one referred at test case.
+* kpath_value_list: list of items with fields: kpath, value
+
+The 'key path' concept (kpath field) consist in json node location
+by mean a dot-separated string: <node1>[.<node2>]..[.<nodeN>].
+
+Example:
+```
+- action: modify_json_key_value
+  arguments:
+    json: notify.json.template
+    new_json: notify.json
+    kpath_value_list:
+    - kpath: "session.smSessionId"
+      value: "the_sm_session_id"
+```
+
+#### system_cmd
+
+Launchs an inline shell command or a user script file.
+
+Arguments: [shell, file, file_parameters]
+* shell: inline shell command and parameters.
+* file: script or procedure defined in a test case file
+* file_parameters: script file parameters (empty by default)
+
+Example:
+```
+- action: system_cmd
+  arguments:
+    shell: echo "this is a test"
+
+- action: system_cmd
+  arguments:
+    shell: rdate remote_host
+
+- action: system_cmd
+  arguments:
+    file: check_consumer_step.py
+    file_parameters: 2
+```
+
+## Environment variables
+
+Agents directory, where templates ADML and KAFKA are located, could be accessible through environment variable 'AGENTS_DIR'.
+For example, you could use it for an system_cmd action:
+
+Example:
+```
+- action: system_cmd
+  arguments:
+    value: check_consumption.sh $AGENTS_DIR/KAFKA/SMPC-consumer.sh.output
+```
+
+## Logs generated
+
+After launcher execution, a '<tests directory>.logs' directory is created within test directory processed.
+This is an example of logs created by AOTS:
+
+```
+drwxrwxr-x 2 vagrant vagrant  4096 Aug 18 21:24 traffic
+-rw-rw-r-- 1 vagrant vagrant 16065 Aug 18 21:24 junit.xml
+-rw-rw-r-- 1 vagrant vagrant 15166 Aug 18 21:24 tests.summary
+-rw-rw-r-- 1 vagrant vagrant 10747 Aug 18 21:24 tests.oam
+-rw-rw-r-- 1 vagrant vagrant   697 Aug 18 21:24 tests.stats
+-rw-rw-r-- 1 vagrant vagrant 83839 Aug 18 21:24 adml.context
+drwxrwxr-x 2 vagrant vagrant  4096 Aug 18 21:24 counters
+drwxrwxr-x 2 vagrant vagrant  4096 Aug 18 21:24 test-reports
+drwxrwxr-x 2 vagrant vagrant  4096 Aug 18 21:24 debug
+```
+
+* traffic: folder containing traffic logs, for example diameter interfaces flow information.
+* junit.xml: JUnit report for the testsuite execution.
+* tests.summary: ADML director tests summary in xml format.
+* tests.oam: ADML director Operation & Maintenance information (counters)
+* tests.stats: ADML director statistic module information (average, deviation, max & min values for any of the statistic concepts registered, for example processing time in every interface, messages size, etc.).
+* adml.context: context information in xml format for the ADML director process.
+* counters: counters information, for example diameter events.
+* test-reports: ADML tests reports in xml format.
+* debug: debug information (ADML traces, yml test case files list, relation between ids and descriptions, ADML operations dumps, etc.).
+
diff --git a/example/diameter/launcher/deployments/aots/agents/ADML/ACTIONS.md b/example/diameter/launcher/deployments/aots/agents/ADML/ACTIONS.md
new file mode 100644 (file)
index 0000000..280b214
--- /dev/null
@@ -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/agents/ADML/HINTS.md b/example/diameter/launcher/deployments/aots/agents/ADML/HINTS.md
new file mode 100644 (file)
index 0000000..a9314e3
--- /dev/null
@@ -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:
+<avp name="AF-Application-Identifier" hex-data="75726e25334175726e2d787878253341336770702d736572766963652e696d732e696373692e6d6d74656c"/>
+
+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/agents/ADML/args.ft b/example/diameter/launcher/deployments/aots/agents/ADML/args.ft
new file mode 100644 (file)
index 0000000..fa3411d
--- /dev/null
@@ -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/agents/ADML/args.st b/example/diameter/launcher/deployments/aots/agents/ADML/args.st
new file mode 100644 (file)
index 0000000..733daaf
--- /dev/null
@@ -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/agents/ADML/services.xml.example b/example/diameter/launcher/deployments/aots/agents/ADML/services.xml.example
new file mode 100644 (file)
index 0000000..a54cf09
--- /dev/null
@@ -0,0 +1,29 @@
+<services>
+  <!--
+  Stacks
+
+  * Monostack: you could select any id value, normally 0.
+  * Multistack: use the application id for the id value.
+                This eases codec engine selection for
+                processed messages.
+  -->
+  <stack id="0" dictionary="dictionary.xml" fixMode="Never"/>
+
+  <!--
+  Nodes
+
+  * Client example:
+
+  <node originHost="<origin host>" applicationId="<application id>"
+        entity="<addr1:port1[,addr2:port2]...[,addrN:portN]>"
+        cer="<cer xml file>" answersTimeout="300000" dumpLog="yes"/>
+
+  * Server example:
+
+  <node originHost="<origin host>" applicationId="<application id>"
+        diameterServer="<addr:port>" diameterServerSessions="10"
+        cea="<cea xml file>" answersTimeout="300000" dumpLog="yes"/>
+  -->
+  <node originHost="afHost.afRealm.com" applicationId="0" entity="localhost:3868"/>
+
+</services>
diff --git a/example/diameter/launcher/deployments/aots/agents/ADML/stacks/DictionaryGx.16777238.xml b/example/diameter/launcher/deployments/aots/agents/ADML/stacks/DictionaryGx.16777238.xml
new file mode 100755 (executable)
index 0000000..5922ae7
--- /dev/null
@@ -0,0 +1,1477 @@
+<dictionary name="DictionaryGx | Application-Id: 16777238">
+   <vendor name="IETF" code="0"/>
+   <vendor name="ERICSSON" code="193"/>
+   <vendor name="3GPP2" code="5535"/>
+   <vendor name="3GPP" code="10415"/>
+   <avp name="User-Name" code="1" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Framed-IP-Address" code="8" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Class" code="25" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Session-Timeout" code="27" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Called-Station-Id" code="30" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Proxy-State" code="33" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Acct-Session-Id" code="44" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Acct-Multi-Session-Id" code="50" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Event-Timestamp" code="55" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Time"/>
+   </avp>
+   <avp name="Acct-Interim-Interval" code="85" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Framed-IPv6-Prefix" code="97" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Host-IP-Address" code="257" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Address"/>
+   </avp>
+   <avp name="Auth-Application-Id" code="258" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Acct-Application-Id" code="259" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Vendor-Specific-Application-Id" code="260" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Vendor-Id" type="Mandatory" qual="1*"/>
+         <avprule id="Auth-Application-Id" type="Optional"/>
+         <avprule id="Acct-Application-Id" type="Optional"/>
+      </grouped>
+   </avp>
+   <avp name="Redirect-Host-Usage" code="261" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-6">
+         <label data="0" alias="DONT_CACHE"/>
+         <label data="1" alias="ALL_SESSION"/>
+         <label data="2" alias="ALL_REALM"/>
+         <label data="3" alias="REALM_AND_APPLICATION"/>
+         <label data="4" alias="ALL_APPLICATION"/>
+         <label data="5" alias="ALL_HOST"/>
+         <label data="6" alias="ALL_USER"/>
+      </single>
+   </avp>
+   <avp name="Redirect-Max-Cache-Time" code="262" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Session-Id" code="263" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Origin-Host" code="264" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Supported-Vendor-Id" code="265" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Vendor-Id" code="266" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Firmware-Revision" code="267" may-encrypt="yes" v-bit="mustnot" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Result-Code" code="268" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32">
+         <label data="1001" alias="DIAMETER_MULTI_ROUND_AUTH"/>
+         <label data="2001" alias="DIAMETER_SUCCESS"/>
+         <label data="2002" alias="DIAMETER_LIMITED_SUCCESS"/>
+         <label data="3001" alias="DIAMETER_COMMAND_UNSUPPORTED"/>
+         <label data="3002" alias="DIAMETER_UNABLE_TO_DELIVER"/>
+         <label data="3003" alias="DIAMETER_REALM_NOT_SERVED"/>
+         <label data="3004" alias="DIAMETER_TOO_BUSY"/>
+         <label data="3005" alias="DIAMETER_LOOP_DETECTED"/>
+         <label data="3006" alias="DIAMETER_REDIRECT_INDICATION"/>
+         <label data="3007" alias="DIAMETER_APPLICATION_UNSUPPORTED"/>
+         <label data="3008" alias="DIAMETER_INVALID_HDR_BITS"/>
+         <label data="3009" alias="DIAMETER_INVALID_AVP_BITS"/>
+         <label data="3010" alias="DIAMETER_UNKNOWN_PEER"/>
+         <label data="4001" alias="DIAMETER_AUTHENTICATION_REJECTED"/>
+         <label data="4002" alias="DIAMETER_OUT_OF_SPACE"/>
+         <label data="4003" alias="DIAMETER_ELECTION_LOST"/>
+         <label data="4241" alias="DIAMETER_NO_AVAILABLE_POLICY_COUNTERS"/>
+         <label data="5001" alias="DIAMETER_AVP_UNSUPPORTED"/>
+         <label data="5002" alias="DIAMETER_UNKNOWN_SESSION_ID"/>
+         <label data="5003" alias="DIAMETER_AUTHORIZATION_REJECTED"/>
+         <label data="5004" alias="DIAMETER_INVALID_AVP_VALUE"/>
+         <label data="5005" alias="DIAMETER_MISSING_AVP"/>
+         <label data="5006" alias="DIAMETER_RESOURCES_EXCEEDED"/>
+         <label data="5007" alias="DIAMETER_CONTRADICTING_AVPS"/>
+         <label data="5008" alias="DIAMETER_AVP_NOT_ALLOWED"/>
+         <label data="5009" alias="DIAMETER_AVP_OCCURS_TOO_MANY_TIMES"/>
+         <label data="5010" alias="DIAMETER_NO_COMMON_APPLICATION"/>
+         <label data="5011" alias="DIAMETER_UNSUPPORTED_VERSION"/>
+         <label data="5012" alias="DIAMETER_UNABLE_TO_COMPLY"/>
+         <label data="5013" alias="DIAMETER_INVALID_BIT_IN_HEADER"/>
+         <label data="5014" alias="DIAMETER_INVALID_AVP_LENGTH"/>
+         <label data="5015" alias="DIAMETER_INVALID_MESSAGE_LENGTH"/>
+         <label data="5016" alias="DIAMETER_INVALID_AVP_BIT_COMBO"/>
+         <label data="5017" alias="DIAMETER_NO_COMMON_SECURITY"/>
+         <label data="5030" alias="DIAMETER_USER_UNKNOWN"/>
+         <label data="5063" alias="REQUESTED_SERVICE_NOT_AUTHORIZED"/>
+         <label data="5065" alias="IP_CAN_SESSION_NOT_AVAILABLE"/>
+      </single>
+   </avp>
+   <avp name="Product-Name" code="269" may-encrypt="yes" v-bit="mustnot" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Session-Binding" code="270" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Session-Server-Failover" code="271" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-3">
+         <label data="0" alias="REFUSE_SERVICE"/>
+         <label data="1" alias="TRY_AGAIN"/>
+         <label data="2" alias="ALLOW_SERVICE"/>
+         <label data="3" alias="TRY_AGAIN_ALLOW_SERVICE"/>
+      </single>
+   </avp>
+   <avp name="Multi-Round-Time-Out" code="272" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Disconnect-Cause" code="273" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-2">
+         <label data="0" alias="REBOOTING"/>
+         <label data="1" alias="BUSY"/>
+         <label data="2" alias="DO_NOT_WANT_TO_TALK_TO_YOU"/>
+      </single>
+   </avp>
+   <avp name="Auth-Request-Type" code="274" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="1-3">
+         <label data="1" alias="AUTHENTICATE_ONLY"/>
+         <label data="2" alias="AUTHORIZE_ONLY"/>
+         <label data="3" alias="AUTHORIZE_AUTHENTICATE"/>
+      </single>
+   </avp>
+   <avp name="Auth-Grace-Period" code="276" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Auth-Session-State" code="277" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="STATE_MAINTAINED"/>
+         <label data="1" alias="NO_STATE_MAINTAINED"/>
+      </single>
+   </avp>
+   <avp name="Origin-State-Id" code="278" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Failed-AVP" code="279" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="AVP" type="Mandatory" qual="1*"/>
+      </grouped>
+   </avp>
+   <avp name="Proxy-Host" code="280" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Error-Message" code="281" may-encrypt="yes" v-bit="mustnot" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Route-Record" code="282" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Destination-Realm" code="283" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Proxy-Info" code="284" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Proxy-Host" type="Mandatory"/>
+         <avprule id="Proxy-State" type="Mandatory"/>
+         <avprule id="AVP" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="Re-Auth-Request-Type" code="285" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="AUTHORIZE_ONLY"/>
+         <label data="1" alias="AUTHORIZE_AUTHENTICATE"/>
+      </single>
+   </avp>
+   <avp name="Accounting-Sub-Session-Id" code="287" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned64"/>
+   </avp>
+   <avp name="Redirect-Host" code="292" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterURI"/>
+   </avp>
+   <avp name="Destination-Host" code="293" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Error-Reporting-Host" code="294" may-encrypt="yes" v-bit="mustnot" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Termination-Cause" code="295" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="1-8">
+         <label data="1" alias="LOGOUT"/>
+         <label data="2" alias="SERVICE_NOT_PROVIDED"/>
+         <label data="3" alias="BAD_ANSWER"/>
+         <label data="4" alias="ADMINISTRATIVE"/>
+         <label data="5" alias="LINK_BROKEN"/>
+         <label data="6" alias="AUTH_EXPIRED"/>
+         <label data="7" alias="USER_MOVED"/>
+         <label data="8" alias="SESSION_TIMEOUT"/>
+      </single>
+   </avp>
+   <avp name="Origin-Realm" code="296" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Experimental-Result" code="297" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Vendor-Id" type="Mandatory"/>
+         <avprule id="Experimental-Result-Code" type="Mandatory"/>
+      </grouped>
+   </avp>
+   <avp name="Experimental-Result-Code" code="298" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Inband-Security-Id" code="299" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="E2E-Sequence" code="300" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="AVP" type="Mandatory" qual="2*"/>
+      </grouped>
+   </avp>
+   <avp name="CC-Input-Octets" code="412" may-encrypt="yes" v-bit="mustnot" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Unsigned64"/>
+   </avp>
+   <avp name="CC-Output-Octets" code="414" may-encrypt="yes" v-bit="mustnot" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Unsigned64"/>
+   </avp>
+   <avp name="CC-Request-Number" code="415" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="CC-Request-Type" code="416" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="1-4">
+         <label data="1" alias="INITIAL_REQUEST"/>
+         <label data="2" alias="UPDATE_REQUEST"/>
+         <label data="3" alias="TERMINATION_REQUEST"/>
+         <label data="4" alias="EVENT_REQUEST"/>
+      </single>
+   </avp>
+   <avp name="CC-Time" code="420" may-encrypt="yes" v-bit="mustnot" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="CC-Total-Octets" code="421" may-encrypt="yes" v-bit="mustnot" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Unsigned64"/>
+   </avp>
+   <avp name="Granted-Service-Unit" code="431" may-encrypt="yes" v-bit="mustnot" m-bit="mustnot" p-bit="mustnot">
+      <grouped>
+         <avprule id="CC-Total-Octets" type="Optional"/>
+         <avprule id="CC-Time" type="Optional"/>
+         <avprule id="CC-Input-Octets" type="Optional"/>
+         <avprule id="CC-Output-Octets" type="Optional"/>
+         <avprule id="AVP" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="Rating-Group" code="432" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Redirect-Address-Type" code="433" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-3">
+         <label data="0" alias="IPv4"/>
+         <label data="1" alias="IPv6Addr"/>
+         <label data="2" alias="URL"/>
+         <label data="3" alias="SIP"/>
+      </single>
+   </avp>
+   <avp name="Redirect-Server-Address" code="435" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Service-Identifier" code="439" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Subscription-Id" code="443" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Subscription-Id-Type" type="Mandatory"/>
+         <avprule id="Subscription-Id-Data" type="Mandatory"/>
+      </grouped>
+   </avp>
+   <avp name="Subscription-Id-Data" code="444" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Used-Service-Unit" code="446" may-encrypt="yes" v-bit="mustnot" m-bit="mustnot" p-bit="mustnot">
+      <grouped>
+         <avprule id="CC-Total-Octets" type="Optional"/>
+         <avprule id="CC-Time" type="Optional"/>
+         <avprule id="CC-Input-Octets" type="Optional"/>
+         <avprule id="CC-Output-Octets" type="Optional"/>
+         <avprule id="AVP" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="Subscription-Id-Type" code="450" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-4">
+         <label data="0" alias="END_USER_E164"/>
+         <label data="1" alias="END_USER_IMSI"/>
+         <label data="2" alias="END_USER_SIP_URI"/>
+         <label data="3" alias="END_USER_NAI"/>
+         <label data="4" alias="END_USER_PRIVATE"/>
+      </single>
+   </avp>
+   <avp name="User-Equipment-Info" code="458" may-encrypt="yes" v-bit="mustnot" m-bit="may" p-bit="mustnot">
+      <grouped>
+         <avprule id="User-Equipment-Info-Type" type="Mandatory"/>
+         <avprule id="User-Equipment-Info-Value" type="Mandatory"/>
+      </grouped>
+   </avp>
+   <avp name="User-Equipment-Info-Type" code="459" may-encrypt="yes" v-bit="mustnot" m-bit="may" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-3">
+         <label data="0" alias="IMEISV"/>
+         <label data="1" alias="MAC"/>
+         <label data="2" alias="EUI64"/>
+         <label data="3" alias="MODIFIED_EUI64"/>
+      </single>
+   </avp>
+   <avp name="User-Equipment-Info-Value" code="460" may-encrypt="yes" v-bit="mustnot" m-bit="may" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Accounting-Record-Type" code="480" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="1-4">
+         <label data="1" alias="EVENT_RECORD"/>
+         <label data="2" alias="START_RECORD"/>
+         <label data="3" alias="INTERIM_RECORD"/>
+         <label data="4" alias="STOP_RECORD"/>
+      </single>
+   </avp>
+   <avp name="Accounting-Realtime-Required" code="483" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="1-3">
+         <label data="1" alias="DELIVER_AND_GRANT"/>
+         <label data="2" alias="GRANT_AND_STORE"/>
+         <label data="3" alias="GRANT_AND_LOSE"/>
+      </single>
+   </avp>
+   <avp name="Accounting-Record-Number" code="485" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Rule-Space-Suggestion" code="290" vendor-name="ERICSSON" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Rule-Space-Decision" code="291" vendor-name="ERICSSON" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Charging-Rule-Authorization" code="1055" vendor-name="ERICSSON" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Authorization-State" type="Mandatory"/>
+         <avprule id="Authorization-State-Change-Time" type="Optional"/>
+         <avprule id="Next-Authorization-State" type="Optional"/>
+         <avprule id="One-Time-Redirect-Control" type="Optional"/>
+      </grouped>
+   </avp>
+   <avp name="Authorization-State" code="1056" vendor-name="ERICSSON" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-12">
+         <label data="0" alias="AUTHORIZED"/>
+         <label data="1" alias="DENIED_CALENDAR_TIME"/>
+         <label data="2" alias="DENIED_ROAMING"/>
+         <label data="3" alias="DENIED_QUALITY_OF_SERVICE"/>
+         <label data="4" alias="DENIED_BLACKLISTED"/>
+         <label data="5" alias="DENIED_TERMINAL"/>
+         <label data="6" alias="DENIED_OPERATOR_REASON_ONE"/>
+         <label data="7" alias="DENIED_OPERATOR_REASON_TWO"/>
+         <label data="8" alias="DENIED_OPERATOR_REASON_THREE"/>
+         <label data="9" alias="DENIED_OPERATOR_REASON_FOUR"/>
+         <label data="10" alias="DENIED_OPERATOR_REASON_FIVE"/>
+         <label data="11" alias="DENIED_UNKNOWN_REASON"/>
+         <label data="12" alias="DENIED_USAGE_CONTROL"/>
+      </single>
+   </avp>
+   <avp name="Gx-Capability-List" code="1060" vendor-name="ERICSSON" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Content-Filtering-Profile-Id" code="1138" vendor-name="ERICSSON" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Customer-Id" code="1146" vendor-name="ERICSSON" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="One-Time-Redirect-Control" code="1193" vendor-name="ERICSSON" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-10">
+         <label data="0" alias="INACTIVE"/>
+         <label data="1" alias="DEFAULT"/>
+         <label data="2" alias="ROAMING"/>
+         <label data="3" alias="QBAU"/>
+         <label data="4" alias="TIME"/>
+         <label data="5" alias="OPERATOR_REASON_ONE"/>
+         <label data="6" alias="OPERATOR_REASON_TWO"/>
+         <label data="7" alias="OPERATOR_REASON_THREE"/>
+         <label data="8" alias="OPERATOR_REASON_FOUR"/>
+         <label data="9" alias="OPERATOR_REASON_FIVE"/>
+         <label data="10" alias="OPERATOR_REASON_SIX"/>
+      </single>
+   </avp>
+   <avp name="3GPP2-BSID" code="9010" vendor-name="3GPP2" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="3GPP-SGSN-Address" code="6" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="3GPP-Charging-Characteristics" code="13" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="3GPP-SGSN-IPv6-Address" code="15" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="3GPP-SGSN-MCC-MNC" code="18" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="3GPP-RAT-Type" code="21" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="3GPP-User-Location-Info" code="22" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="3GPP-MS-TimeZone" code="23" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Access-Network-Charging-Address" code="501" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Address"/>
+   </avp>
+   <avp name="Access-Network-Charging-Identifier-Value" code="503" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="AF-Charging-Identifier" code="505" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Flow-Description" code="507" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="IPFilterRule"/>
+   </avp>
+   <avp name="Flow-Number" code="509" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Flows" code="510" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Media-Component-Number" type="Mandatory"/>
+         <avprule id="Flow-Number" type="Optional" qual="*"/>
+         <avprule id="AVP" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="Flow-Status" code="511" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-4">
+         <label data="0" alias="ENABLED-UPLINK"/>
+         <label data="1" alias="ENABLED-DOWNLINK"/>
+         <label data="2" alias="ENABLED"/>
+         <label data="3" alias="DISABLED"/>
+         <label data="4" alias="REMOVED"/>
+      </single>
+   </avp>
+   <avp name="Max-Requested-Bandwidth-DL" code="515" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Max-Requested-Bandwidth-UL" code="516" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Media-Component-Number" code="518" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Required-Access-Info" code="536" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="USER_LOCATION"/>
+         <label data="1" alias="MS_TIME_ZONE"/>
+      </single>
+   </avp>
+   <avp name="Charging-Information" code="618" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Primary-Event-Charging-Function-Name" type="Optional"/>
+         <avprule id="Secondary-Event-Charging-Function-Name" type="Optional"/>
+         <avprule id="Primary-Charging-Collection-Function-Name" type="Optional"/>
+         <avprule id="Secondary-Charging-Collection-Function-Name" type="Optional"/>
+         <avprule id="AVP" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="Primary-Event-Charging-Function-Name" code="619" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterURI"/>
+   </avp>
+   <avp name="Secondary-Event-Charging-Function-Name" code="620" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterURI"/>
+   </avp>
+   <avp name="Primary-Charging-Collection-Function-Name" code="621" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterURI"/>
+   </avp>
+   <avp name="Secondary-Charging-Collection-Function-Name" code="622" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterURI"/>
+   </avp>
+   <avp name="Supported-Features" code="628" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="may" p-bit="mustnot">
+      <grouped>
+         <avprule id="Vendor-Id" type="Mandatory"/>
+         <avprule id="Feature-List-ID" type="Mandatory"/>
+         <avprule id="Feature-List" type="Mandatory"/>
+         <avprule id="AVP" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="Feature-List-ID" code="629" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Feature-List" code="630" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="RAI" code="909" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Bearer-Usage" code="1000" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="GENERAL"/>
+         <label data="1" alias="IMS_SIGNALLING"/>
+      </single>
+   </avp>
+   <avp name="Charging-Rule-Install" code="1001" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Charging-Rule-Definition" type="Optional" qual="*"/>
+         <avprule id="Charging-Rule-Name" type="Optional" qual="*"/>
+         <avprule id="Charging-Rule-Base-Name" type="Optional" qual="*"/>
+         <avprule id="Charging-Rule-Authorization" type="Optional" qual="*"/>
+         <avprule id="Bearer-Identifier" type="Optional"/>
+         <avprule id="Rule-Activation-Time" type="Optional"/>
+         <avprule id="Rule-DeActivation-Time" type="Optional"/>
+         <avprule id="Resource-Allocation-Notification" type="Optional"/>
+         <avprule id="AVP" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="Charging-Rule-Remove" code="1002" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Charging-Rule-Name" type="Optional" qual="*"/>
+         <avprule id="Charging-Rule-Base-Name" type="Optional" qual="*"/>
+         <avprule id="Required-Access-Info" type="Optional" qual="*"/>
+         <avprule id="AVP" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="Charging-Rule-Definition" code="1003" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Charging-Rule-Name" type="Mandatory"/>
+         <avprule id="Service-Identifier" type="Optional"/>
+         <avprule id="Rating-Group" type="Optional"/>
+         <avprule id="Flow-Information" type="Optional" qual="*"/>
+         <avprule id="Flow-Status" type="Optional"/>
+         <avprule id="QoS-Information" type="Optional"/>
+         <avprule id="Reporting-Level" type="Optional"/>
+         <avprule id="Online" type="Optional"/>
+         <avprule id="Offline" type="Optional"/>
+         <avprule id="Metering-Method" type="Optional"/>
+         <avprule id="Precedence" type="Optional"/>
+         <avprule id="AF-Charging-Identifier" type="Optional"/>
+         <avprule id="Flows" type="Optional" qual="*"/>
+         <avprule id="Monitoring-Key" type="Optional"/>
+         <avprule id="Required-Access-Info" type="Optional" qual="*"/>
+         <avprule id="TDF-Application-Identifier" type="Optional"/>
+         <avprule id="Redirect-Information" type="Optional"/>
+         <avprule id="Mute-Notification" type="Optional"/>
+         <avprule id="AVP" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="Charging-Rule-Base-Name" code="1004" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Charging-Rule-Name" code="1005" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Event-Trigger" code="1006" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-2,4-4,7-7,12-13,15-17,20-22,25-27,29-29,33-34,39-40,45-45,48">
+         <label data="0" alias="SGSN_CHANGE"/>
+         <label data="1" alias="QOS_CHANGE"/>
+         <label data="2" alias="RAT_CHANGE"/>
+         <label data="4" alias="PLMN_CHANGE"/>
+         <label data="7" alias="IP-CAN-CHANGE"/>
+         <label data="12" alias="RAI_CHANGE"/>
+         <label data="13" alias="USER_LOCATION_CHANGE"/>
+         <label data="15" alias="OUT_OF_CREDIT"/>
+         <label data="16" alias="REALLOCATION_OF_CREDIT"/>
+         <label data="17" alias="REVALIDATION_TIMEOUT"/>
+         <label data="20" alias="DEFAULT_EPS_BEARER_QOS_CHANGE"/>
+         <label data="21" alias="AN_GW_CHANGE"/>
+         <label data="22" alias="SUCCESSFUL_RESOURCE_ALLOCATION"/>
+         <label data="25" alias="UE_TIME_ZONE_CHANGE"/>
+         <label data="26" alias="TAI_CHANGE"/>
+         <label data="27" alias="ECGI_CHANGE"/>
+         <label data="29" alias="APN-AMBR_MODIFICATION_FAILURE"/>
+         <label data="33" alias="USAGE_REPORT"/>
+         <label data="34" alias="DEFAULT-EPS-BEARER-QOS_MODIFICATION_FAILURE"/>
+         <label data="39" alias="APPLICATION_START"/>
+         <label data="40" alias="APPLICATION_STOP"/>
+         <label data="45" alias="ACCESS_NETWORK_INFO_REPORT"/>
+         <label data="48" alias="CHANGE_OF_UE_PRESENCE_IN_PRESENCE_REPORTING_AREA_REPORT"/>
+      </single>
+   </avp>
+   <avp name="Metering-Method" code="1007" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-2">
+         <label data="0" alias="DURATION"/>
+         <label data="1" alias="VOLUME"/>
+         <label data="2" alias="DURATION_VOLUME"/>
+      </single>
+   </avp>
+   <avp name="Offline" code="1008" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="DISABLE_OFFLINE"/>
+         <label data="1" alias="ENABLE_OFFLINE"/>
+      </single>
+   </avp>
+   <avp name="Online" code="1009" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="DISABLE_ONLINE"/>
+         <label data="1" alias="ENABLE_ONLINE"/>
+      </single>
+   </avp>
+   <avp name="Precedence" code="1010" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Reporting-Level" code="1011" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="SERVICE_IDENTIFIER_LEVEL"/>
+         <label data="1" alias="RATING_GROUP_LEVEL"/>
+      </single>
+   </avp>
+   <avp name="ToS-Traffic-Class" code="1014" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="QoS-Information" code="1016" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="QoS-Class-Identifier" type="Optional"/>
+         <avprule id="Max-Requested-Bandwidth-UL" type="Optional"/>
+         <avprule id="Max-Requested-Bandwidth-DL" type="Optional"/>
+         <avprule id="Guaranteed-Bitrate-UL" type="Optional"/>
+         <avprule id="Guaranteed-Bitrate-DL" type="Optional"/>
+         <avprule id="Bearer-Identifier" type="Optional"/>
+         <avprule id="Allocation-Retention-Priority" type="Optional"/>
+         <avprule id="APN-Aggregate-Max-Bitrate-UL" type="Optional"/>
+         <avprule id="APN-Aggregate-Max-Bitrate-DL" type="Optional"/>
+         <avprule id="AVP" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="Charging-Rule-Report" code="1018" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Charging-Rule-Name" type="Optional" qual="*"/>
+         <avprule id="Charging-Rule-Base-Name" type="Optional" qual="*"/>
+         <avprule id="PCC-Rule-Status" type="Optional"/>
+         <avprule id="Rule-Failure-Code" type="Optional"/>
+         <avprule id="AVP" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="PCC-Rule-Status" code="1019" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-2">
+         <label data="0" alias="ACTIVE"/>
+         <label data="1" alias="INACTIVE"/>
+         <label data="2" alias="TEMPORARILY_INACTIVE"/>
+      </single>
+   </avp>
+   <avp name="Bearer-Identifier" code="1020" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Bearer-Operation" code="1021" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-2">
+         <label data="0" alias="TERMINATION"/>
+         <label data="1" alias="ESTABLISHMENT"/>
+         <label data="2" alias="MODIFICATION"/>
+      </single>
+   </avp>
+   <avp name="Access-Network-Charging-Identifier-Gx" code="1022" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Access-Network-Charging-Identifier-Value" type="Mandatory"/>
+         <avprule id="Charging-Rule-Base-Name" type="Optional" qual="*"/>
+         <avprule id="Charging-Rule-Name" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="Bearer-Control-Mode" code="1023" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-2">
+         <label data="0" alias="UE_ONLY"/>
+         <label data="1" alias="RESERVED"/>
+         <label data="2" alias="UE_NW"/>
+      </single>
+   </avp>
+   <avp name="Network-Request-Support" code="1024" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="NETWORK_REQUEST_NOT_SUPPORTED"/>
+         <label data="1" alias="NETWORK_REQUEST_SUPPORTED"/>
+      </single>
+   </avp>
+   <avp name="Guaranteed-Bitrate-DL" code="1025" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Guaranteed-Bitrate-UL" code="1026" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="IP-CAN-Type" code="1027" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-7">
+         <label data="0" alias="3GPP-GPRS"/>
+         <label data="1" alias="DOCSIS"/>
+         <label data="2" alias="xDSL"/>
+         <label data="3" alias="WiMAX"/>
+         <label data="4" alias="3GPP2"/>
+         <label data="5" alias="3GPP-EPS"/>
+         <label data="6" alias="Non-3GPP-EPS"/>
+         <label data="7" alias="FBA"/>
+      </single>
+   </avp>
+   <avp name="QoS-Class-Identifier" code="1028" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="1-254">
+         <label data="1" alias="QCI_1"/>
+         <label data="2" alias="QCI_2"/>
+         <label data="3" alias="QCI_3"/>
+         <label data="4" alias="QCI_4"/>
+         <label data="5" alias="QCI_5"/>
+         <label data="6" alias="QCI_6"/>
+         <label data="7" alias="QCI_7"/>
+         <label data="8" alias="QCI_8"/>
+         <label data="9" alias="QCI_9"/>
+         <label data="10" alias="QCI_10"/>
+         <label data="11" alias="QCI_11"/>
+         <label data="12" alias="QCI_12"/>
+         <label data="13" alias="QCI_13"/>
+         <label data="14" alias="QCI_14"/>
+         <label data="15" alias="QCI_15"/>
+         <label data="16" alias="QCI_16"/>
+         <label data="17" alias="QCI_17"/>
+         <label data="18" alias="QCI_18"/>
+         <label data="19" alias="QCI_19"/>
+         <label data="20" alias="QCI_20"/>
+         <label data="21" alias="QCI_21"/>
+         <label data="22" alias="QCI_22"/>
+         <label data="23" alias="QCI_23"/>
+         <label data="24" alias="QCI_24"/>
+         <label data="25" alias="QCI_25"/>
+         <label data="26" alias="QCI_26"/>
+         <label data="27" alias="QCI_27"/>
+         <label data="28" alias="QCI_28"/>
+         <label data="29" alias="QCI_29"/>
+         <label data="30" alias="QCI_30"/>
+         <label data="31" alias="QCI_31"/>
+         <label data="32" alias="QCI_32"/>
+         <label data="33" alias="QCI_33"/>
+         <label data="34" alias="QCI_34"/>
+         <label data="35" alias="QCI_35"/>
+         <label data="36" alias="QCI_36"/>
+         <label data="37" alias="QCI_37"/>
+         <label data="38" alias="QCI_38"/>
+         <label data="39" alias="QCI_39"/>
+         <label data="40" alias="QCI_40"/>
+         <label data="41" alias="QCI_41"/>
+         <label data="42" alias="QCI_42"/>
+         <label data="43" alias="QCI_43"/>
+         <label data="44" alias="QCI_44"/>
+         <label data="45" alias="QCI_45"/>
+         <label data="46" alias="QCI_46"/>
+         <label data="47" alias="QCI_47"/>
+         <label data="48" alias="QCI_48"/>
+         <label data="49" alias="QCI_49"/>
+         <label data="50" alias="QCI_50"/>
+         <label data="51" alias="QCI_51"/>
+         <label data="52" alias="QCI_52"/>
+         <label data="53" alias="QCI_53"/>
+         <label data="54" alias="QCI_54"/>
+         <label data="55" alias="QCI_55"/>
+         <label data="56" alias="QCI_56"/>
+         <label data="57" alias="QCI_57"/>
+         <label data="58" alias="QCI_58"/>
+         <label data="59" alias="QCI_59"/>
+         <label data="60" alias="QCI_60"/>
+         <label data="61" alias="QCI_61"/>
+         <label data="62" alias="QCI_62"/>
+         <label data="63" alias="QCI_63"/>
+         <label data="64" alias="QCI_64"/>
+         <label data="65" alias="QCI_65"/>
+         <label data="66" alias="QCI_66"/>
+         <label data="67" alias="QCI_67"/>
+         <label data="68" alias="QCI_68"/>
+         <label data="69" alias="QCI_69"/>
+         <label data="70" alias="QCI_70"/>
+         <label data="71" alias="QCI_71"/>
+         <label data="72" alias="QCI_72"/>
+         <label data="73" alias="QCI_73"/>
+         <label data="74" alias="QCI_74"/>
+         <label data="75" alias="QCI_75"/>
+         <label data="76" alias="QCI_76"/>
+         <label data="77" alias="QCI_77"/>
+         <label data="78" alias="QCI_78"/>
+         <label data="79" alias="QCI_79"/>
+         <label data="80" alias="QCI_80"/>
+         <label data="81" alias="QCI_81"/>
+         <label data="82" alias="QCI_82"/>
+         <label data="83" alias="QCI_83"/>
+         <label data="84" alias="QCI_84"/>
+         <label data="85" alias="QCI_85"/>
+         <label data="86" alias="QCI_86"/>
+         <label data="87" alias="QCI_87"/>
+         <label data="88" alias="QCI_88"/>
+         <label data="89" alias="QCI_89"/>
+         <label data="90" alias="QCI_90"/>
+         <label data="91" alias="QCI_91"/>
+         <label data="92" alias="QCI_92"/>
+         <label data="93" alias="QCI_93"/>
+         <label data="94" alias="QCI_94"/>
+         <label data="95" alias="QCI_95"/>
+         <label data="96" alias="QCI_96"/>
+         <label data="97" alias="QCI_97"/>
+         <label data="98" alias="QCI_98"/>
+         <label data="99" alias="QCI_99"/>
+         <label data="100" alias="QCI_100"/>
+         <label data="101" alias="QCI_101"/>
+         <label data="102" alias="QCI_102"/>
+         <label data="103" alias="QCI_103"/>
+         <label data="104" alias="QCI_104"/>
+         <label data="105" alias="QCI_105"/>
+         <label data="106" alias="QCI_106"/>
+         <label data="107" alias="QCI_107"/>
+         <label data="108" alias="QCI_108"/>
+         <label data="109" alias="QCI_109"/>
+         <label data="110" alias="QCI_110"/>
+         <label data="111" alias="QCI_111"/>
+         <label data="112" alias="QCI_112"/>
+         <label data="113" alias="QCI_113"/>
+         <label data="114" alias="QCI_114"/>
+         <label data="115" alias="QCI_115"/>
+         <label data="116" alias="QCI_116"/>
+         <label data="117" alias="QCI_117"/>
+         <label data="118" alias="QCI_118"/>
+         <label data="119" alias="QCI_119"/>
+         <label data="120" alias="QCI_120"/>
+         <label data="121" alias="QCI_121"/>
+         <label data="122" alias="QCI_122"/>
+         <label data="123" alias="QCI_123"/>
+         <label data="124" alias="QCI_124"/>
+         <label data="125" alias="QCI_125"/>
+         <label data="126" alias="QCI_126"/>
+         <label data="127" alias="QCI_127"/>
+         <label data="128" alias="QCI_128"/>
+         <label data="129" alias="QCI_129"/>
+         <label data="130" alias="QCI_130"/>
+         <label data="131" alias="QCI_131"/>
+         <label data="132" alias="QCI_132"/>
+         <label data="133" alias="QCI_133"/>
+         <label data="134" alias="QCI_134"/>
+         <label data="135" alias="QCI_135"/>
+         <label data="136" alias="QCI_136"/>
+         <label data="137" alias="QCI_137"/>
+         <label data="138" alias="QCI_138"/>
+         <label data="139" alias="QCI_139"/>
+         <label data="140" alias="QCI_140"/>
+         <label data="141" alias="QCI_141"/>
+         <label data="142" alias="QCI_142"/>
+         <label data="143" alias="QCI_143"/>
+         <label data="144" alias="QCI_144"/>
+         <label data="145" alias="QCI_145"/>
+         <label data="146" alias="QCI_146"/>
+         <label data="147" alias="QCI_147"/>
+         <label data="148" alias="QCI_148"/>
+         <label data="149" alias="QCI_149"/>
+         <label data="150" alias="QCI_150"/>
+         <label data="151" alias="QCI_151"/>
+         <label data="152" alias="QCI_152"/>
+         <label data="153" alias="QCI_153"/>
+         <label data="154" alias="QCI_154"/>
+         <label data="155" alias="QCI_155"/>
+         <label data="156" alias="QCI_156"/>
+         <label data="157" alias="QCI_157"/>
+         <label data="158" alias="QCI_158"/>
+         <label data="159" alias="QCI_159"/>
+         <label data="160" alias="QCI_160"/>
+         <label data="161" alias="QCI_161"/>
+         <label data="162" alias="QCI_162"/>
+         <label data="163" alias="QCI_163"/>
+         <label data="164" alias="QCI_164"/>
+         <label data="165" alias="QCI_165"/>
+         <label data="166" alias="QCI_166"/>
+         <label data="167" alias="QCI_167"/>
+         <label data="168" alias="QCI_168"/>
+         <label data="169" alias="QCI_169"/>
+         <label data="170" alias="QCI_170"/>
+         <label data="171" alias="QCI_171"/>
+         <label data="172" alias="QCI_172"/>
+         <label data="173" alias="QCI_173"/>
+         <label data="174" alias="QCI_174"/>
+         <label data="175" alias="QCI_175"/>
+         <label data="176" alias="QCI_176"/>
+         <label data="177" alias="QCI_177"/>
+         <label data="178" alias="QCI_178"/>
+         <label data="179" alias="QCI_179"/>
+         <label data="180" alias="QCI_180"/>
+         <label data="181" alias="QCI_181"/>
+         <label data="182" alias="QCI_182"/>
+         <label data="183" alias="QCI_183"/>
+         <label data="184" alias="QCI_184"/>
+         <label data="185" alias="QCI_185"/>
+         <label data="186" alias="QCI_186"/>
+         <label data="187" alias="QCI_187"/>
+         <label data="188" alias="QCI_188"/>
+         <label data="189" alias="QCI_189"/>
+         <label data="190" alias="QCI_190"/>
+         <label data="191" alias="QCI_191"/>
+         <label data="192" alias="QCI_192"/>
+         <label data="193" alias="QCI_193"/>
+         <label data="194" alias="QCI_194"/>
+         <label data="195" alias="QCI_195"/>
+         <label data="196" alias="QCI_196"/>
+         <label data="197" alias="QCI_197"/>
+         <label data="198" alias="QCI_198"/>
+         <label data="199" alias="QCI_199"/>
+         <label data="200" alias="QCI_200"/>
+         <label data="201" alias="QCI_201"/>
+         <label data="202" alias="QCI_202"/>
+         <label data="203" alias="QCI_203"/>
+         <label data="204" alias="QCI_204"/>
+         <label data="205" alias="QCI_205"/>
+         <label data="206" alias="QCI_206"/>
+         <label data="207" alias="QCI_207"/>
+         <label data="208" alias="QCI_208"/>
+         <label data="209" alias="QCI_209"/>
+         <label data="210" alias="QCI_210"/>
+         <label data="211" alias="QCI_211"/>
+         <label data="212" alias="QCI_212"/>
+         <label data="213" alias="QCI_213"/>
+         <label data="214" alias="QCI_214"/>
+         <label data="215" alias="QCI_215"/>
+         <label data="216" alias="QCI_216"/>
+         <label data="217" alias="QCI_217"/>
+         <label data="218" alias="QCI_218"/>
+         <label data="219" alias="QCI_219"/>
+         <label data="220" alias="QCI_220"/>
+         <label data="221" alias="QCI_221"/>
+         <label data="222" alias="QCI_222"/>
+         <label data="223" alias="QCI_223"/>
+         <label data="224" alias="QCI_224"/>
+         <label data="225" alias="QCI_225"/>
+         <label data="226" alias="QCI_226"/>
+         <label data="227" alias="QCI_227"/>
+         <label data="228" alias="QCI_228"/>
+         <label data="229" alias="QCI_229"/>
+         <label data="230" alias="QCI_230"/>
+         <label data="231" alias="QCI_231"/>
+         <label data="232" alias="QCI_232"/>
+         <label data="233" alias="QCI_233"/>
+         <label data="234" alias="QCI_234"/>
+         <label data="235" alias="QCI_235"/>
+         <label data="236" alias="QCI_236"/>
+         <label data="237" alias="QCI_237"/>
+         <label data="238" alias="QCI_238"/>
+         <label data="239" alias="QCI_239"/>
+         <label data="240" alias="QCI_240"/>
+         <label data="241" alias="QCI_241"/>
+         <label data="242" alias="QCI_242"/>
+         <label data="243" alias="QCI_243"/>
+         <label data="244" alias="QCI_244"/>
+         <label data="245" alias="QCI_245"/>
+         <label data="246" alias="QCI_246"/>
+         <label data="247" alias="QCI_247"/>
+         <label data="248" alias="QCI_248"/>
+         <label data="249" alias="QCI_249"/>
+         <label data="250" alias="QCI_250"/>
+         <label data="251" alias="QCI_251"/>
+         <label data="252" alias="QCI_252"/>
+         <label data="253" alias="QCI_253"/>
+         <label data="254" alias="QCI_254"/>
+      </single>
+   </avp>
+   <avp name="QoS-Negotiation" code="1029" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="NO_QoS_NEGOTIATION"/>
+         <label data="1" alias="QoS_NEGOTIATION_SUPPORTED"/>
+      </single>
+   </avp>
+   <avp name="QoS-Upgrade" code="1030" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="QoS_UPGRADE_NOT_SUPPORTED"/>
+         <label data="1" alias="QoS_UPGRADE_SUPPORTED"/>
+      </single>
+   </avp>
+   <avp name="Rule-Failure-Code" code="1031" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="1-15,18">
+         <label data="1" alias="UNKNOWN_RULE_NAME"/>
+         <label data="2" alias="RATING_GROUP_ERROR"/>
+         <label data="3" alias="SERVICE_IDENTIFIER_ERROR"/>
+         <label data="4" alias="GWPCEF_MALFUNCTION"/>
+         <label data="5" alias="RESOURCES_LIMITATION"/>
+         <label data="6" alias="MAX_NR_BEARERS_REACHED"/>
+         <label data="7" alias="UNKNOWN_BEARER_ID"/>
+         <label data="8" alias="MISSING_BEARER_ID"/>
+         <label data="9" alias="MISSING_FLOW_INFORMATION"/>
+         <label data="10" alias="RESOURCE_ALLOCATION_FAILURE"/>
+         <label data="11" alias="UNSUCCESSFUL_QOS_VALIDATION"/>
+         <label data="12" alias="INCORRECT_FLOW_INFORMATION"/>
+         <label data="13" alias="PS_TO_CS_HANDOVER"/>
+         <label data="14" alias="TDF_APPLICATION_IDENTIFIER_ERROR"/>
+         <label data="15" alias="NO_BEARER_BOUND"/>
+         <label data="18" alias="MISSING_REDIRECT_SERVER_ADDRESS"/>
+      </single>
+   </avp>
+   <avp name="RAT-Type" code="1032" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1,1000-1004,2000-2003">
+         <label data="0" alias="WLAN"/>
+         <label data="1" alias="VIRTUAL"/>
+         <label data="1000" alias="UTRAN"/>
+         <label data="1001" alias="GERAN"/>
+         <label data="1002" alias="GAN"/>
+         <label data="1003" alias="HSPA_EVOLUTION"/>
+         <label data="1004" alias="EUTRAN"/>
+         <label data="2000" alias="CDMA2000_1X"/>
+         <label data="2001" alias="HRPD"/>
+         <label data="2002" alias="UMB"/>
+         <label data="2003" alias="EHRPD"/>
+      </single>
+   </avp>
+   <avp name="Allocation-Retention-Priority" code="1034" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <grouped>
+         <avprule id="Priority-Level" type="Mandatory"/>
+         <avprule id="Pre-emption-Capability" type="Optional"/>
+         <avprule id="Pre-emption-Vulnerability" type="Optional"/>
+      </grouped>
+   </avp>
+   <avp name="APN-Aggregate-Max-Bitrate-DL" code="1040" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="APN-Aggregate-Max-Bitrate-UL" code="1041" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Revalidation-Time" code="1042" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Rule-Activation-Time" code="1043" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Rule-DeActivation-Time" code="1044" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Session-Release-Cause" code="1045" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="UNSPECIFIED_REASON"/>
+         <label data="1" alias="UE_SUBSCRIPTION_REASON"/>
+      </single>
+   </avp>
+   <avp name="Priority-Level" code="1046" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Pre-emption-Capability" code="1047" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="PRE-EMPTION_CAPABILITY_ENABLED"/>
+         <label data="1" alias="PRE-EMPTION_CAPABILITY_DISABLED"/>
+      </single>
+   </avp>
+   <avp name="Pre-emption-Vulnerability" code="1048" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="PRE-EMPTION_VULNERABILITY_ENABLED"/>
+         <label data="1" alias="PRE-EMPTION_VULNERABILITY_DISABLED"/>
+      </single>
+   </avp>
+   <avp name="Default-EPS-Bearer-QoS" code="1049" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <grouped>
+         <avprule id="QoS-Class-Identifier" type="Optional"/>
+         <avprule id="Allocation-Retention-Priority" type="Optional"/>
+         <avprule id="AVP" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="AN-GW-Address" code="1050" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Address"/>
+   </avp>
+   <avp name="Authorization-State-Change-Time" code="1057" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Time"/>
+   </avp>
+   <avp name="Flow-Information" code="1058" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <grouped>
+         <avprule id="Flow-Description" type="Optional"/>
+         <avprule id="Flow-Direction" type="Optional"/>
+         <avprule id="AVP" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="Next-Authorization-State" code="1059" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="AUTHORIZED"/>
+         <label data="1" alias="DENIED_CALENDAR_TIME"/>
+      </single>
+   </avp>
+   <avp name="Resource-Allocation-Notification" code="1063" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0">
+         <label data="0" alias="ENABLE_NOTIFICATION"/>
+      </single>
+   </avp>
+   <avp name="Monitoring-Key" code="1066" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Usage-Monitoring-Information" code="1067" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <grouped>
+         <avprule id="Monitoring-Key" type="Optional"/>
+         <avprule id="Granted-Service-Unit" type="Optional"/>
+         <avprule id="Used-Service-Unit" type="Optional"/>
+         <avprule id="Usage-Monitoring-Level" type="Optional"/>
+         <avprule id="Usage-Monitoring-Report" type="Optional"/>
+         <avprule id="Usage-Monitoring-Support" type="Optional"/>
+         <avprule id="AVP" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="Usage-Monitoring-Level" code="1068" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="SESSION_LEVEL"/>
+         <label data="1" alias="PCC_RULE_LEVEL"/>
+      </single>
+   </avp>
+   <avp name="Usage-Monitoring-Report" code="1069" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0">
+         <label data="0" alias="USAGE_MONITORING_REPORT_REQUIRED"/>
+      </single>
+   </avp>
+   <avp name="Usage-Monitoring-Support" code="1070" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0">
+         <label data="0" alias="USAGE_MONITORING_DISABLED"/>
+      </single>
+   </avp>
+   <avp name="Flow-Direction" code="1080" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-3">
+         <label data="0" alias="UNSPECIFIED"/>
+         <label data="1" alias="DOWNLINK"/>
+         <label data="2" alias="UPLINK"/>
+         <label data="3" alias="BIDIRECTIONAL"/>
+      </single>
+   </avp>
+   <avp name="Redirect-Information" code="1085" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <grouped>
+         <avprule id="Redirect-Support" type="Optional"/>
+         <avprule id="Redirect-Address-Type" type="Optional"/>
+         <avprule id="Redirect-Server-Address" type="Optional"/>
+         <avprule id="AVP" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="Redirect-Support" code="1086" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="REDIRECTION_DISABLED"/>
+         <label data="1" alias="REDIRECTION_ENABLED"/>
+      </single>
+   </avp>
+   <avp name="TDF-Application-Identifier" code="1088" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Application-Detection-Information" code="1098" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <grouped>
+         <avprule id="TDF-Application-Identifier" type="Mandatory"/>
+         <avprule id="TDF-Application-Instance-Identifier" type="Optional"/>
+         <avprule id="Flow-Information" type="Optional" qual="*"/>
+         <avprule id="AVP" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="TDF-Application-Instance-Identifier" code="2802" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Mute-Notification" code="2809" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0">
+         <label data="0" alias="MUTE_REQUIRED"/>
+      </single>
+   </avp>
+   <avp name="User-Location-Info-Time" code="2812" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Time"/>
+   </avp>
+   <avp name="Presence-Reporting-Area-Elements-List" code="2820" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Presence-Reporting-Area-Identifier" code="2821" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Presence-Reporting-Area-Information" code="2822" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <grouped>
+         <avprule id="Presence-Reporting-Area-Identifier" type="Optional"/>
+         <avprule id="Presence-Reporting-Area-Status" type="Optional"/>
+         <avprule id="Presence-Reporting-Area-Elements-List" type="Optional"/>
+         <avprule id="AVP" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="Presence-Reporting-Area-Status" code="2823" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="NetLoc-Access-Support" code="2824" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <command name="CER" code="257" type="Request">
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Host-IP-Address" type="Mandatory" qual="1*"/>
+      <avprule id="Vendor-Id" type="Mandatory"/>
+      <avprule id="Product-Name" type="Mandatory"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Supported-Vendor-Id" type="Optional" qual="*"/>
+      <avprule id="Auth-Application-Id" type="Optional" qual="*"/>
+      <avprule id="Inband-Security-Id" type="Optional" qual="*"/>
+      <avprule id="Acct-Application-Id" type="Optional" qual="*"/>
+      <avprule id="Vendor-Specific-Application-Id" type="Optional" qual="*"/>
+      <avprule id="Firmware-Revision" type="Optional"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="CEA" code="257" type="Answer">
+      <avprule id="Result-Code" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Host-IP-Address" type="Mandatory" qual="1*"/>
+      <avprule id="Vendor-Id" type="Mandatory"/>
+      <avprule id="Product-Name" type="Mandatory"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Error-Message" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional" qual="*"/>
+      <avprule id="Supported-Vendor-Id" type="Optional" qual="*"/>
+      <avprule id="Auth-Application-Id" type="Optional" qual="*"/>
+      <avprule id="Inband-Security-Id" type="Optional" qual="*"/>
+      <avprule id="Acct-Application-Id" type="Optional" qual="*"/>
+      <avprule id="Vendor-Specific-Application-Id" type="Optional" qual="*"/>
+      <avprule id="Firmware-Revision" type="Optional"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="RA-Request" code="258" type="Request">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Auth-Application-Id" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Destination-Realm" type="Mandatory"/>
+      <avprule id="Destination-Host" type="Mandatory"/>
+      <avprule id="Re-Auth-Request-Type" type="Mandatory"/>
+      <avprule id="Session-Release-Cause" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Event-Trigger" type="Optional" qual="*"/>
+      <avprule id="Charging-Rule-Remove" type="Optional" qual="*"/>
+      <avprule id="Charging-Rule-Install" type="Optional" qual="*"/>
+      <avprule id="Default-EPS-Bearer-QoS" type="Optional"/>
+      <avprule id="QoS-Information" type="Optional"/>
+      <avprule id="Revalidation-Time" type="Optional"/>
+      <avprule id="Content-Filtering-Profile-Id" type="Optional"/>
+      <avprule id="Usage-Monitoring-Information" type="Optional" qual="*"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="Route-Record" type="Optional" qual="*"/>
+      <avprule id="Presence-Reporting-Area-Information" type="Optional"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="RA-Answer" code="258" type="Answer">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Result-Code" type="Optional"/>
+      <avprule id="Experimental-Result" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="IP-CAN-Type" type="Optional"/>
+      <avprule id="RAT-Type" type="Optional"/>
+      <avprule id="AN-GW-Address" type="Optional" qual="0*2"/>
+      <avprule id="3GPP-SGSN-MCC-MNC" type="Optional"/>
+      <avprule id="3GPP-SGSN-Address" type="Optional"/>
+      <avprule id="3GPP-SGSN-IPv6-Address" type="Optional"/>
+      <avprule id="RAI" type="Optional"/>
+      <avprule id="3GPP-User-Location-Info" type="Optional"/>
+      <avprule id="3GPP-MS-TimeZone" type="Optional"/>
+      <avprule id="Charging-Rule-Report" type="Optional" qual="*"/>
+      <avprule id="Error-Message" type="Optional"/>
+      <avprule id="Error-Reporting-Host" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional" qual="*"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="NetLoc-Access-Support" type="Optional"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="ACR" code="271" type="Request">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Destination-Realm" type="Mandatory"/>
+      <avprule id="Accounting-Record-Type" type="Mandatory"/>
+      <avprule id="Accounting-Record-Number" type="Mandatory"/>
+      <avprule id="Acct-Application-Id" type="Optional"/>
+      <avprule id="Vendor-Specific-Application-Id" type="Optional"/>
+      <avprule id="User-Name" type="Optional"/>
+      <avprule id="Accounting-Sub-Session-Id" type="Optional"/>
+      <avprule id="Acct-Session-Id" type="Optional"/>
+      <avprule id="Acct-Multi-Session-Id" type="Optional"/>
+      <avprule id="Acct-Interim-Interval" type="Optional"/>
+      <avprule id="Accounting-Realtime-Required" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Event-Timestamp" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="Route-Record" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="ACA" code="271" type="Answer">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Result-Code" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Accounting-Record-Type" type="Mandatory"/>
+      <avprule id="Accounting-Record-Number" type="Mandatory"/>
+      <avprule id="Acct-Application-Id" type="Optional"/>
+      <avprule id="Vendor-Specific-Application-Id" type="Optional"/>
+      <avprule id="User-Name" type="Optional"/>
+      <avprule id="Accounting-Sub-Session-Id" type="Optional"/>
+      <avprule id="Acct-Session-Id" type="Optional"/>
+      <avprule id="Acct-Multi-Session-Id" type="Optional"/>
+      <avprule id="Error-Reporting-Host" type="Optional"/>
+      <avprule id="Acct-Interim-Interval" type="Optional"/>
+      <avprule id="Accounting-Realtime-Required" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Event-Timestamp" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="CC-Request" code="272" type="Request">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Auth-Application-Id" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Destination-Realm" type="Mandatory"/>
+      <avprule id="CC-Request-Type" type="Mandatory"/>
+      <avprule id="CC-Request-Number" type="Mandatory"/>
+      <avprule id="Destination-Host" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Subscription-Id" type="Optional" qual="*"/>
+      <avprule id="Supported-Features" type="Optional" qual="*"/>
+      <avprule id="Network-Request-Support" type="Optional"/>
+      <avprule id="Bearer-Identifier" type="Optional"/>
+      <avprule id="Bearer-Operation" type="Optional"/>
+      <avprule id="Framed-IP-Address" type="Optional"/>
+      <avprule id="Framed-IPv6-Prefix" type="Optional"/>
+      <avprule id="IP-CAN-Type" type="Optional"/>
+      <avprule id="3GPP-RAT-Type" type="Optional"/>
+      <avprule id="RAT-Type" type="Optional"/>
+      <avprule id="Termination-Cause" type="Optional"/>
+      <avprule id="User-Equipment-Info" type="Optional"/>
+      <avprule id="QoS-Information" type="Optional"/>
+      <avprule id="QoS-Negotiation" type="Optional"/>
+      <avprule id="QoS-Upgrade" type="Optional"/>
+      <avprule id="Default-EPS-Bearer-QoS" type="Optional"/>
+      <avprule id="AN-GW-Address" type="Optional" qual="0*2"/>
+      <avprule id="3GPP-SGSN-MCC-MNC" type="Optional"/>
+      <avprule id="3GPP-SGSN-Address" type="Optional"/>
+      <avprule id="3GPP-SGSN-IPv6-Address" type="Optional"/>
+      <avprule id="RAI" type="Optional"/>
+      <avprule id="3GPP-User-Location-Info" type="Optional"/>
+      <avprule id="3GPP-MS-TimeZone" type="Optional"/>
+      <avprule id="User-Location-Info-Time" type="Optional"/>
+      <avprule id="Called-Station-Id" type="Optional"/>
+      <avprule id="Bearer-Usage" type="Optional"/>
+      <avprule id="Online" type="Optional"/>
+      <avprule id="Offline" type="Optional"/>
+      <avprule id="3GPP-Charging-Characteristics" type="Optional"/>
+      <avprule id="Gx-Capability-List" type="Optional"/>
+      <avprule id="Rule-Space-Decision" type="Optional"/>
+      <avprule id="Rule-Space-Suggestion" type="Optional"/>
+      <avprule id="Charging-Rule-Report" type="Optional" qual="*"/>
+      <avprule id="Event-Trigger" type="Optional" qual="*"/>
+      <avprule id="Access-Network-Charging-Address" type="Optional"/>
+      <avprule id="Application-Detection-Information" type="Optional" qual="*"/>
+      <avprule id="Access-Network-Charging-Identifier-Gx" type="Optional" qual="*"/>
+      <avprule id="Usage-Monitoring-Information" type="Optional" qual="*"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="Route-Record" type="Optional" qual="*"/>
+      <avprule id="Presence-Reporting-Area-Information" type="Optional"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="CC-Answer" code="272" type="Answer">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Auth-Application-Id" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="CC-Request-Type" type="Mandatory"/>
+      <avprule id="CC-Request-Number" type="Mandatory"/>
+      <avprule id="Result-Code" type="Optional"/>
+      <avprule id="Experimental-Result" type="Optional"/>
+      <avprule id="Supported-Features" type="Optional" qual="*"/>
+      <avprule id="Bearer-Control-Mode" type="Optional"/>
+      <avprule id="Event-Trigger" type="Optional" qual="*"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Redirect-Host" type="Optional" qual="*"/>
+      <avprule id="Redirect-Host-Usage" type="Optional"/>
+      <avprule id="Redirect-Max-Cache-Time" type="Optional"/>
+      <avprule id="Charging-Rule-Remove" type="Optional" qual="*"/>
+      <avprule id="Charging-Rule-Install" type="Optional" qual="*"/>
+      <avprule id="Charging-Information" type="Optional"/>
+      <avprule id="Online" type="Optional"/>
+      <avprule id="Offline" type="Optional"/>
+      <avprule id="Content-Filtering-Profile-Id" type="Optional"/>
+      <avprule id="3GPP-Charging-Characteristics" type="Optional"/>
+      <avprule id="Gx-Capability-List" type="Optional"/>
+      <avprule id="QoS-Information" type="Optional"/>
+      <avprule id="Revalidation-Time" type="Optional"/>
+      <avprule id="Default-EPS-Bearer-QoS" type="Optional"/>
+      <avprule id="Customer-Id" type="Optional"/>
+      <avprule id="Usage-Monitoring-Information" type="Optional" qual="*"/>
+      <avprule id="Failed-AVP" type="Optional" qual="*"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="Route-Record" type="Optional" qual="*"/>
+      <avprule id="Presence-Reporting-Area-Information" type="Optional"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="ASR" code="274" type="Request">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Destination-Realm" type="Mandatory"/>
+      <avprule id="Destination-Host" type="Mandatory"/>
+      <avprule id="Auth-Application-Id" type="Mandatory"/>
+      <avprule id="User-Name" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="Route-Record" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="ASA" code="274" type="Answer">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Result-Code" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="User-Name" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Error-Message" type="Optional"/>
+      <avprule id="Error-Reporting-Host" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional" qual="*"/>
+      <avprule id="Redirect-Host" type="Optional" qual="*"/>
+      <avprule id="Redirect-Host-Usage" type="Optional"/>
+      <avprule id="Redirect-Max-Cache-Time" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="STR" code="275" type="Request">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Destination-Realm" type="Mandatory"/>
+      <avprule id="Auth-Application-Id" type="Mandatory"/>
+      <avprule id="Termination-Cause" type="Mandatory"/>
+      <avprule id="User-Name" type="Optional"/>
+      <avprule id="Destination-Host" type="Optional"/>
+      <avprule id="Class" type="Optional" qual="*"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="Route-Record" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="STA" code="275" type="Answer">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Result-Code" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="User-Name" type="Optional"/>
+      <avprule id="Class" type="Optional" qual="*"/>
+      <avprule id="Error-Message" type="Optional"/>
+      <avprule id="Error-Reporting-Host" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional" qual="*"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Redirect-Host" type="Optional" qual="*"/>
+      <avprule id="Redirect-Host-Usage" type="Optional"/>
+      <avprule id="Redirect-Max-Cache-Time" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="DWR" code="280" type="Request">
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+   </command>
+   <command name="DWA" code="280" type="Answer">
+      <avprule id="Result-Code" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Error-Message" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional" qual="*"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+   </command>
+   <command name="DPR" code="282" type="Request">
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Disconnect-Cause" type="Mandatory"/>
+   </command>
+   <command name="DPA" code="282" type="Answer">
+      <avprule id="Result-Code" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Error-Message" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional" qual="*"/>
+   </command>
+</dictionary>
diff --git a/example/diameter/launcher/deployments/aots/agents/ADML/stacks/DictionaryRx.16777236.xml b/example/diameter/launcher/deployments/aots/agents/ADML/stacks/DictionaryRx.16777236.xml
new file mode 100755 (executable)
index 0000000..bfd6c3c
--- /dev/null
@@ -0,0 +1,835 @@
+<dictionary name="DictionaryRx | Application-Id: 16777236">
+   <vendor name="IETF" code="0"/>
+   <vendor name="3GPP" code="10415"/>
+   <vendor name="ETSI" code="13019"/>
+   <avp name="User-Name" code="1" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Framed-IP-Address" code="8" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Class" code="25" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Session-Timeout" code="27" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Called-Station-Id" code="30" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Proxy-State" code="33" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Acct-Session-Id" code="44" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Acct-Multi-Session-Id" code="50" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Event-Timestamp" code="55" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Time"/>
+   </avp>
+   <avp name="Acct-Interim-Interval" code="85" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Framed-IPv6-Prefix" code="97" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Host-IP-Address" code="257" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Address"/>
+   </avp>
+   <avp name="Auth-Application-Id" code="258" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Acct-Application-Id" code="259" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Vendor-Specific-Application-Id" code="260" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Vendor-Id" type="Mandatory" qual="1*"/>
+         <avprule id="Auth-Application-Id" type="Optional"/>
+         <avprule id="Acct-Application-Id" type="Optional"/>
+      </grouped>
+   </avp>
+   <avp name="Redirect-Host-Usage" code="261" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-6">
+         <label data="0" alias="DONT_CACHE"/>
+         <label data="1" alias="ALL_SESSION"/>
+         <label data="2" alias="ALL_REALM"/>
+         <label data="3" alias="REALM_AND_APPLICATION"/>
+         <label data="4" alias="ALL_APPLICATION"/>
+         <label data="5" alias="ALL_HOST"/>
+         <label data="6" alias="ALL_USER"/>
+      </single>
+   </avp>
+   <avp name="Redirect-Max-Cache-Time" code="262" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Session-Id" code="263" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Origin-Host" code="264" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Supported-Vendor-Id" code="265" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Vendor-Id" code="266" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Firmware-Revision" code="267" may-encrypt="yes" v-bit="mustnot" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Result-Code" code="268" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32">
+         <label data="1001" alias="DIAMETER_MULTI_ROUND_AUTH"/>
+         <label data="2001" alias="DIAMETER_SUCCESS"/>
+         <label data="2002" alias="DIAMETER_LIMITED_SUCCESS"/>
+         <label data="3001" alias="DIAMETER_COMMAND_UNSUPPORTED"/>
+         <label data="3002" alias="DIAMETER_UNABLE_TO_DELIVER"/>
+         <label data="3003" alias="DIAMETER_REALM_NOT_SERVED"/>
+         <label data="3004" alias="DIAMETER_TOO_BUSY"/>
+         <label data="3005" alias="DIAMETER_LOOP_DETECTED"/>
+         <label data="3006" alias="DIAMETER_REDIRECT_INDICATION"/>
+         <label data="3007" alias="DIAMETER_APPLICATION_UNSUPPORTED"/>
+         <label data="3008" alias="DIAMETER_INVALID_HDR_BITS"/>
+         <label data="3009" alias="DIAMETER_INVALID_AVP_BITS"/>
+         <label data="3010" alias="DIAMETER_UNKNOWN_PEER"/>
+         <label data="4001" alias="DIAMETER_AUTHENTICATION_REJECTED"/>
+         <label data="4002" alias="DIAMETER_OUT_OF_SPACE"/>
+         <label data="4003" alias="DIAMETER_ELECTION_LOST"/>
+         <label data="4241" alias="DIAMETER_NO_AVAILABLE_POLICY_COUNTERS"/>
+         <label data="5001" alias="DIAMETER_AVP_UNSUPPORTED"/>
+         <label data="5002" alias="DIAMETER_UNKNOWN_SESSION_ID"/>
+         <label data="5003" alias="DIAMETER_AUTHORIZATION_REJECTED"/>
+         <label data="5004" alias="DIAMETER_INVALID_AVP_VALUE"/>
+         <label data="5005" alias="DIAMETER_MISSING_AVP"/>
+         <label data="5006" alias="DIAMETER_RESOURCES_EXCEEDED"/>
+         <label data="5007" alias="DIAMETER_CONTRADICTING_AVPS"/>
+         <label data="5008" alias="DIAMETER_AVP_NOT_ALLOWED"/>
+         <label data="5009" alias="DIAMETER_AVP_OCCURS_TOO_MANY_TIMES"/>
+         <label data="5010" alias="DIAMETER_NO_COMMON_APPLICATION"/>
+         <label data="5011" alias="DIAMETER_UNSUPPORTED_VERSION"/>
+         <label data="5012" alias="DIAMETER_UNABLE_TO_COMPLY"/>
+         <label data="5013" alias="DIAMETER_INVALID_BIT_IN_HEADER"/>
+         <label data="5014" alias="DIAMETER_INVALID_AVP_LENGTH"/>
+         <label data="5015" alias="DIAMETER_INVALID_MESSAGE_LENGTH"/>
+         <label data="5016" alias="DIAMETER_INVALID_AVP_BIT_COMBO"/>
+         <label data="5017" alias="DIAMETER_NO_COMMON_SECURITY"/>
+         <label data="5030" alias="DIAMETER_USER_UNKNOWN"/>
+         <label data="5063" alias="REQUESTED_SERVICE_NOT_AUTHORIZED"/>
+         <label data="5065" alias="IP_CAN_SESSION_NOT_AVAILABLE"/>
+      </single>
+   </avp>
+   <avp name="Product-Name" code="269" may-encrypt="yes" v-bit="mustnot" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Session-Binding" code="270" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Session-Server-Failover" code="271" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-3">
+         <label data="0" alias="REFUSE_SERVICE"/>
+         <label data="1" alias="TRY_AGAIN"/>
+         <label data="2" alias="ALLOW_SERVICE"/>
+         <label data="3" alias="TRY_AGAIN_ALLOW_SERVICE"/>
+      </single>
+   </avp>
+   <avp name="Multi-Round-Time-Out" code="272" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Disconnect-Cause" code="273" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-2">
+         <label data="0" alias="REBOOTING"/>
+         <label data="1" alias="BUSY"/>
+         <label data="2" alias="DO_NOT_WANT_TO_TALK_TO_YOU"/>
+      </single>
+   </avp>
+   <avp name="Auth-Request-Type" code="274" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="1-3">
+         <label data="1" alias="AUTHENTICATE_ONLY"/>
+         <label data="2" alias="AUTHORIZE_ONLY"/>
+         <label data="3" alias="AUTHORIZE_AUTHENTICATE"/>
+      </single>
+   </avp>
+   <avp name="Auth-Grace-Period" code="276" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Auth-Session-State" code="277" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="STATE_MAINTAINED"/>
+         <label data="1" alias="NO_STATE_MAINTAINED"/>
+      </single>
+   </avp>
+   <avp name="Origin-State-Id" code="278" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Failed-AVP" code="279" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="AVP" type="Mandatory" qual="1*"/>
+      </grouped>
+   </avp>
+   <avp name="Proxy-Host" code="280" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Error-Message" code="281" may-encrypt="yes" v-bit="mustnot" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Route-Record" code="282" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Destination-Realm" code="283" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Proxy-Info" code="284" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Proxy-Host" type="Mandatory"/>
+         <avprule id="Proxy-State" type="Mandatory"/>
+         <avprule id="AVP" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="Re-Auth-Request-Type" code="285" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="AUTHORIZE_ONLY"/>
+         <label data="1" alias="AUTHORIZE_AUTHENTICATE"/>
+      </single>
+   </avp>
+   <avp name="Accounting-Sub-Session-Id" code="287" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned64"/>
+   </avp>
+   <avp name="Redirect-Host" code="292" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterURI"/>
+   </avp>
+   <avp name="Destination-Host" code="293" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Error-Reporting-Host" code="294" may-encrypt="yes" v-bit="mustnot" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Termination-Cause" code="295" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="1-8">
+         <label data="1" alias="LOGOUT"/>
+         <label data="2" alias="SERVICE_NOT_PROVIDED"/>
+         <label data="3" alias="BAD_ANSWER"/>
+         <label data="4" alias="ADMINISTRATIVE"/>
+         <label data="5" alias="LINK_BROKEN"/>
+         <label data="6" alias="AUTH_EXPIRED"/>
+         <label data="7" alias="USER_MOVED"/>
+         <label data="8" alias="SESSION_TIMEOUT"/>
+      </single>
+   </avp>
+   <avp name="Origin-Realm" code="296" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Experimental-Result" code="297" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Vendor-Id" type="Mandatory"/>
+         <avprule id="Experimental-Result-Code" type="Mandatory"/>
+      </grouped>
+   </avp>
+   <avp name="Experimental-Result-Code" code="298" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Inband-Security-Id" code="299" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="E2E-Sequence" code="300" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="AVP" type="Mandatory" qual="2*"/>
+      </grouped>
+   </avp>
+   <avp name="Subscription-Id" code="443" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Subscription-Id-Type" type="Mandatory"/>
+         <avprule id="Subscription-Id-Data" type="Mandatory"/>
+      </grouped>
+   </avp>
+   <avp name="Subscription-Id-Data" code="444" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Subscription-Id-Type" code="450" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-4">
+         <label data="0" alias="END_USER_E164"/>
+         <label data="1" alias="END_USER_IMSI"/>
+         <label data="2" alias="END_USER_SIP_URI"/>
+         <label data="3" alias="END_USER_NAI"/>
+         <label data="4" alias="END_USER_PRIVATE"/>
+      </single>
+   </avp>
+   <avp name="User-Equipment-Info" code="458" may-encrypt="yes" v-bit="mustnot" m-bit="mustnot" p-bit="mustnot">
+      <grouped>
+         <avprule id="User-Equipment-Info-Type" type="Mandatory"/>
+         <avprule id="User-Equipment-Info-Value" type="Mandatory"/>
+      </grouped>
+   </avp>
+   <avp name="User-Equipment-Info-Type" code="459" may-encrypt="yes" v-bit="mustnot" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-3">
+         <label data="0" alias="IMEISV"/>
+         <label data="1" alias="MAC"/>
+         <label data="2" alias="EUI64"/>
+         <label data="3" alias="MODIFIED_EUI64"/>
+      </single>
+   </avp>
+   <avp name="User-Equipment-Info-Value" code="460" may-encrypt="yes" v-bit="mustnot" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Accounting-Record-Type" code="480" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="1-4">
+         <label data="1" alias="EVENT_RECORD"/>
+         <label data="2" alias="START_RECORD"/>
+         <label data="3" alias="INTERIM_RECORD"/>
+         <label data="4" alias="STOP_RECORD"/>
+      </single>
+   </avp>
+   <avp name="Accounting-Realtime-Required" code="483" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="1-3">
+         <label data="1" alias="DELIVER_AND_GRANT"/>
+         <label data="2" alias="GRANT_AND_STORE"/>
+         <label data="3" alias="GRANT_AND_LOSE"/>
+      </single>
+   </avp>
+   <avp name="Accounting-Record-Number" code="485" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="3GPP-SGSN-MCC-MNC" code="18" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="3GPP-User-Location-Info" code="22" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="3GPP-MS-TimeZone" code="23" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="TWAN-Identifier" code="29" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Abort-Cause" code="500" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-4">
+         <label data="0" alias="BEARER_RELEASED"/>
+         <label data="1" alias="INSUFFICIENT_SERVER_RESOURCES"/>
+         <label data="2" alias="INSUFFICIENT_BEARER_RESOURCES"/>
+         <label data="3" alias="PS_TO_CS_HANDOVER"/>
+         <label data="4" alias="SPONSORED_DATA_CONNECTIVITY_DISALLOWED"/>
+      </single>
+   </avp>
+   <avp name="Access-Network-Charging-Identifier" code="502" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Access-Network-Charging-Identifier-Value" type="Mandatory"/>
+         <avprule id="Flows" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="Access-Network-Charging-Identifier-Value" code="503" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="AF-Application-Identifier" code="504" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="AF-Charging-Identifier" code="505" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Flow-Description" code="507" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="IPFilterRule"/>
+   </avp>
+   <avp name="Flow-Number" code="509" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Flows" code="510" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Media-Component-Number" type="Mandatory"/>
+         <avprule id="Flow-Number" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="Flow-Status" code="511" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-4">
+         <label data="0" alias="ENABLED-UPLINK"/>
+         <label data="1" alias="ENABLED-DOWNLINK"/>
+         <label data="2" alias="ENABLED"/>
+         <label data="3" alias="DISABLED"/>
+         <label data="4" alias="REMOVED"/>
+      </single>
+   </avp>
+   <avp name="Flow-Usage" code="512" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-2">
+         <label data="0" alias="NO_INFORMATION"/>
+         <label data="1" alias="RTCP"/>
+         <label data="2" alias="AF_SIGNALLING"/>
+      </single>
+   </avp>
+   <avp name="Specific-Action" code="513" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="1-4,6-14,16">
+         <label data="1" alias="CHARGING_CORRELATION_EXCHANGE"/>
+         <label data="2" alias="INDICATION_OF_LOSS_OF_BEARER"/>
+         <label data="3" alias="INDICATION_OF_RECOVERY_OF_BEARER"/>
+         <label data="4" alias="INDICATION_OF_RELEASE_OF_BEARER"/>
+         <label data="6" alias="IP-CAN_CHANGE"/>
+         <label data="7" alias="INDICATION_OF_OUT_OF_CREDIT"/>
+         <label data="8" alias="INDICATION_OF_SUCCESSFUL_RESOURCES_ALLOCATION"/>
+         <label data="9" alias="INDICATION_OF_FAILED_RESOURCES_ALLOCATION"/>
+         <label data="10" alias="INDICATION_OF_LIMITED_PCC_DEPLOYMENT"/>
+         <label data="11" alias="USAGE_REPORT"/>
+         <label data="12" alias="ACCESS_NETWORK_INFO_REPORT"/>
+         <label data="13" alias="INDICATION_OF_RECOVERY_FROM_LIMITED_PCC_DEPLOYMENT"/>
+         <label data="14" alias="INDICATION_OF_ACCESS_NETWORK_INFO_REPORTING_FAILURE"/>
+         <label data="16" alias="PLMN_CHANGE"/>
+      </single>
+   </avp>
+   <avp name="Max-Requested-Bandwidth-DL" code="515" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Max-Requested-Bandwidth-UL" code="516" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Media-Component-Description" code="517" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Media-Component-Number" type="Mandatory"/>
+         <avprule id="Media-Sub-Component" type="Optional" qual="*"/>
+         <avprule id="AF-Application-Identifier" type="Optional"/>
+         <avprule id="Media-Type" type="Optional"/>
+         <avprule id="Max-Requested-Bandwidth-UL" type="Optional"/>
+         <avprule id="Max-Requested-Bandwidth-DL" type="Optional"/>
+         <avprule id="Min-Requested-Bandwidth-UL" type="Optional"/>
+         <avprule id="Min-Requested-Bandwidth-DL" type="Optional"/>
+         <avprule id="Extended-Max-Requested-BW-UL" type="Optional"/>
+         <avprule id="Extended-Max-Requested-BW-DL" type="Optional"/>
+         <avprule id="Flow-Status" type="Optional"/>
+         <avprule id="Reservation-Priority" type="Optional"/>
+         <avprule id="RS-Bandwidth" type="Optional"/>
+         <avprule id="RR-Bandwidth" type="Optional"/>
+         <avprule id="Codec-Data" type="Optional" qual="*"/>
+         <avprule id="AVP" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="Media-Component-Number" code="518" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Media-Sub-Component" code="519" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Flow-Number" type="Mandatory"/>
+         <avprule id="Flow-Description" type="Optional" qual="0*2"/>
+         <avprule id="Flow-Status" type="Optional"/>
+         <avprule id="Flow-Usage" type="Optional"/>
+         <avprule id="AF-Signalling-Protocol" type="Optional"/>
+         <avprule id="Max-Requested-Bandwidth-UL" type="Optional"/>
+         <avprule id="Max-Requested-Bandwidth-DL" type="Optional"/>
+         <avprule id="Extended-Max-Requested-BW-UL" type="Optional"/>
+         <avprule id="Extended-Max-Requested-BW-DL" type="Optional"/>
+         <avprule id="AVP" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="Media-Type" code="520" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-6">
+         <label data="0" alias="OTHER"/>
+         <label data="1" alias="VIDEO"/>
+         <label data="2" alias="DATA"/>
+         <label data="3" alias="APPLICATION"/>
+         <label data="4" alias="CONTROL"/>
+         <label data="5" alias="TEXT"/>
+         <label data="6" alias="MESSAGE"/>
+      </single>
+   </avp>
+   <avp name="RR-Bandwidth" code="521" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="RS-Bandwidth" code="522" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="SIP-Forking-Indication" code="523" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="SINGLE_DIALOGUE"/>
+         <label data="1" alias="SEVERAL_DIALOGUES"/>
+      </single>
+   </avp>
+   <avp name="Codec-Data" code="524" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Service-URN" code="525" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Service-Info-Status" code="527" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="FINAL_SERVICE_INFORMATION"/>
+         <label data="1" alias="PRELIMINARY_SERVICE_INFORMATION"/>
+      </single>
+   </avp>
+   <avp name="MPS-Identifier" code="528" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="AF-Signalling-Protocol" code="529" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="NO_INFORMATION"/>
+         <label data="1" alias="SIP"/>
+      </single>
+   </avp>
+   <avp name="Rx-Request-Type" code="533" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-2">
+         <label data="0" alias="INITIAL_REQUEST"/>
+         <label data="1" alias="UPDATE_REQUEST"/>
+         <label data="2" alias="PCSCF_RESTORATION"/>
+      </single>
+   </avp>
+   <avp name="Min-Requested-Bandwidth-DL" code="534" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Min-Requested-Bandwidth-UL" code="535" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Required-Access-Info" code="536" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="USER_LOCATION"/>
+         <label data="1" alias="MS_TIME_ZONE"/>
+      </single>
+   </avp>
+   <avp name="IP-Domain-Id" code="537" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="AF-Requested-Data" code="551" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Extended-Max-Requested-BW-DL" code="554" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Extended-Max-Requested-BW-UL" code="555" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Supported-Features" code="628" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Vendor-Id" type="Mandatory"/>
+         <avprule id="Feature-List-ID" type="Mandatory"/>
+         <avprule id="Feature-List" type="Mandatory"/>
+         <avprule id="AVP" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="Feature-List-ID" code="629" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Feature-List" code="630" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="IP-CAN-Type" code="1027" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-7">
+         <label data="0" alias="3GPP-GPRS"/>
+         <label data="1" alias="DOCSIS"/>
+         <label data="2" alias="xDSL"/>
+         <label data="3" alias="WiMAX"/>
+         <label data="4" alias="3GPP2"/>
+         <label data="5" alias="3GPP-EPS"/>
+         <label data="6" alias="Non-3GPP-EPS"/>
+         <label data="7" alias="FBA"/>
+      </single>
+   </avp>
+   <avp name="RAT-Type" code="1032" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1,1000-1005,2000-2003">
+         <label data="0" alias="WLAN"/>
+         <label data="1" alias="VIRTUAL"/>
+         <label data="1000" alias="UTRAN"/>
+         <label data="1001" alias="GERAN"/>
+         <label data="1002" alias="GAN"/>
+         <label data="1003" alias="HSPA_EVOLUTION"/>
+         <label data="1004" alias="EUTRAN"/>
+         <label data="1005" alias="EUTRAN-NB-IoT"/>
+         <label data="2000" alias="CDMA2000_1X"/>
+         <label data="2001" alias="HRPD"/>
+         <label data="2002" alias="UMB"/>
+         <label data="2003" alias="EHRPD"/>
+      </single>
+   </avp>
+   <avp name="AN-GW-Address" code="1050" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Address"/>
+   </avp>
+   <avp name="AN-Trusted" code="1503" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="TRUSTED"/>
+         <label data="1" alias="UNTRUSTED"/>
+      </single>
+   </avp>
+   <avp name="User-Equipment-Local-IP-Address" code="2805" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="UDP-Source-Port" code="2806" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="User-Location-Info-Time" code="2812" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Time"/>
+   </avp>
+   <avp name="NetLoc-Access-Support" code="2824" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="TCP-Source-Port" code="2843" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Reservation-Priority" code="458" vendor-name="ETSI" may-encrypt="yes" v-bit="must" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-15">
+         <label data="0" alias="DEFAULT"/>
+         <label data="1" alias="PRIORITY-ONE"/>
+         <label data="2" alias="PRIORITY-TWO"/>
+         <label data="3" alias="PRIORITY-THREE"/>
+         <label data="4" alias="PRIORITY-FOUR"/>
+         <label data="5" alias="PRIORITY-FIVE"/>
+         <label data="6" alias="PRIORITY-SIX"/>
+         <label data="7" alias="PRIORITY-SEVEN"/>
+         <label data="8" alias="PRIORITY-EIGHT"/>
+         <label data="9" alias="PRIORITY-NINE"/>
+         <label data="10" alias="PRIORITY-TEN"/>
+         <label data="11" alias="PRIORITY-ELEVEN"/>
+         <label data="12" alias="PRIORITY-TWELVE"/>
+         <label data="13" alias="PRIORITY-THIRTEEN"/>
+         <label data="14" alias="PRIORITY-FOURTEEN"/>
+         <label data="15" alias="PRIORITY-FIFTEEN"/>
+      </single>
+   </avp>
+   <command name="CER" code="257" type="Request">
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Host-IP-Address" type="Mandatory" qual="1*"/>
+      <avprule id="Vendor-Id" type="Mandatory"/>
+      <avprule id="Product-Name" type="Mandatory"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Supported-Vendor-Id" type="Optional" qual="*"/>
+      <avprule id="Auth-Application-Id" type="Optional" qual="*"/>
+      <avprule id="Inband-Security-Id" type="Optional" qual="*"/>
+      <avprule id="Acct-Application-Id" type="Optional" qual="*"/>
+      <avprule id="Vendor-Specific-Application-Id" type="Optional" qual="*"/>
+      <avprule id="Firmware-Revision" type="Optional"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="CEA" code="257" type="Answer">
+      <avprule id="Result-Code" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Host-IP-Address" type="Mandatory" qual="1*"/>
+      <avprule id="Vendor-Id" type="Mandatory"/>
+      <avprule id="Product-Name" type="Mandatory"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Error-Message" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional" qual="*"/>
+      <avprule id="Supported-Vendor-Id" type="Optional" qual="*"/>
+      <avprule id="Auth-Application-Id" type="Optional" qual="*"/>
+      <avprule id="Inband-Security-Id" type="Optional" qual="*"/>
+      <avprule id="Acct-Application-Id" type="Optional" qual="*"/>
+      <avprule id="Vendor-Specific-Application-Id" type="Optional" qual="*"/>
+      <avprule id="Firmware-Revision" type="Optional"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="RA-Request" code="258" type="Request">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Destination-Realm" type="Mandatory"/>
+      <avprule id="Destination-Host" type="Mandatory"/>
+      <avprule id="Auth-Application-Id" type="Mandatory"/>
+      <avprule id="Specific-Action" type="Mandatory" qual="*"/>
+      <avprule id="Access-Network-Charging-Identifier" type="Optional" qual="*"/>
+      <avprule id="Flows" type="Optional" qual="*"/>
+      <avprule id="Abort-Cause" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="Route-Record" type="Optional" qual="*"/>
+      <avprule id="3GPP-MS-TimeZone" type="Optional"/>
+      <avprule id="3GPP-User-Location-Info" type="Optional"/>
+      <avprule id="User-Equipment-Local-IP-Address" type="Optional"/>
+      <avprule id="TCP-Source-Port" type="Optional"/>
+      <avprule id="UDP-Source-Port" type="Optional"/>
+      <avprule id="TWAN-Identifier" type="Optional"/>
+      <avprule id="3GPP-SGSN-MCC-MNC" type="Optional"/>
+      <avprule id="NetLoc-Access-Support" type="Optional"/>
+      <avprule id="User-Location-Info-Time" type="Optional"/>
+      <avprule id="IP-CAN-Type" type="Optional"/>
+      <avprule id="RAT-Type" type="Optional"/>
+      <avprule id="AN-Trusted" type="Optional"/>
+      <avprule id="AN-GW-Address" type="Optional" qual="0*2"/>
+   </command>
+   <command name="RA-Answer" code="258" type="Answer">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Result-Code" type="Optional"/>
+      <avprule id="Experimental-Result" type="Optional"/>
+      <avprule id="Media-Component-Description" type="Optional" qual="*"/>
+      <avprule id="Service-URN" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Class" type="Optional" qual="*"/>
+      <avprule id="Error-Message" type="Optional"/>
+      <avprule id="Error-Reporting-Host" type="Optional"/>
+      <avprule id="Redirect-Host" type="Optional" qual="*"/>
+      <avprule id="Redirect-Host-Usage" type="Optional"/>
+      <avprule id="Redirect-Max-Cache-Time" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="AA-Request" code="265" type="Request">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Auth-Application-Id" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Destination-Realm" type="Mandatory"/>
+      <avprule id="Destination-Host" type="Optional"/>
+      <avprule id="IP-Domain-Id" type="Optional"/>
+      <avprule id="AF-Application-Identifier" type="Optional"/>
+      <avprule id="Media-Component-Description" type="Optional" qual="*"/>
+      <avprule id="Service-Info-Status" type="Optional"/>
+      <avprule id="AF-Charging-Identifier" type="Optional"/>
+      <avprule id="SIP-Forking-Indication" type="Optional"/>
+      <avprule id="Specific-Action" type="Optional" qual="*"/>
+      <avprule id="Subscription-Id" type="Optional" qual="*"/>
+      <avprule id="Supported-Features" type="Optional" qual="*"/>
+      <avprule id="Reservation-Priority" type="Optional"/>
+      <avprule id="Framed-IP-Address" type="Optional"/>
+      <avprule id="Framed-IPv6-Prefix" type="Optional"/>
+      <avprule id="Called-Station-Id" type="Optional"/>
+      <avprule id="Service-URN" type="Optional"/>
+      <avprule id="MPS-Identifier" type="Optional"/>
+      <avprule id="Rx-Request-Type" type="Optional"/>
+      <avprule id="Required-Access-Info" type="Optional" qual="*"/>
+      <avprule id="AF-Requested-Data" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="Route-Record" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="AA-Answer" code="265" type="Answer">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Auth-Application-Id" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Result-Code" type="Optional"/>
+      <avprule id="Experimental-Result" type="Optional"/>
+      <avprule id="AN-Trusted" type="Optional"/>
+      <avprule id="IP-CAN-Type" type="Optional"/>
+      <avprule id="RAT-Type" type="Optional"/>
+      <avprule id="Supported-Features" type="Optional" qual="*"/>
+      <avprule id="Subscription-Id" type="Optional" qual="*"/>
+      <avprule id="User-Equipment-Info" type="Optional"/>
+      <avprule id="3GPP-SGSN-MCC-MNC" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+   </command>
+   <command name="ACR" code="271" type="Request">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Destination-Realm" type="Mandatory"/>
+      <avprule id="Accounting-Record-Type" type="Mandatory"/>
+      <avprule id="Accounting-Record-Number" type="Mandatory"/>
+      <avprule id="Acct-Application-Id" type="Optional"/>
+      <avprule id="Vendor-Specific-Application-Id" type="Optional"/>
+      <avprule id="User-Name" type="Optional"/>
+      <avprule id="Accounting-Sub-Session-Id" type="Optional"/>
+      <avprule id="Acct-Session-Id" type="Optional"/>
+      <avprule id="Acct-Multi-Session-Id" type="Optional"/>
+      <avprule id="Acct-Interim-Interval" type="Optional"/>
+      <avprule id="Accounting-Realtime-Required" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Event-Timestamp" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="Route-Record" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="ACA" code="271" type="Answer">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Result-Code" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Accounting-Record-Type" type="Mandatory"/>
+      <avprule id="Accounting-Record-Number" type="Mandatory"/>
+      <avprule id="Acct-Application-Id" type="Optional"/>
+      <avprule id="Vendor-Specific-Application-Id" type="Optional"/>
+      <avprule id="User-Name" type="Optional"/>
+      <avprule id="Accounting-Sub-Session-Id" type="Optional"/>
+      <avprule id="Acct-Session-Id" type="Optional"/>
+      <avprule id="Acct-Multi-Session-Id" type="Optional"/>
+      <avprule id="Error-Reporting-Host" type="Optional"/>
+      <avprule id="Acct-Interim-Interval" type="Optional"/>
+      <avprule id="Accounting-Realtime-Required" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Event-Timestamp" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="AS-Request" code="274" type="Request">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Destination-Realm" type="Mandatory"/>
+      <avprule id="Destination-Host" type="Mandatory"/>
+      <avprule id="Auth-Application-Id" type="Mandatory"/>
+      <avprule id="Abort-Cause" type="Mandatory"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="Route-Record" type="Optional" qual="*"/>
+   </command>
+   <command name="AS-Answer" code="274" type="Answer">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Result-Code" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Error-Message" type="Optional"/>
+      <avprule id="Error-Reporting-Host" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional"/>
+      <avprule id="Redirect-Host" type="Optional" qual="*"/>
+      <avprule id="Redirect-Host-Usage" type="Optional"/>
+      <avprule id="Redirect-Max-Cache-Time" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="ST-Request" code="275" type="Request">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Destination-Realm" type="Mandatory"/>
+      <avprule id="Auth-Application-Id" type="Mandatory"/>
+      <avprule id="Termination-Cause" type="Mandatory"/>
+      <avprule id="Destination-Host" type="Optional"/>
+      <avprule id="Required-Access-Info" type="Optional" qual="*"/>
+      <avprule id="Class" type="Optional" qual="*"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="Route-Record" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="ST-Answer" code="275" type="Answer">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Result-Code" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="3GPP-MS-TimeZone" type="Optional"/>
+      <avprule id="3GPP-User-Location-Info" type="Optional"/>
+      <avprule id="User-Equipment-Local-IP-Address" type="Optional"/>
+      <avprule id="TCP-Source-Port" type="Optional"/>
+      <avprule id="UDP-Source-Port" type="Optional"/>
+      <avprule id="TWAN-Identifier" type="Optional"/>
+      <avprule id="3GPP-SGSN-MCC-MNC" type="Optional"/>
+      <avprule id="NetLoc-Access-Support" type="Optional"/>
+      <avprule id="User-Location-Info-Time" type="Optional"/>
+   </command>
+   <command name="DWR" code="280" type="Request">
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+   </command>
+   <command name="DWA" code="280" type="Answer">
+      <avprule id="Result-Code" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Error-Message" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional" qual="*"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+   </command>
+   <command name="DPR" code="282" type="Request">
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Disconnect-Cause" type="Mandatory"/>
+   </command>
+   <command name="DPA" code="282" type="Answer">
+      <avprule id="Result-Code" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Error-Message" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional" qual="*"/>
+   </command>
+</dictionary>
diff --git a/example/diameter/launcher/deployments/aots/agents/ADML/stacks/DictionarySy.16777302.xml b/example/diameter/launcher/deployments/aots/agents/ADML/stacks/DictionarySy.16777302.xml
new file mode 100755 (executable)
index 0000000..0817e8e
--- /dev/null
@@ -0,0 +1,554 @@
+<dictionary name="DictionarySy | Application-Id: 16777302">
+   <vendor name="IETF" code="0"/>
+   <vendor name="3GPP" code="10415"/>
+   <avp name="User-Name" code="1" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Class" code="25" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Session-Timeout" code="27" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Proxy-State" code="33" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Acct-Session-Id" code="44" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Acct-Multi-Session-Id" code="50" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Event-Timestamp" code="55" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Time"/>
+   </avp>
+   <avp name="Acct-Interim-Interval" code="85" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Host-IP-Address" code="257" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Address"/>
+   </avp>
+   <avp name="Auth-Application-Id" code="258" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Acct-Application-Id" code="259" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Vendor-Specific-Application-Id" code="260" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Vendor-Id" type="Mandatory" qual="1*"/>
+         <avprule id="Auth-Application-Id" type="Optional"/>
+         <avprule id="Acct-Application-Id" type="Optional"/>
+      </grouped>
+   </avp>
+   <avp name="Redirect-Host-Usage" code="261" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-6">
+         <label data="0" alias="DONT_CACHE"/>
+         <label data="1" alias="ALL_SESSION"/>
+         <label data="2" alias="ALL_REALM"/>
+         <label data="3" alias="REALM_AND_APPLICATION"/>
+         <label data="4" alias="ALL_APPLICATION"/>
+         <label data="5" alias="ALL_HOST"/>
+         <label data="6" alias="ALL_USER"/>
+      </single>
+   </avp>
+   <avp name="Redirect-Max-Cache-Time" code="262" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Session-Id" code="263" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Origin-Host" code="264" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Supported-Vendor-Id" code="265" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Vendor-Id" code="266" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Firmware-Revision" code="267" may-encrypt="yes" v-bit="mustnot" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Result-Code" code="268" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32">
+         <label data="1001" alias="DIAMETER_MULTI_ROUND_AUTH"/>
+         <label data="2001" alias="DIAMETER_SUCCESS"/>
+         <label data="2002" alias="DIAMETER_LIMITED_SUCCESS"/>
+         <label data="3001" alias="DIAMETER_COMMAND_UNSUPPORTED"/>
+         <label data="3002" alias="DIAMETER_UNABLE_TO_DELIVER"/>
+         <label data="3003" alias="DIAMETER_REALM_NOT_SERVED"/>
+         <label data="3004" alias="DIAMETER_TOO_BUSY"/>
+         <label data="3005" alias="DIAMETER_LOOP_DETECTED"/>
+         <label data="3006" alias="DIAMETER_REDIRECT_INDICATION"/>
+         <label data="3007" alias="DIAMETER_APPLICATION_UNSUPPORTED"/>
+         <label data="3008" alias="DIAMETER_INVALID_HDR_BITS"/>
+         <label data="3009" alias="DIAMETER_INVALID_AVP_BITS"/>
+         <label data="3010" alias="DIAMETER_UNKNOWN_PEER"/>
+         <label data="4001" alias="DIAMETER_AUTHENTICATION_REJECTED"/>
+         <label data="4002" alias="DIAMETER_OUT_OF_SPACE"/>
+         <label data="4003" alias="DIAMETER_ELECTION_LOST"/>
+         <label data="4241" alias="DIAMETER_NO_AVAILABLE_POLICY_COUNTERS"/>
+         <label data="5001" alias="DIAMETER_AVP_UNSUPPORTED"/>
+         <label data="5002" alias="DIAMETER_UNKNOWN_SESSION_ID"/>
+         <label data="5003" alias="DIAMETER_AUTHORIZATION_REJECTED"/>
+         <label data="5004" alias="DIAMETER_INVALID_AVP_VALUE"/>
+         <label data="5005" alias="DIAMETER_MISSING_AVP"/>
+         <label data="5006" alias="DIAMETER_RESOURCES_EXCEEDED"/>
+         <label data="5007" alias="DIAMETER_CONTRADICTING_AVPS"/>
+         <label data="5008" alias="DIAMETER_AVP_NOT_ALLOWED"/>
+         <label data="5009" alias="DIAMETER_AVP_OCCURS_TOO_MANY_TIMES"/>
+         <label data="5010" alias="DIAMETER_NO_COMMON_APPLICATION"/>
+         <label data="5011" alias="DIAMETER_UNSUPPORTED_VERSION"/>
+         <label data="5012" alias="DIAMETER_UNABLE_TO_COMPLY"/>
+         <label data="5013" alias="DIAMETER_INVALID_BIT_IN_HEADER"/>
+         <label data="5014" alias="DIAMETER_INVALID_AVP_LENGTH"/>
+         <label data="5015" alias="DIAMETER_INVALID_MESSAGE_LENGTH"/>
+         <label data="5016" alias="DIAMETER_INVALID_AVP_BIT_COMBO"/>
+         <label data="5017" alias="DIAMETER_NO_COMMON_SECURITY"/>
+         <label data="5030" alias="DIAMETER_USER_UNKNOWN"/>
+         <label data="5063" alias="REQUESTED_SERVICE_NOT_AUTHORIZED"/>
+         <label data="5065" alias="IP_CAN_SESSION_NOT_AVAILABLE"/>
+      </single>
+   </avp>
+   <avp name="Product-Name" code="269" may-encrypt="yes" v-bit="mustnot" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Session-Binding" code="270" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Session-Server-Failover" code="271" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-3">
+         <label data="0" alias="REFUSE_SERVICE"/>
+         <label data="1" alias="TRY_AGAIN"/>
+         <label data="2" alias="ALLOW_SERVICE"/>
+         <label data="3" alias="TRY_AGAIN_ALLOW_SERVICE"/>
+      </single>
+   </avp>
+   <avp name="Multi-Round-Time-Out" code="272" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Disconnect-Cause" code="273" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-2">
+         <label data="0" alias="REBOOTING"/>
+         <label data="1" alias="BUSY"/>
+         <label data="2" alias="DO_NOT_WANT_TO_TALK_TO_YOU"/>
+      </single>
+   </avp>
+   <avp name="Auth-Request-Type" code="274" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="1-3">
+         <label data="1" alias="AUTHENTICATE_ONLY"/>
+         <label data="2" alias="AUTHORIZE_ONLY"/>
+         <label data="3" alias="AUTHORIZE_AUTHENTICATE"/>
+      </single>
+   </avp>
+   <avp name="Auth-Grace-Period" code="276" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Auth-Session-State" code="277" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="STATE_MAINTAINED"/>
+         <label data="1" alias="NO_STATE_MAINTAINED"/>
+      </single>
+   </avp>
+   <avp name="Origin-State-Id" code="278" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Failed-AVP" code="279" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="AVP" type="Mandatory" qual="1*"/>
+      </grouped>
+   </avp>
+   <avp name="Proxy-Host" code="280" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Error-Message" code="281" may-encrypt="yes" v-bit="mustnot" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Route-Record" code="282" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Destination-Realm" code="283" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Proxy-Info" code="284" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Proxy-Host" type="Mandatory"/>
+         <avprule id="Proxy-State" type="Mandatory"/>
+         <avprule id="AVP" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="Re-Auth-Request-Type" code="285" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="AUTHORIZE_ONLY"/>
+         <label data="1" alias="AUTHORIZE_AUTHENTICATE"/>
+      </single>
+   </avp>
+   <avp name="Accounting-Sub-Session-Id" code="287" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned64"/>
+   </avp>
+   <avp name="Redirect-Host" code="292" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterURI"/>
+   </avp>
+   <avp name="Destination-Host" code="293" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Error-Reporting-Host" code="294" may-encrypt="yes" v-bit="mustnot" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Termination-Cause" code="295" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="1-8">
+         <label data="1" alias="LOGOUT"/>
+         <label data="2" alias="SERVICE_NOT_PROVIDED"/>
+         <label data="3" alias="BAD_ANSWER"/>
+         <label data="4" alias="ADMINISTRATIVE"/>
+         <label data="5" alias="LINK_BROKEN"/>
+         <label data="6" alias="AUTH_EXPIRED"/>
+         <label data="7" alias="USER_MOVED"/>
+         <label data="8" alias="SESSION_TIMEOUT"/>
+      </single>
+   </avp>
+   <avp name="Origin-Realm" code="296" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Experimental-Result" code="297" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Vendor-Id" type="Mandatory"/>
+         <avprule id="Experimental-Result-Code" type="Mandatory"/>
+      </grouped>
+   </avp>
+   <avp name="Experimental-Result-Code" code="298" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Inband-Security-Id" code="299" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="E2E-Sequence" code="300" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="AVP" type="Mandatory" qual="2*"/>
+      </grouped>
+   </avp>
+   <avp name="Logical-Access-ID" code="302" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Physical-Access-ID" code="313" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Subscription-Id" code="443" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Subscription-Id-Type" type="Mandatory"/>
+         <avprule id="Subscription-Id-Data" type="Mandatory"/>
+      </grouped>
+   </avp>
+   <avp name="Subscription-Id-Data" code="444" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Subscription-Id-Type" code="450" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-4">
+         <label data="0" alias="END_USER_E164"/>
+         <label data="1" alias="END_USER_IMSI"/>
+         <label data="2" alias="END_USER_SIP_URI"/>
+         <label data="3" alias="END_USER_NAI"/>
+         <label data="4" alias="END_USER_PRIVATE"/>
+      </single>
+   </avp>
+   <avp name="Accounting-Record-Type" code="480" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="1-4">
+         <label data="1" alias="EVENT_RECORD"/>
+         <label data="2" alias="START_RECORD"/>
+         <label data="3" alias="INTERIM_RECORD"/>
+         <label data="4" alias="STOP_RECORD"/>
+      </single>
+   </avp>
+   <avp name="Accounting-Realtime-Required" code="483" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="1-3">
+         <label data="1" alias="DELIVER_AND_GRANT"/>
+         <label data="2" alias="GRANT_AND_STORE"/>
+         <label data="3" alias="GRANT_AND_LOSE"/>
+      </single>
+   </avp>
+   <avp name="Accounting-Record-Number" code="485" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Policy-Counter-Identifier" code="2901" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Policy-Counter-Status" code="2902" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Policy-Counter-Status-Report" code="2903" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Policy-Counter-Identifier" type="Mandatory"/>
+         <avprule id="Policy-Counter-Status" type="Mandatory"/>
+         <avprule id="Pending-Policy-Counter-Information" type="Optional" qual="*"/>
+         <avprule id="AVP" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="SL-Request-Type" code="2904" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="INITIAL_REQUEST"/>
+         <label data="1" alias="INTERMEDIATE_REQUEST"/>
+      </single>
+   </avp>
+   <avp name="Pending-Policy-Counter-Information" code="2905" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Policy-Counter-Status" type="Mandatory"/>
+         <avprule id="Pending-Policy-Counter-Change-Time" type="Mandatory"/>
+         <avprule id="AVP" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="Pending-Policy-Counter-Change-Time" code="2906" vendor-name="3GPP" may-encrypt="yes" v-bit="must" m-bit="must" p-bit="mustnot">
+      <single format-name="Time"/>
+   </avp>
+   <command name="CER" code="257" type="Request">
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Host-IP-Address" type="Mandatory" qual="1*"/>
+      <avprule id="Vendor-Id" type="Mandatory"/>
+      <avprule id="Product-Name" type="Mandatory"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Supported-Vendor-Id" type="Optional" qual="*"/>
+      <avprule id="Auth-Application-Id" type="Optional" qual="*"/>
+      <avprule id="Inband-Security-Id" type="Optional" qual="*"/>
+      <avprule id="Acct-Application-Id" type="Optional" qual="*"/>
+      <avprule id="Vendor-Specific-Application-Id" type="Optional" qual="*"/>
+      <avprule id="Firmware-Revision" type="Optional"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="CEA" code="257" type="Answer">
+      <avprule id="Result-Code" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Host-IP-Address" type="Mandatory" qual="1*"/>
+      <avprule id="Vendor-Id" type="Mandatory"/>
+      <avprule id="Product-Name" type="Mandatory"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Error-Message" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional" qual="*"/>
+      <avprule id="Supported-Vendor-Id" type="Optional" qual="*"/>
+      <avprule id="Auth-Application-Id" type="Optional" qual="*"/>
+      <avprule id="Inband-Security-Id" type="Optional" qual="*"/>
+      <avprule id="Acct-Application-Id" type="Optional" qual="*"/>
+      <avprule id="Vendor-Specific-Application-Id" type="Optional" qual="*"/>
+      <avprule id="Firmware-Revision" type="Optional"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="RAR" code="258" type="Request">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Destination-Realm" type="Mandatory"/>
+      <avprule id="Destination-Host" type="Mandatory"/>
+      <avprule id="Auth-Application-Id" type="Mandatory"/>
+      <avprule id="Re-Auth-Request-Type" type="Mandatory"/>
+      <avprule id="User-Name" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="Route-Record" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="RAA" code="258" type="Answer">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Result-Code" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="User-Name" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Error-Message" type="Optional"/>
+      <avprule id="Error-Reporting-Host" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional" qual="*"/>
+      <avprule id="Redirect-Host" type="Optional" qual="*"/>
+      <avprule id="Redirect-Host-Usage" type="Optional"/>
+      <avprule id="Redirect-Max-Cache-Time" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="ACR" code="271" type="Request">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Destination-Realm" type="Mandatory"/>
+      <avprule id="Accounting-Record-Type" type="Mandatory"/>
+      <avprule id="Accounting-Record-Number" type="Mandatory"/>
+      <avprule id="Acct-Application-Id" type="Optional"/>
+      <avprule id="Vendor-Specific-Application-Id" type="Optional"/>
+      <avprule id="User-Name" type="Optional"/>
+      <avprule id="Accounting-Sub-Session-Id" type="Optional"/>
+      <avprule id="Acct-Session-Id" type="Optional"/>
+      <avprule id="Acct-Multi-Session-Id" type="Optional"/>
+      <avprule id="Acct-Interim-Interval" type="Optional"/>
+      <avprule id="Accounting-Realtime-Required" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Event-Timestamp" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="Route-Record" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="ACA" code="271" type="Answer">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Result-Code" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Accounting-Record-Type" type="Mandatory"/>
+      <avprule id="Accounting-Record-Number" type="Mandatory"/>
+      <avprule id="Acct-Application-Id" type="Optional"/>
+      <avprule id="Vendor-Specific-Application-Id" type="Optional"/>
+      <avprule id="User-Name" type="Optional"/>
+      <avprule id="Accounting-Sub-Session-Id" type="Optional"/>
+      <avprule id="Acct-Session-Id" type="Optional"/>
+      <avprule id="Acct-Multi-Session-Id" type="Optional"/>
+      <avprule id="Error-Reporting-Host" type="Optional"/>
+      <avprule id="Acct-Interim-Interval" type="Optional"/>
+      <avprule id="Accounting-Realtime-Required" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Event-Timestamp" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="ASR" code="274" type="Request">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Destination-Realm" type="Mandatory"/>
+      <avprule id="Destination-Host" type="Mandatory"/>
+      <avprule id="Auth-Application-Id" type="Mandatory"/>
+      <avprule id="User-Name" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="Route-Record" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="ASA" code="274" type="Answer">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Result-Code" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="User-Name" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Error-Message" type="Optional"/>
+      <avprule id="Error-Reporting-Host" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional" qual="*"/>
+      <avprule id="Redirect-Host" type="Optional" qual="*"/>
+      <avprule id="Redirect-Host-Usage" type="Optional"/>
+      <avprule id="Redirect-Max-Cache-Time" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="ST-Request" code="275" type="Request">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Destination-Realm" type="Mandatory"/>
+      <avprule id="Auth-Application-Id" type="Mandatory"/>
+      <avprule id="Termination-Cause" type="Mandatory"/>
+      <avprule id="Destination-Host" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="Route-Record" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="ST-Answer" code="275" type="Answer">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Result-Code" type="Optional"/>
+      <avprule id="Error-Message" type="Optional"/>
+      <avprule id="Error-Reporting-Host" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional" qual="*"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Redirect-Host" type="Optional" qual="*"/>
+      <avprule id="Redirect-Host-Usage" type="Optional"/>
+      <avprule id="Redirect-Max-Cache-Time" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="DWR" code="280" type="Request">
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+   </command>
+   <command name="DWA" code="280" type="Answer">
+      <avprule id="Result-Code" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Error-Message" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional" qual="*"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+   </command>
+   <command name="DPR" code="282" type="Request">
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Disconnect-Cause" type="Mandatory"/>
+   </command>
+   <command name="DPA" code="282" type="Answer">
+      <avprule id="Result-Code" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Error-Message" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="SL-Request" code="8388635" type="Request">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Auth-Application-Id" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Destination-Realm" type="Mandatory"/>
+      <avprule id="SL-Request-Type" type="Mandatory"/>
+      <avprule id="Destination-Host" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Subscription-Id" type="Optional" qual="*"/>
+      <avprule id="Policy-Counter-Identifier" type="Optional" qual="*"/>
+      <avprule id="Logical-Access-ID" type="Optional"/>
+      <avprule id="Physical-Access-ID" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="Route-Record" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="SL-Answer" code="8388635" type="Answer">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Auth-Application-Id" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Result-Code" type="Optional"/>
+      <avprule id="Experimental-Result" type="Optional"/>
+      <avprule id="Policy-Counter-Status-Report" type="Optional" qual="*"/>
+      <avprule id="Error-Message" type="Optional"/>
+      <avprule id="Error-Reporting-Host" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional" qual="*"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Redirect-Host" type="Optional" qual="*"/>
+      <avprule id="Redirect-Host-Usage" type="Optional"/>
+      <avprule id="Redirect-Max-Cache-Time" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="SN-Request" code="8388636" type="Request">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Destination-Realm" type="Mandatory"/>
+      <avprule id="Destination-Host" type="Mandatory"/>
+      <avprule id="Auth-Application-Id" type="Mandatory"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Policy-Counter-Status-Report" type="Optional" qual="*"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="Route-Record" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="SN-Answer" code="8388636" type="Answer">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Result-Code" type="Optional"/>
+      <avprule id="Experimental-Result" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Error-Message" type="Optional"/>
+      <avprule id="Error-Reporting-Host" type="Optional"/>
+      <avprule id="Redirect-Host" type="Optional" qual="*"/>
+      <avprule id="Redirect-Host-Usage" type="Optional"/>
+      <avprule id="Redirect-Max-Cache-Time" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional" qual="*"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+</dictionary>
diff --git a/example/diameter/launcher/deployments/aots/agents/ADML/stacks/diameter_base.0.xml b/example/diameter/launcher/deployments/aots/agents/ADML/stacks/diameter_base.0.xml
new file mode 100755 (executable)
index 0000000..0724bec
--- /dev/null
@@ -0,0 +1,439 @@
+<dictionary name="diameter_gen_base_rfc3588 | Application-Id: 0">
+   <vendor name="IETF" code="0"/>
+   <avp name="User-Name" code="1" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Class" code="25" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Session-Timeout" code="27" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Proxy-State" code="33" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Acct-Session-Id" code="44" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="OctetString"/>
+   </avp>
+   <avp name="Acct-Multi-Session-Id" code="50" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Event-Timestamp" code="55" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Time"/>
+   </avp>
+   <avp name="Acct-Interim-Interval" code="85" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Host-IP-Address" code="257" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Address"/>
+   </avp>
+   <avp name="Auth-Application-Id" code="258" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Acct-Application-Id" code="259" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Vendor-Specific-Application-Id" code="260" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Vendor-Id" type="Mandatory" qual="1*"/>
+         <avprule id="Auth-Application-Id" type="Optional"/>
+         <avprule id="Acct-Application-Id" type="Optional"/>
+      </grouped>
+   </avp>
+   <avp name="Redirect-Host-Usage" code="261" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-6">
+         <label data="0" alias="DONT_CACHE"/>
+         <label data="1" alias="ALL_SESSION"/>
+         <label data="2" alias="ALL_REALM"/>
+         <label data="3" alias="REALM_AND_APPLICATION"/>
+         <label data="4" alias="ALL_APPLICATION"/>
+         <label data="5" alias="ALL_HOST"/>
+         <label data="6" alias="ALL_USER"/>
+      </single>
+   </avp>
+   <avp name="Redirect-Max-Cache-Time" code="262" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Session-Id" code="263" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Origin-Host" code="264" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Supported-Vendor-Id" code="265" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Vendor-Id" code="266" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Firmware-Revision" code="267" may-encrypt="yes" v-bit="mustnot" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Result-Code" code="268" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32">
+         <label data="1001" alias="DIAMETER_MULTI_ROUND_AUTH"/>
+         <label data="2001" alias="DIAMETER_SUCCESS"/>
+         <label data="2002" alias="DIAMETER_LIMITED_SUCCESS"/>
+         <label data="3001" alias="DIAMETER_COMMAND_UNSUPPORTED"/>
+         <label data="3002" alias="DIAMETER_UNABLE_TO_DELIVER"/>
+         <label data="3003" alias="DIAMETER_REALM_NOT_SERVED"/>
+         <label data="3004" alias="DIAMETER_TOO_BUSY"/>
+         <label data="3005" alias="DIAMETER_LOOP_DETECTED"/>
+         <label data="3006" alias="DIAMETER_REDIRECT_INDICATION"/>
+         <label data="3007" alias="DIAMETER_APPLICATION_UNSUPPORTED"/>
+         <label data="3008" alias="DIAMETER_INVALID_HDR_BITS"/>
+         <label data="3009" alias="DIAMETER_INVALID_AVP_BITS"/>
+         <label data="3010" alias="DIAMETER_UNKNOWN_PEER"/>
+         <label data="4001" alias="DIAMETER_AUTHENTICATION_REJECTED"/>
+         <label data="4002" alias="DIAMETER_OUT_OF_SPACE"/>
+         <label data="4003" alias="DIAMETER_ELECTION_LOST"/>
+         <label data="4241" alias="DIAMETER_NO_AVAILABLE_POLICY_COUNTERS"/>
+         <label data="5001" alias="DIAMETER_AVP_UNSUPPORTED"/>
+         <label data="5002" alias="DIAMETER_UNKNOWN_SESSION_ID"/>
+         <label data="5003" alias="DIAMETER_AUTHORIZATION_REJECTED"/>
+         <label data="5004" alias="DIAMETER_INVALID_AVP_VALUE"/>
+         <label data="5005" alias="DIAMETER_MISSING_AVP"/>
+         <label data="5006" alias="DIAMETER_RESOURCES_EXCEEDED"/>
+         <label data="5007" alias="DIAMETER_CONTRADICTING_AVPS"/>
+         <label data="5008" alias="DIAMETER_AVP_NOT_ALLOWED"/>
+         <label data="5009" alias="DIAMETER_AVP_OCCURS_TOO_MANY_TIMES"/>
+         <label data="5010" alias="DIAMETER_NO_COMMON_APPLICATION"/>
+         <label data="5011" alias="DIAMETER_UNSUPPORTED_VERSION"/>
+         <label data="5012" alias="DIAMETER_UNABLE_TO_COMPLY"/>
+         <label data="5013" alias="DIAMETER_INVALID_BIT_IN_HEADER"/>
+         <label data="5014" alias="DIAMETER_INVALID_AVP_LENGTH"/>
+         <label data="5015" alias="DIAMETER_INVALID_MESSAGE_LENGTH"/>
+         <label data="5016" alias="DIAMETER_INVALID_AVP_BIT_COMBO"/>
+         <label data="5017" alias="DIAMETER_NO_COMMON_SECURITY"/>
+         <label data="5030" alias="DIAMETER_USER_UNKNOWN"/>
+         <label data="5063" alias="REQUESTED_SERVICE_NOT_AUTHORIZED"/>
+         <label data="5065" alias="IP_CAN_SESSION_NOT_AVAILABLE"/>
+      </single>
+   </avp>
+   <avp name="Product-Name" code="269" may-encrypt="yes" v-bit="mustnot" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Session-Binding" code="270" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Session-Server-Failover" code="271" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-3">
+         <label data="0" alias="REFUSE_SERVICE"/>
+         <label data="1" alias="TRY_AGAIN"/>
+         <label data="2" alias="ALLOW_SERVICE"/>
+         <label data="3" alias="TRY_AGAIN_ALLOW_SERVICE"/>
+      </single>
+   </avp>
+   <avp name="Multi-Round-Time-Out" code="272" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Disconnect-Cause" code="273" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-2">
+         <label data="0" alias="REBOOTING"/>
+         <label data="1" alias="BUSY"/>
+         <label data="2" alias="DO_NOT_WANT_TO_TALK_TO_YOU"/>
+      </single>
+   </avp>
+   <avp name="Auth-Request-Type" code="274" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="1-3">
+         <label data="1" alias="AUTHENTICATE_ONLY"/>
+         <label data="2" alias="AUTHORIZE_ONLY"/>
+         <label data="3" alias="AUTHORIZE_AUTHENTICATE"/>
+      </single>
+   </avp>
+   <avp name="Auth-Grace-Period" code="276" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Auth-Session-State" code="277" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="STATE_MAINTAINED"/>
+         <label data="1" alias="NO_STATE_MAINTAINED"/>
+      </single>
+   </avp>
+   <avp name="Origin-State-Id" code="278" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Failed-AVP" code="279" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="AVP" type="Mandatory" qual="1*"/>
+      </grouped>
+   </avp>
+   <avp name="Proxy-Host" code="280" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Error-Message" code="281" may-encrypt="yes" v-bit="mustnot" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="UTF8String"/>
+   </avp>
+   <avp name="Route-Record" code="282" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Destination-Realm" code="283" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Proxy-Info" code="284" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Proxy-Host" type="Mandatory"/>
+         <avprule id="Proxy-State" type="Mandatory"/>
+         <avprule id="AVP" type="Optional" qual="*"/>
+      </grouped>
+   </avp>
+   <avp name="Re-Auth-Request-Type" code="285" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="0-1">
+         <label data="0" alias="AUTHORIZE_ONLY"/>
+         <label data="1" alias="AUTHORIZE_AUTHENTICATE"/>
+      </single>
+   </avp>
+   <avp name="Accounting-Sub-Session-Id" code="287" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned64"/>
+   </avp>
+   <avp name="Redirect-Host" code="292" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterURI"/>
+   </avp>
+   <avp name="Destination-Host" code="293" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Error-Reporting-Host" code="294" may-encrypt="yes" v-bit="mustnot" m-bit="mustnot" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Termination-Cause" code="295" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="1-8">
+         <label data="1" alias="LOGOUT"/>
+         <label data="2" alias="SERVICE_NOT_PROVIDED"/>
+         <label data="3" alias="BAD_ANSWER"/>
+         <label data="4" alias="ADMINISTRATIVE"/>
+         <label data="5" alias="LINK_BROKEN"/>
+         <label data="6" alias="AUTH_EXPIRED"/>
+         <label data="7" alias="USER_MOVED"/>
+         <label data="8" alias="SESSION_TIMEOUT"/>
+      </single>
+   </avp>
+   <avp name="Origin-Realm" code="296" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="DiameterIdentity"/>
+   </avp>
+   <avp name="Experimental-Result" code="297" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="Vendor-Id" type="Mandatory"/>
+         <avprule id="Experimental-Result-Code" type="Mandatory"/>
+      </grouped>
+   </avp>
+   <avp name="Experimental-Result-Code" code="298" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="Inband-Security-Id" code="299" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <avp name="E2E-Sequence" code="300" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <grouped>
+         <avprule id="AVP" type="Mandatory" qual="2*"/>
+      </grouped>
+   </avp>
+   <avp name="Accounting-Record-Type" code="480" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="1-4">
+         <label data="1" alias="EVENT_RECORD"/>
+         <label data="2" alias="START_RECORD"/>
+         <label data="3" alias="INTERIM_RECORD"/>
+         <label data="4" alias="STOP_RECORD"/>
+      </single>
+   </avp>
+   <avp name="Accounting-Realtime-Required" code="483" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Enumerated" enum="1-3">
+         <label data="1" alias="DELIVER_AND_GRANT"/>
+         <label data="2" alias="GRANT_AND_STORE"/>
+         <label data="3" alias="GRANT_AND_LOSE"/>
+      </single>
+   </avp>
+   <avp name="Accounting-Record-Number" code="485" may-encrypt="yes" v-bit="mustnot" m-bit="must" p-bit="mustnot">
+      <single format-name="Unsigned32"/>
+   </avp>
+   <command name="CER" code="257" type="Request">
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Host-IP-Address" type="Mandatory" qual="1*"/>
+      <avprule id="Vendor-Id" type="Mandatory"/>
+      <avprule id="Product-Name" type="Mandatory"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Supported-Vendor-Id" type="Optional" qual="*"/>
+      <avprule id="Auth-Application-Id" type="Optional" qual="*"/>
+      <avprule id="Inband-Security-Id" type="Optional" qual="*"/>
+      <avprule id="Acct-Application-Id" type="Optional" qual="*"/>
+      <avprule id="Vendor-Specific-Application-Id" type="Optional" qual="*"/>
+      <avprule id="Firmware-Revision" type="Optional"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="CEA" code="257" type="Answer">
+      <avprule id="Result-Code" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Host-IP-Address" type="Mandatory" qual="1*"/>
+      <avprule id="Vendor-Id" type="Mandatory"/>
+      <avprule id="Product-Name" type="Mandatory"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Error-Message" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional" qual="*"/>
+      <avprule id="Supported-Vendor-Id" type="Optional" qual="*"/>
+      <avprule id="Auth-Application-Id" type="Optional" qual="*"/>
+      <avprule id="Inband-Security-Id" type="Optional" qual="*"/>
+      <avprule id="Acct-Application-Id" type="Optional" qual="*"/>
+      <avprule id="Vendor-Specific-Application-Id" type="Optional" qual="*"/>
+      <avprule id="Firmware-Revision" type="Optional"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="RAR" code="258" type="Request">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Destination-Realm" type="Mandatory"/>
+      <avprule id="Destination-Host" type="Mandatory"/>
+      <avprule id="Auth-Application-Id" type="Mandatory"/>
+      <avprule id="Re-Auth-Request-Type" type="Mandatory"/>
+      <avprule id="User-Name" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="Route-Record" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="RAA" code="258" type="Answer">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Result-Code" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="User-Name" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Error-Message" type="Optional"/>
+      <avprule id="Error-Reporting-Host" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional" qual="*"/>
+      <avprule id="Redirect-Host" type="Optional" qual="*"/>
+      <avprule id="Redirect-Host-Usage" type="Optional"/>
+      <avprule id="Redirect-Max-Cache-Time" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="ACR" code="271" type="Request">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Destination-Realm" type="Mandatory"/>
+      <avprule id="Accounting-Record-Type" type="Mandatory"/>
+      <avprule id="Accounting-Record-Number" type="Mandatory"/>
+      <avprule id="Acct-Application-Id" type="Optional"/>
+      <avprule id="Vendor-Specific-Application-Id" type="Optional"/>
+      <avprule id="User-Name" type="Optional"/>
+      <avprule id="Accounting-Sub-Session-Id" type="Optional"/>
+      <avprule id="Acct-Session-Id" type="Optional"/>
+      <avprule id="Acct-Multi-Session-Id" type="Optional"/>
+      <avprule id="Acct-Interim-Interval" type="Optional"/>
+      <avprule id="Accounting-Realtime-Required" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Event-Timestamp" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="Route-Record" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="ACA" code="271" type="Answer">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Result-Code" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Accounting-Record-Type" type="Mandatory"/>
+      <avprule id="Accounting-Record-Number" type="Mandatory"/>
+      <avprule id="Acct-Application-Id" type="Optional"/>
+      <avprule id="Vendor-Specific-Application-Id" type="Optional"/>
+      <avprule id="User-Name" type="Optional"/>
+      <avprule id="Accounting-Sub-Session-Id" type="Optional"/>
+      <avprule id="Acct-Session-Id" type="Optional"/>
+      <avprule id="Acct-Multi-Session-Id" type="Optional"/>
+      <avprule id="Error-Reporting-Host" type="Optional"/>
+      <avprule id="Acct-Interim-Interval" type="Optional"/>
+      <avprule id="Accounting-Realtime-Required" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Event-Timestamp" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="ASR" code="274" type="Request">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Destination-Realm" type="Mandatory"/>
+      <avprule id="Destination-Host" type="Mandatory"/>
+      <avprule id="Auth-Application-Id" type="Mandatory"/>
+      <avprule id="User-Name" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="Route-Record" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="ASA" code="274" type="Answer">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Result-Code" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="User-Name" type="Optional"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Error-Message" type="Optional"/>
+      <avprule id="Error-Reporting-Host" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional" qual="*"/>
+      <avprule id="Redirect-Host" type="Optional" qual="*"/>
+      <avprule id="Redirect-Host-Usage" type="Optional"/>
+      <avprule id="Redirect-Max-Cache-Time" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="STR" code="275" type="Request">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Destination-Realm" type="Mandatory"/>
+      <avprule id="Auth-Application-Id" type="Mandatory"/>
+      <avprule id="Termination-Cause" type="Mandatory"/>
+      <avprule id="User-Name" type="Optional"/>
+      <avprule id="Destination-Host" type="Optional"/>
+      <avprule id="Class" type="Optional" qual="*"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="Route-Record" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="STA" code="275" type="Answer">
+      <avprule id="Session-Id" type="Fixed"/>
+      <avprule id="Result-Code" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="User-Name" type="Optional"/>
+      <avprule id="Class" type="Optional" qual="*"/>
+      <avprule id="Error-Message" type="Optional"/>
+      <avprule id="Error-Reporting-Host" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional" qual="*"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+      <avprule id="Redirect-Host" type="Optional" qual="*"/>
+      <avprule id="Redirect-Host-Usage" type="Optional"/>
+      <avprule id="Redirect-Max-Cache-Time" type="Optional"/>
+      <avprule id="Proxy-Info" type="Optional" qual="*"/>
+      <avprule id="AVP" type="Optional" qual="*"/>
+   </command>
+   <command name="DWR" code="280" type="Request">
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+   </command>
+   <command name="DWA" code="280" type="Answer">
+      <avprule id="Result-Code" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Error-Message" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional" qual="*"/>
+      <avprule id="Origin-State-Id" type="Optional"/>
+   </command>
+   <command name="DPR" code="282" type="Request">
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Disconnect-Cause" type="Mandatory"/>
+   </command>
+   <command name="DPA" code="282" type="Answer">
+      <avprule id="Result-Code" type="Mandatory"/>
+      <avprule id="Origin-Host" type="Mandatory"/>
+      <avprule id="Origin-Realm" type="Mandatory"/>
+      <avprule id="Error-Message" type="Optional"/>
+      <avprule id="Failed-AVP" type="Optional" qual="*"/>
+   </command>
+</dictionary>
diff --git a/example/diameter/launcher/deployments/aots/agents/ADML/start.sh b/example/diameter/launcher/deployments/aots/agents/ADML/start.sh
new file mode 100755 (executable)
index 0000000..213a8e0
--- /dev/null
@@ -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
+./ADML `grep -v ^# $ARGS` &
+echo $! > .pid
+echo "Done !"
+
diff --git a/example/diameter/launcher/deployments/aots/agents/ADML/stop.sh b/example/diameter/launcher/deployments/aots/agents/ADML/stop.sh
new file mode 100755 (executable)
index 0000000..a4f1121
--- /dev/null
@@ -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/agents/HTTPMOCK/ACTIONS.md b/example/diameter/launcher/deployments/aots/agents/HTTPMOCK/ACTIONS.md
new file mode 100644 (file)
index 0000000..4a3548a
--- /dev/null
@@ -0,0 +1,20 @@
+# HTTPMOCK actions
+
+## serve_json
+
+Serve a json response body for specific method is given uri.
+
+Arguments: json, method, uri
+* json: json file for response body.
+* method: POST, PUT, GET, DELETE.
+* uri: uri where the json response is served.
+
+Example:
+```
+- action: DA/serve_json
+  arguments:
+    json: reponse.json
+    method: POST
+    uri: <uri-where-to-serve>
+```
+
diff --git a/example/diameter/launcher/deployments/aots/agents/HTTPMOCK/Provision.sh b/example/diameter/launcher/deployments/aots/agents/HTTPMOCK/Provision.sh
new file mode 100755 (executable)
index 0000000..7da49eb
--- /dev/null
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+usage() {
+  cat << EOF
+
+  Usage: $0 <test id> <step id> <test path> <address> <mock json> <method: POST|PUT|GET|DELETE> <uri>
+
+EOF
+  exit 1
+}
+
+[ -z "$7" ] && usage
+
+control_post_request=$(mktemp)
+cat << EOF > ${control_post_request}
+{
+  "control": {
+    "flowId": "$1",
+    "flowStepId": "$2",
+    "flowPath":"$3"
+  },
+  "action": {
+    "id": "serve_json",
+    "arguments": {
+      "json": "$5",
+      "method": "$6",
+      "uri": "$7"
+    }
+  }
+}
+EOF
+
+CONTROL_URI=http://$4/aots-control/v1/ctrl/action/annotate
+CURL="curl -s --http2-prior-knowledge"
+${CURL} -d@${control_post_request} -H "Content-Type: application/json" -X $6 ${CONTROL_URI}/serve_json >/dev/null
+rm ${control_post_request}
diff --git a/example/diameter/launcher/deployments/aots/agents/KAFKA/ACTIONS.md b/example/diameter/launcher/deployments/aots/agents/KAFKA/ACTIONS.md
new file mode 100644 (file)
index 0000000..bf7d7b1
--- /dev/null
@@ -0,0 +1,93 @@
+# KAFKA actions
+
+## consume_json
+
+Consume until reception of json message.
+
+Arguments: json[, auto_offset_reset, timeout, background, debug]
+* json: json file that we want to wait for ('any' to skip matching).
+* timeout: timeout for the operation in seconds.
+* auto_offset_reset: auto offset reset (earliest, latest, etc.). By default: latest.
+* background: "yes"|["no"]. Executes the step in background. By default is blocking, allowing to gather the result code for the step.
+* debug: "yes"|["no"]. Python kafka library debug information.
+
+Example:
+```
+- action: SMPC/consume_json
+  arguments:
+    json: expected.json
+    timeout: 5
+    auto_offset_reset: earliest
+```
+
+## produce_json
+
+Produces a json message to the kafka message bus.
+
+Arguments: json[, delay_ms, background, debug]
+* json: json file that we want to produce.
+* delay_ms: delay before producing in milliseconds.
+* background: "yes"|["no"]. Executes the step in background. By default is blocking, allowing to gather the result code for the step.
+* debug: "yes"|["no"]. Python kafka library debug information.
+
+Example:
+```
+- action: SMPC/produce_json:
+  arguments:
+    json: produced.json
+```
+
+## admin
+
+Administration actions.
+
+Arguments: operation[, debug]
+* operation: only 'clean_topic' implemented
+* debug: "yes"|["no"]. Python kafka library debug information.
+
+clean_topic: administration operation to clear message bus in case you want to consume reading earliest messages in the queue. By default, consumer offsets to latest message, but it won't see nothing if it is launched after production of the expected message. Depending on the way you define the test case, you will need clean or not the message bus.
+
+* Example(1): given an 'SMPC' kafka node:
+
+```
+- action: SMPC/admin:
+  arguments:
+    operation: clean_topic
+
+- action: AF/send_xml_to_entity
+  arguments:
+    xml: AAR.xml
+
+- action: SPMC/consume_json
+  arguments:
+    json: expected.json
+    auto_offset_reset: earliest
+```
+
+* Example(2): given an 'SMPC' kafka node:
+
+```
+- action: SPMC/consume_json
+  arguments:
+    json: expected.json
+    background: "yes"
+
+- action: AF/send_xml_to_entity
+  arguments:
+    xml: AAR.xml
+```
+
+* Example(3): given an 'SMPC' kafka node:
+
+```
+- action: SPMC/produce_json
+  arguments:
+    json: produced.json
+    delay_ms: 200
+    background: "yes"
+
+- action: AF/wait_xml_from_entity
+  arguments:
+    xml: ASR.xml
+```
+
diff --git a/example/diameter/launcher/deployments/aots/agents/KAFKA/Admin.py b/example/diameter/launcher/deployments/aots/agents/KAFKA/Admin.py
new file mode 100644 (file)
index 0000000..060faee
--- /dev/null
@@ -0,0 +1,92 @@
+#!/usr/bin/env python
+
+import os, sys, datetime, logging
+
+from kafka.admin import KafkaAdminClient
+from argparse import ArgumentParser
+
+from kafka.errors import (UnknownError, KafkaConnectionError, FailedPayloadsError,
+                          KafkaTimeoutError, KafkaUnavailableError,
+                          LeaderNotAvailableError, UnknownTopicOrPartitionError,
+                          NotLeaderForPartitionError, ReplicaNotAvailableError)
+
+
+def _exit(message = None, rc=0):
+  if (message): printMsg(message)
+  printMsg("Exiting [rc={}]".format(rc))
+  sys.exit(rc)
+
+
+def timeFormat():
+  return '%b.%d.%Y-%H.%M.%S.%f'
+
+
+def printMsg(message):
+  print ("[{}] [{}] {}".format(datetime.datetime.now().strftime(timeFormat()), GNode, message))
+
+
+class MyKafkaAdmin:
+    kafka_service = os.environ.get('KAFKA_SERVER')
+    kafka_port = os.environ.get('KAFKA_PORT')
+
+    if not kafka_service: kafka_service = "localhost"
+    if not kafka_port: kafka_port = "9092"
+
+    server = kafka_service + ':' + kafka_port
+
+    def __init__(self, topic, rtopic):
+
+      # Topic:
+      printMsg("Working topic: '{}'".format(topic))
+
+      # Action 'remove topic':
+      printMsg("Remove topic from message bus: {}".format(str(rtopic)))
+
+      if (rtopic):
+        admin = KafkaAdminClient(bootstrap_servers=self.server)
+        try:
+          admin.delete_topics([topic])
+        except UnknownTopicOrPartitionError as e:
+          printMsg("UnknownTopicOrPartitionError => missing messages for topic (removal considered successful)")
+
+
+def parse_arguments():
+
+  parser = ArgumentParser(description='Kafka Manager')
+  parser.add_argument('-n', '--node', help='Node name (spaces are removed)', required=True)
+  parser.add_argument('-t', '--topic', help='Kafka topic', required=True)
+  parser.add_argument('-r', '--remove-topic', help='Remove topic from message bus', required=False, action='store_true')
+  parser.add_argument('-d', '--debug', help='Debug kafka', required=False, action='store_true')
+
+  arguments = parser.parse_args()
+
+  return arguments
+
+
+#####################
+#      M A I N      #
+#####################
+
+if __name__ == "__main__":
+
+  arguments = parse_arguments()
+
+  GNode = arguments.node.replace(" ", "")
+  topic = arguments.topic
+  rtopic = arguments.remove_topic
+  debug = arguments.debug
+
+  printMsg("Starting")
+
+  if (debug):
+    now = datetime.datetime.now().strftime(timeFormat())
+    logfile='{}-Admin-{}.log'.format(GNode, now)
+    printMsg("Logging kafka in: '{}'".format(logfile))
+    logging.basicConfig(filename=logfile, format='%(asctime)s - %(message)s', datefmt=timeFormat(), level=logging.DEBUG)
+
+  try:
+    admin = MyKafkaAdmin(topic, rtopic)
+    _exit()
+
+  except Exception as e:
+    _exit(e, 1)
diff --git a/example/diameter/launcher/deployments/aots/agents/KAFKA/Consumer.py b/example/diameter/launcher/deployments/aots/agents/KAFKA/Consumer.py
new file mode 100644 (file)
index 0000000..5b3d56c
--- /dev/null
@@ -0,0 +1,177 @@
+#!/usr/bin/env python
+
+import os, sys, re, datetime, logging, json, signal
+
+from kafka import KafkaConsumer
+from json import loads
+from argparse import ArgumentParser
+
+
+def _exit(message = None, rc=0):
+  if (message): printMsg(message)
+  printMsg("Exiting [rc={}]".format(rc))
+  sys.exit(rc)
+
+def timeFormat():
+  return '%b.%d.%Y-%H.%M.%S.%f'
+
+
+def printMsg(message):
+  print ("[{}] [{}] {}".format(datetime.datetime.now().strftime(timeFormat()), GNode, message))
+
+
+class MyKafkaConsumer:
+    kafka_service = os.environ.get('KAFKA_SERVER')
+    kafka_port = os.environ.get('KAFKA_PORT')
+
+    if not kafka_service: kafka_service = "localhost"
+    if not kafka_port: kafka_port = "9092"
+
+    server = kafka_service + ':' + kafka_port
+
+    def __init__(self, topic, mregexp, aor, showReadWhenMatchingEnabled):
+
+        # Auto offset reset:
+        printMsg("Auto offset reset: '{}'".format(aor))
+
+        # Topic:
+        printMsg("Working topic: '{}'".format(topic))
+
+        # Show fulfilled:
+        self.showReadWhenMatchingEnabled = showReadWhenMatchingEnabled
+
+        # Json content:
+        self.json = None
+        if GJfile:
+          with open(GJfile) as json_file:
+            self.json = json.load(json_file)
+          printMsg("Established break condition for expected json ('{}'): {}".format(GJfile, self.json))
+        else:
+          printMsg("No expected json ('any' selected)")
+
+        # Interpreted as regexp indicator:
+        self.mregexp = mregexp
+        if GJfile:
+          if (self.mregexp): printMsg("Json file will be matched as regular expression")
+          else: printMsg("Json file will be matched as exact comparison")
+
+
+        self.consumer = KafkaConsumer(topic,
+                        bootstrap_servers=[self.server],
+                        auto_offset_reset=aor,
+                        enable_auto_commit=True,
+                        value_deserializer=lambda x:
+                        json.loads(x.decode('utf-8')))
+
+    def match(self, json):
+       if self.mregexp:
+         for key, value in  sorted(json.items()):
+           match = re.match(self.json[key], value)
+           if not match: return False
+         return True
+
+       else:
+         return (sorted(self.json.items()) == sorted(json.items()))
+
+    def receive_data(self):
+        printMsg('Reading data from kafka ...')
+
+        for message in self.consumer:
+            # Show read
+            if (self.showReadWhenMatchingEnabled if self.json else True):
+              printMsg('Data read: {}'.format(message))
+
+            # Break condition (in case that a expected json is blocking consumer):
+            if (self.json):
+              #if (sorted(self.json.items()) == sorted(message.value.items())):
+              if (self.match(message.value)):
+                printMsg('Message retrieved: condition fulfilled !')
+                return message
+              else:
+                global GSequence
+                GSequence += 1
+                #saved='{}.read.{}.json'.format(os.path.basename(GJfile), GSequence)
+                saved='{}.read.{}.json'.format(GJfile, GSequence)
+                with open(saved, 'w') as outfile:
+                  json.dump(message.value, outfile)
+
+                printMsg('Message retrieved: condition not fulfilled (saved {}) !'.format(saved))
+
+
+    def close (self):
+      self.consumer.close()
+
+
+def handler_function(signum, frame):
+  rc=0
+  if (GJfile): rc=1
+  _exit('Timeout !', rc)
+
+
+def parse_arguments():
+
+  parser = ArgumentParser(description='Kafka Consumer Simulator')
+  parser.add_argument('-n', '--node', help='Node name (spaces are removed)', required=True)
+  parser.add_argument('-t', '--topic', help='Kafka topic', required=True)
+  parser.add_argument('-o', '--auto-offset-reset', help='Auto offset reset (earliest, latest, etc.). By default: latest', required=False)
+  parser.add_argument('-f', '--file', help='Json file to consume (consumer will break execution with rc=0)', required=False)
+  parser.add_argument('-m', '--match-regexp', help='Match file as regexp (exact match by default)', required=False, action='store_true')
+  parser.add_argument('-w', '--timeout', help='Timeout (in seconds) for completion. Timeout will break execution with rc=1', required=False)
+  parser.add_argument('-d', '--debug', help='Debug kafka', required=False, action='store_true')
+  parser.add_argument('-r', '--show-read-when-matching-enabled', help='Show read messages. False by default when json file for matching is provided.', required=False, action='store_true')
+
+  arguments = parser.parse_args()
+
+  return arguments
+
+
+#####################
+#      M A I N      #
+#####################
+
+if __name__ == "__main__":
+
+  arguments = parse_arguments()
+
+  GNode = arguments.node.replace(" ", "")
+  topic = arguments.topic
+  auto_offset_reset = arguments.auto_offset_reset
+  GJfile = arguments.file
+  mregexp = arguments.match_regexp
+  timeout = arguments.timeout
+  debug = arguments.debug
+  show_read_when_matching_enabled = arguments.show_read_when_matching_enabled
+
+  GSequence = 0
+
+  printMsg("Starting")
+
+  if (debug):
+    now = datetime.datetime.now().strftime(timeFormat())
+    logfile='{}-Consumer-{}.log'.format(GNode, now)
+    printMsg("Logging kafka in: '{}'".format(logfile))
+    logging.basicConfig(filename=logfile, format='%(asctime)s - %(message)s', datefmt=timeFormat(), level=logging.DEBUG)
+
+  if (timeout):
+    printMsg("Established timeout for consumer: '{} seconds'".format(timeout))
+    # Signal to terminate after timeout:
+    signal.signal(signal.SIGALRM,handler_function)
+    signal.alarm(int(timeout))
+
+
+  if not GJfile:
+    if (mregexp): printMsg("To match json file as regexp, you must provide the file. See help (-h) !")
+    printMsg("Show messages read (matching is enabled): {}".format(str(show_read_when_matching_enabled)));
+
+  if not auto_offset_reset: auto_offset_reset='latest'
+
+  consumer = None
+  try:
+    consumer = MyKafkaConsumer(topic, mregexp, auto_offset_reset, show_read_when_matching_enabled)
+    consumer.receive_data()
+    consumer.close()
+    _exit()
+
+  except Exception as e:
+    consumer.close()
+    _exit(e, 1)
diff --git a/example/diameter/launcher/deployments/aots/agents/KAFKA/Producer.py b/example/diameter/launcher/deployments/aots/agents/KAFKA/Producer.py
new file mode 100644 (file)
index 0000000..f969c63
--- /dev/null
@@ -0,0 +1,125 @@
+#!/usr/bin/env python
+
+import os, sys, datetime, logging, json, time
+
+from kafka import KafkaProducer
+from json import loads
+from argparse import ArgumentParser
+
+
+def _exit(message = None, rc=0):
+  if (message): printMsg(message)
+  printMsg("Exiting [rc={}]".format(rc))
+  sys.exit(rc)
+
+
+def timeFormat():
+  return '%b.%d.%Y-%H.%M.%S.%f'
+
+
+def printMsg(message):
+  print ("[{}] [{}] {}".format(datetime.datetime.now().strftime(timeFormat()), GNode, message))
+
+
+class MyKafkaProducer:
+    kafka_service = os.environ.get('KAFKA_SERVER')
+    kafka_port = os.environ.get('KAFKA_PORT')
+
+    if not kafka_service: kafka_service = "localhost"
+    if not kafka_port: kafka_port = "9092"
+
+    server = kafka_service + ':' + kafka_port
+
+    def __init__(self):
+        self.producer = KafkaProducer(bootstrap_servers=[self.server],
+                        value_serializer=lambda x:
+                        json.dumps(x).encode('utf-8'))
+
+    def send_data(self, topic, data):
+        json = self.__read_json_data(data)
+        self.producer.send(topic, value=json)
+        self.producer.flush()
+        printMsg("Json produced on topic '{}': {}".format(topic, json))
+
+    def __read_json_data(self,jsonData):
+        with open(jsonData,'r') as schema_file:
+            schema_data = schema_file.read()
+        schema = json.loads(schema_data)
+        return schema
+
+    def set_producer_topic(self, kafka_topic):
+        self.kafka_topic = kafka_topic
+
+    def close(self):
+        self.producer.close()
+
+class MyProducer:
+    #def __init__(self):
+    producer = MyKafkaProducer()
+
+    def send_data(self, topic, data):
+        self.producer.send_data(topic, data)
+
+    def close(self):
+        self.producer.close()
+
+
+def parse_arguments():
+
+  parser = ArgumentParser(description='Kafka Producer Simulator')
+  parser.add_argument('-n', '--node', help='Node name (spaces are removed)', required=True)
+  parser.add_argument('-t', '--topic', help='Kafka topic', required=True)
+  parser.add_argument('-w', '--delay', help='Delay (in milliseconds) before producing', required=False)
+  parser.add_argument('-f', '--file', help='Json file to produce', required=True)
+  parser.add_argument('-d', '--debug', help='Debug kafka', required=False, action='store_true')
+
+  arguments = parser.parse_args()
+
+  return arguments
+
+
+#####################
+#      M A I N      #
+#####################
+
+if __name__ == "__main__":
+
+  arguments = parse_arguments()
+
+  GNode = arguments.node.replace(" ", "")
+  topic = arguments.topic
+  delay = arguments.delay
+  jfile = arguments.file
+  debug = arguments.debug
+
+  printMsg("Starting")
+
+  if (debug):
+    now = datetime.datetime.now().strftime(timeFormat())
+    logfile='{}-Producer-{}.log'.format(GNode, now)
+    printMsg("Logging kafka in: '{}'".format(logfile))
+    logging.basicConfig(filename=logfile, format='%(asctime)s - %(message)s', datefmt=timeFormat(), level=logging.DEBUG)
+
+  if not os.path.exists(jfile):
+    _exit("Invalid provided file '{}'".format(jfile), 1)
+
+  if (delay):
+    try:
+      delay = int(delay)
+    except:
+      _exit("Invalid provided delay '{}'. Must be an integer value !".format(str(delay)))
+
+    printMsg("Delay before producing: {} milliseconds ...".format(delay))
+    time.sleep(delay / 1000)
+    printMsg("Delay completed !".format(delay))
+
+  producer = None
+  try:
+    producer = MyProducer()
+    producer.send_data(topic, jfile)
+    producer.close()
+    _exit()
+
+  except Exception as e:
+    producer.close()
+    _exit(e, 1)
diff --git a/example/diameter/launcher/deployments/aots/launcher.py b/example/diameter/launcher/deployments/aots/launcher.py
new file mode 100755 (executable)
index 0000000..88317d6
--- /dev/null
@@ -0,0 +1,991 @@
+#!/usr/bin/env python
+# Anna Agents-Oriented Testing Setup Launcher
+
+# Requires PyYAML: pip install pyyaml
+
+import os, sys, datetime, glob, re, json, time, shutil, filecmp, datetime
+from argparse import ArgumentParser
+import xml.etree.ElementTree
+#from pathlib import Path
+
+from json import loads
+
+# PyYAML
+from yaml import load, dump
+try:
+    from yaml import CLoader as Loader, CDumper as Dumper
+except ImportError:
+    from yaml import Loader, Dumper
+
+from enum import Enum
+
+
+class AttributeType(Enum):
+  Mandatory = "Mandatory"
+  Optional = "Optional"
+
+
+def _exit(message = None, rc=0):
+  if (message): printMsg(message)
+  printMsg("Exiting [rc={}]".format(rc))
+  sys.exit(rc)
+
+
+def timeFormat():
+  return '%b.%d.%Y-%H.%M.%S'
+
+
+def printMsg(message):
+  print ("[{}] {}".format(datetime.datetime.now().strftime(timeFormat()), message))
+
+
+class YamlConfigParser():
+  """
+  Yaml parser
+  """
+  def __init__(self, yaml_config_file):
+    """
+    Convert the yaml file into a Python object
+    """
+    self.data = None
+    try:
+      with open(yaml_config_file, 'r') as ss:
+        self.data = load(ss, Loader=Loader)
+        ss.close()
+
+    except Exception:
+     raise
+
+  def getData(self):
+    return self.data
+
+  def show(self, options):
+    output = dump(self.data, Dumper=Dumper)
+    print(output)
+
+
+def writeFileContent(filename, content):
+  _file = open(filename, "w")
+  _file.write(content)
+  _file.close()
+
+
+def getNodeValue(dictionary, key, attributeType = AttributeType.Mandatory):
+
+  # Type checking:
+  if not isinstance(attributeType, AttributeType):
+    raise TypeError("'attributeType' must be an instance of AttributeType Enum")
+
+  value = None
+  try:
+    value = dictionary[key]
+  except Exception as e:
+    if (attributeType == AttributeType.Mandatory): _exit ("Missing mandatory key: {}".format(e) , 1)
+
+  #printMsg("getNodeValue -> {}: {}".format(key, value))
+  return value
+
+
+def get_parent_dir(_file):
+  abspath = os.path.abspath(_file)
+  return os.path.dirname(abspath)
+
+
+def is_absolute_path(path):
+  return os.path.isabs(path)
+
+
+def provision_begin(iplimit):
+  global GProvisionContent
+
+  GProvisionContent += '''test|report
+test|ip-limit|{}
+test|dump-stdout|yes
+'''.format(iplimit)
+
+def assertFile(filepath):
+  if not (os.path.exists(filepath)):
+    raise Exception ("File '{}' not found !".format(filepath))
+
+
+def assertAgentId(agent_id):
+  global agents_ADML_dir
+  global agents_KAFKA_dir
+  global agents_HTTPMOCK_dir
+
+  adml_node_file = agents_ADML_dir + "/{}.node".format(agent_id)
+  kafka_node_file = agents_KAFKA_dir + "/{}.node".format(agent_id)
+  httpmock_node_file = agents_HTTPMOCK_dir + "/{}.node".format(agent_id)
+  if (os.path.exists(adml_node_file)):
+    return "ADML"
+  elif (os.path.exists(kafka_node_file)):
+    return "KAFKA"
+  elif (os.path.exists(httpmock_node_file)):
+    return "HTTPMOCK"
+  else:
+    raise Exception ("Agent id '{}' not found as registered. Check the agents yaml configuration loaded !".format(agent_id))
+
+
+def modify_xml_avp_data(stepNumber, arguments):
+  assertSupportedKeys(stepNumber, arguments, ["xml", "new_xml", "xpath_value_list"])
+
+  _xml = getNodeValue(arguments, "xml")
+  if not is_absolute_path(_xml): _xml = GTcDir + "/" + _xml
+  assertFile(_xml)
+
+  new_xml = getNodeValue(arguments, "new_xml")
+  if not is_absolute_path(new_xml): new_xml = GTcDir + "/" + new_xml
+
+  # xml tree:
+  et = xml.etree.ElementTree.parse(_xml)
+  root = et.getroot()
+
+  xv_list = getNodeValue(arguments, "xpath_value_list")
+  for item in xv_list:
+    assertSupportedKeys(stepNumber, item, ["xpath", "value"])
+    xpath = getNodeValue(item, "xpath")
+    value = getNodeValue(item, "value")
+
+    # Replace operation:
+    targets = root.findall(xpath)
+    for target in targets:
+      if "data" in target.attrib:
+        target.attrib["data"] = value
+      elif "hex-data" in target.attrib:
+        target.attrib["hex-data"] = value
+
+  et.write(new_xml)
+
+
+def modify_json_key_value(stepNumber, arguments):
+  assertSupportedKeys(stepNumber, arguments, ["json", "new_json", "kpath_value_list"])
+
+  _json = getNodeValue(arguments, "json")
+  if not is_absolute_path(_json): _json = GTcDir + "/" + _json
+  assertFile(_json)
+
+  new_json = getNodeValue(arguments, "new_json")
+  if not is_absolute_path(new_json): new_json = GTcDir + "/" + new_json
+
+  # Replace operation:
+  with open(_json, 'r') as json_file:
+    json_data = json_file.read()
+    json_dict = json.loads(json_data)
+
+  kv_list = getNodeValue(arguments, "kpath_value_list")
+  for item in kv_list:
+    assertSupportedKeys(stepNumber, item, ["kpath", "value"])
+    kpath = getNodeValue(item, "kpath")
+    value = getNodeValue(item, "value")
+
+    key_path_list = kpath.split(".")
+    elem = key_path_list[0]
+    if (len(key_path_list) == 1):
+      json_dict[elem] = value
+
+    else:
+      aux_dict = json_dict[elem]
+      #print("aux dict: " + str(aux_dict))
+      for elem in key_path_list[1:-1]:
+        aux_dict = aux_dict[elem]
+
+      aux_dict[key_path_list[-1]] = value
+
+
+  content = json.dumps(json_dict)
+  writeFileContent(new_json, content)
+
+
+def system_cmd(stepNumber, arguments):
+  global GProvisionContent
+
+  assertSupportedKeys(stepNumber, arguments, ["shell", "file", "file_parameters"])
+
+  shell = getNodeValue(arguments, "shell", AttributeType.Optional)
+  _file = getNodeValue(arguments, "file", AttributeType.Optional)
+  fp = getNodeValue(arguments, "file_parameters", AttributeType.Optional)
+  file_parameters = ""
+  if (fp): file_parameters = fp
+
+  if (shell and _file):
+    raise Exception ("Both 'shell' and 'file' cannot be provided at system_cmd action !")
+
+  if (not shell and not _file):
+    raise Exception ("Neither 'shell' nor 'file' have been provided at system_cmd action !")
+
+  if (shell and fp):
+    raise Exception ("Both 'shell' and 'file_parameters' cannot be provided at system_cmd action !")
+
+  if (shell):
+    GProvisionContent += 'test|{}|sh-command|{}\n'.format(GTcNumber, shell)
+
+  elif (_file):
+    if not is_absolute_path(_file): _file = GTcDir + "/" + _file
+    assertFile(_file)
+    GProvisionContent += 'test|{}|sh-command|{} {}\n'.format(GTcNumber, _file, file_parameters)
+
+
+def process_test_case_step_GENERIC(stepNumber, action_id, arguments):
+  global GProvisionContent
+  global GTcNumber
+  global GIpLimit
+
+  if action_id == "sh_command":
+    GProvisionContent += 'test|{}|sh-command|{}\n'.format(GTcNumber, getNodeValue(arguments, "value"))
+  elif action_id == "ip_limit":
+    il = getNodeValue(arguments, "value")
+    if (il):
+      value = None
+      if (il == "launcher"):
+        value = GIpLimit
+      else:
+        value = il
+
+      GProvisionContent += 'test|{}|ip-limit|{}\n'.format(GTcNumber, value)
+
+  elif action_id == "timeout_ms":
+    GProvisionContent += 'test|{}|timeout|{}\n'.format(GTcNumber, getNodeValue(arguments, "value"))
+  elif action_id == "delay_ms":
+    GProvisionContent += 'test|{}|delay|{}\n'.format(GTcNumber, getNodeValue(arguments, "value"))
+  elif action_id == "modify_xml_avp_data":
+    # Always create the step or step references will be impossible to determine:
+    GProvisionContent += 'test|{}|sh-command|echo "{}"\n'.format(GTcNumber, "Replacing xml file: " + str(arguments))
+    modify_xml_avp_data(stepNumber, arguments)
+  elif action_id == "modify_json_key_value":
+    # Always create the step or step references will be impossible to determine:
+    GProvisionContent += 'test|{}|sh-command|echo "{}"\n'.format(GTcNumber, "Replacing json file: " + str(arguments))
+    modify_json_key_value(stepNumber, arguments)
+  elif action_id == "system_cmd":
+    system_cmd(stepNumber, arguments)
+  else:
+    raise Exception("ERROR: Step {}: unsupported generic action-id '{}'".format(stepNumber, action_id))
+
+
+def assertSupportedKeys(stepNumber, arguments, supported):
+  for key in arguments:
+    if not key in supported:
+      raise Exception("ERROR: Step {}: unsupported argument '{}' (allowed: {})".format(stepNumber, key, str(supported)))
+
+
+def process_test_case_step_ADML(stepNumber, agent_id, action_id, arguments):
+  global GProvisionContent
+  global GTcDir
+
+  if action_id == "send_xml_to_entity":
+    assertSupportedKeys(stepNumber, arguments, ["xml", "answers_to"])
+
+    xml = getNodeValue(arguments, "xml")
+    if not is_absolute_path(xml): xml = GTcDir + "/" + xml
+    assertFile(xml)
+
+    at = getNodeValue(arguments, "answers_to", AttributeType.Optional)
+    answers_to = ""
+    if (at): answers_to = "|{}".format(at)
+
+    GProvisionContent += 'test|{}|sendxml2e|{}{}\n'.format(GTcNumber, xml, answers_to)
+
+  elif action_id == "wait_xml_from_entity":
+    assertSupportedKeys(stepNumber, arguments, ["xml"])
+
+    xml = getNodeValue(arguments, "xml")
+    if not is_absolute_path(xml): xml = GTcDir + "/" + xml
+    assertFile(xml)
+
+    GProvisionContent += 'test|{}|waitfe-xml|{}\n'.format(GTcNumber, xml)
+
+  elif action_id == "send_xml_to_client":
+    assertSupportedKeys(stepNumber, arguments, ["xml", "answers_to"])
+
+    xml = getNodeValue(arguments, "xml")
+    if not is_absolute_path(xml): xml = GTcDir + "/" + xml
+    assertFile(xml)
+
+    at = getNodeValue(arguments, "answers_to", AttributeType.Optional)
+    answers_to = ""
+    if (at): answers_to = "|{}".format(at)
+
+    GProvisionContent += 'test|{}|sendxml2c|{}{}\n'.format(GTcNumber, xml, answers_to)
+
+  elif action_id == "wait_xml_from_client":
+    assertSupportedKeys(stepNumber, arguments, ["xml"])
+
+    xml = getNodeValue(arguments, "xml")
+    if not is_absolute_path(xml): xml = GTcDir + "/" + xml
+    assertFile(xml)
+
+    GProvisionContent += 'test|{}|waitfc-xml|{}\n'.format(GTcNumber, xml)
+
+  else:
+    raise Exception("ERROR: Step {}: unsupported generic action-id '{}' for ADML node type (agent '{}')".format(stepNumber, action_id, agent_id))
+
+
+def process_test_case_step_HTTPMOCK(stepNumber, agent_id, action_id, arguments):
+  global GProvisionContent
+  global GTcDir
+  global agents_HTTPMOCK_dir
+
+  if action_id == "serve_json":
+    assertSupportedKeys(stepNumber, arguments, ["json", "method", "uri"])
+
+    json = getNodeValue(arguments, "json")
+    if not is_absolute_path(json): json = GTcDir + "/" + json
+    assertFile(json)
+
+    method = getNodeValue(arguments, "method")
+    uri = getNodeValue(arguments, "uri")
+
+
+    # Provision script is in the form: agents/HTTPMOCK/<agent-id>-provision.sh
+    provision_script = agents_HTTPMOCK_dir + "/" + agent_id + "-provision.sh "
+    args = str(GTcNumber) + " " + str(stepNumber) + " " + GTcDir + " \"" + json + "\" " + method + " \"" + uri + "\""
+    GProvisionContent += 'test|{}|sh-command|{}\n'.format(GTcNumber, provision_script + args)
+
+  else:
+    raise Exception("ERROR: Step {}: unsupported generic action-id '{}' for HTTPMOCK node type (agent '{}')".format(stepNumber, action_id, agent_id))
+
+
+def process_test_case_step_KAFKA(stepNumber, agent_id, action_id, arguments):
+  global GProvisionContent
+  global GTcDir
+  global agents_KAFKA_dir
+
+  if action_id == "consume_json":
+    assertSupportedKeys(stepNumber, arguments, ["json", "timeout", "auto_offset_reset", "background", "debug"])
+
+    json = getNodeValue(arguments, "json")
+    if json != "any":
+      if not is_absolute_path(json): json = GTcDir + "/" + json
+      assertFile(json)
+
+    autoOffsetReset = ""
+    aor = getNodeValue(arguments, "auto_offset_reset", AttributeType.Optional)
+    if (aor): autoOffsetReset = aor
+
+    timeout = 0
+    to = getNodeValue(arguments, "timeout", AttributeType.Optional)
+    if(to): timeout = to
+
+    background = ""
+    bck = getNodeValue(arguments, "background", AttributeType.Optional)
+    if (bck == "yes"): background = "&"
+
+    debug = "no"
+    deb = getNodeValue(arguments, "debug", AttributeType.Optional)
+    if (deb): debug = deb
+
+    # Consumer script is in the form: agents/KAFKA/<agent-id>-consumer.sh
+    consumer_script = agents_KAFKA_dir + "/" + agent_id + "-consumer.sh "
+    args = json + " " + autoOffsetReset + " " + str(timeout) + " " + debug
+    GProvisionContent += 'test|{}|sh-command|{}{}\n'.format(GTcNumber, consumer_script + args, background)
+
+  elif action_id == "produce_json":
+    assertSupportedKeys(stepNumber, arguments, ["json", "delay_ms", "background", "debug"])
+
+    json = getNodeValue(arguments, "json")
+    if not is_absolute_path(json): json = GTcDir + "/" + json
+    assertFile(json)
+
+    delay_ms = 0
+    dl = getNodeValue(arguments, "delay_ms", AttributeType.Optional)
+    if(dl): delay_ms = dl
+
+    background = ""
+    bck = getNodeValue(arguments, "background", AttributeType.Optional)
+    if (bck == "yes"): background = "&"
+
+    debug = "no"
+    deb = getNodeValue(arguments, "debug", AttributeType.Optional)
+    if (deb): debug = deb
+
+    # Producer script is in the form: agents/KAFKA/<agent-id>-producer.sh
+    producer_script = agents_KAFKA_dir + "/" + agent_id + "-producer.sh "
+    args = json + " " + str(delay_ms) + " " + debug
+    GProvisionContent += 'test|{}|sh-command|{}{}\n'.format(GTcNumber, producer_script + args, background)
+
+  elif action_id == "admin":
+    assertSupportedKeys(stepNumber, arguments, ["operation", "debug"])
+
+    operation = getNodeValue(arguments, "operation")
+
+    debug = "no"
+    deb = getNodeValue(arguments, "debug", AttributeType.Optional)
+    if (deb): debug = deb
+
+    # Admin script is in the form: agents/KAFKA/<agent-id>-admin.sh
+    admin_script = agents_KAFKA_dir + "/" + agent_id + "-admin.sh "
+    args = operation + " " + debug
+    GProvisionContent += 'test|{}|sh-command|{}\n'.format(GTcNumber, admin_script + args)
+
+  else:
+    raise Exception("ERROR: Step {}: unsupported generic action-id '{}' for KAFKA node type (agent '{}')".format(stepNumber, action_id, agent_id))
+
+
+def process_test_case_step(stepNumber, dictionary):
+
+  action = getNodeValue(dictionary, "action")
+  arguments = getNodeValue(dictionary, "arguments")
+
+  # Action is in the form '[agent id/]<action id>':
+  # OPTIONAL: agent_id
+  # MANDATORY: action_id
+  agent_id = None
+  agent_template = None
+  try:
+    agent_id, action_id = action.split('/')
+    agent_template = assertAgentId(agent_id)
+
+  except ValueError:
+    action_id = action
+    pass
+
+  # Actions for ADML
+  if agent_template == "ADML":
+    process_test_case_step_ADML(stepNumber, agent_id, action_id, arguments)
+  elif agent_template == "KAFKA":
+    process_test_case_step_KAFKA(stepNumber, agent_id, action_id, arguments)
+  elif agent_template == "HTTPMOCK":
+    process_test_case_step_HTTPMOCK(stepNumber, agent_id, action_id, arguments)
+  else:
+    process_test_case_step_GENERIC(stepNumber, action_id, arguments)
+
+  #trace = "Step {}, Agent-Id '{}', Action-Id '{}', Parameters: {}"
+  #print(trace.format(stepNumber, str(agent_id), action_id, str(arguments)))
+
+
+def  process_test_case_yml(testcaseList):
+  for step in testcaseList:
+    indx = testcaseList.index(step)
+    process_test_case_step(indx+1, step)
+
+
+def provision_test_case(filename, testcaseList):
+
+  global GTcNumber
+  global GTcDir
+  global GIdsVsDescs
+  global GProvisionContent
+  GTcNumber += 1
+  GTcDir = get_parent_dir(filename)
+
+  id_desc = "{} : {}".format(GTcNumber, filename)
+  GIdsVsDescs += id_desc + "\n"
+  tc_desc = "test case '{}'".format(id_desc)
+
+  printMsg("Provisioning {} ...".format(tc_desc))
+
+  # Set test case description
+  GProvisionContent += 'test|{}|description|{}\n'.format(GTcNumber, filename)
+
+  # Process the yml definition for the test case
+  process_test_case_yml(testcaseList)
+
+
+def provision_tests(files):
+  for filename in files.splitlines():
+    if (filename[0] == "#"):
+      printMsg("Ignoring commented test case: '{}'".format(filename))
+    else:
+      # Test case is a list of steps:
+      tc = YamlConfigParser(filename)
+      provision_test_case(filename, tc.getData())
+
+
+def parse_arguments():
+
+  parser = ArgumentParser(description='Anna Agents-Oriented Testing Setup Launcher')
+  parser.add_argument('-t', '--tests-dir', help='Tests parent directory where to find .yml files (from the next directories level)', required=True)
+  parser.add_argument('-k', '--keep-list-if-exists', help='Keeps intact the list of test cases (<test-dir>/launcher.list), creates it if missing', required=False, action='store_true')
+  parser.add_argument('-s', '--stop-adml-at-the-end', help='At the end, ADML keeps running to ease debugging. You could force stop with this option', required=False, action='store_true')
+  parser.add_argument('-i', '--interactive', help='Interactive execution to ease debugging of test cases', required=False, action='store_true')
+  parser.add_argument('-d', '--dry-run', help='Used to test and debug provision, no execution is launched', required=False, action='store_true')
+  parser.add_argument('-p', '--ip-limit', help="In-Progress limit is the number of coexisting In-Progress State test cases. Defaults to 1 (sequential), -1 would be 'no limit').", required=False)
+  parser.add_argument('-r', '--ttps', help="Rate of test cases launched (test ticks per second). By default 50 (recommended for monothread version).", required=False)
+
+
+  arguments = parser.parse_args()
+
+  return arguments
+
+
+def start_agents():
+  # At the moment, only ADML is started (KAFKA/HTTPMOCK agents uses scripts):
+  global agents_ADML_dir
+
+  rc = 1
+  maxRetries = 5
+  retry = 0
+  os.system(agents_ADML_dir + "/stop.sh")
+  os.system(agents_ADML_dir + "/start.sh")
+
+  rc = adml_operation("node >/dev/null")
+  while rc != 0:
+    retry += 1
+    if (retry > maxRetries): break
+    time.sleep(1)
+    printMsg("Check ADML health retry ({}/{}) ...".format(retry, maxRetries))
+    rc = adml_operation("node >/dev/null")
+
+  return rc
+
+
+def adml_operation(arguments):
+  global agents_ADML_dir
+
+  rc = os.system(agents_ADML_dir + "/operation.sh " + arguments)
+  return rc
+
+
+def adml_operation_output(operation):
+  global agents_ADML_dir
+
+  output = os.popen(agents_ADML_dir + "/operation.sh {}".format(operation)).read()
+  return output
+
+
+def collect_results(abs_tdir):
+  global agents_ADML_dir
+
+  # Logs directory:
+  logs_dir = abs_tdir + ".logs"
+  shutil.rmtree(logs_dir, ignore_errors=True)
+  os.mkdir(logs_dir)
+  os.mkdir(logs_dir + "/traffic")
+  os.mkdir(logs_dir + "/counters")
+  os.mkdir(logs_dir + "/test-reports")
+  os.mkdir(logs_dir + "/debug")
+
+  # Summary states:
+  print("\n\n")
+  printMsg("Retrieving tests summary states ...")
+  statesOUTPUT = adml_operation_output("\"test|summary-states\"")
+  writeFileContent(logs_dir + "/tests.summary-states", statesOUTPUT)
+
+  # Summary counts:
+  print("\n\n")
+  printMsg("Retrieving tests summary counts ...")
+  verdictOUTPUT = adml_operation_output("\"test|summary-counts\"")
+  writeFileContent(logs_dir + "/tests.summary-counts", verdictOUTPUT)
+  verdictRC = 1
+  if "PASS" in verdictOUTPUT: verdictRC = 0
+
+  # Traffic logs:
+  # ADML
+  printMsg("Retrieving diameter traffic logs ...")
+  for f in glob.glob(agents_ADML_dir + "/*.log"):
+    shutil.copy(f, logs_dir + "/traffic")
+  for f in glob.glob(agents_ADML_dir + "/*.xml"):
+    shutil.copy(f, logs_dir + "/traffic")
+  # KAFKA
+  printMsg("Retrieving kafka traffic logs ...")
+  for f in glob.glob(agents_KAFKA_dir + "/*.log"):
+    shutil.copy(f, logs_dir + "/traffic")
+  for f in glob.glob(agents_KAFKA_dir + "/*.json"):
+    shutil.copy(f, logs_dir + "/traffic")
+
+  # HTTPMOCK
+  #printMsg("Retrieving httpmock activity logs ...")
+  #...
+
+  printMsg("Generating junit report ...")
+  writeFileContent(logs_dir + "/junit.xml", adml_operation_output("\"test|junit\""))
+  printMsg("Generating tests summary ...")
+  writeFileContent(logs_dir + "/tests.summary", adml_operation_output("\"test|summary\""))
+
+  printMsg("Generating tests oam reports ...")
+  writeFileContent(logs_dir + "/tests.oam", adml_operation_output("\"show-oam\""))
+  printMsg("Generating tests statistics ...")
+  writeFileContent(logs_dir + "/tests.stats", adml_operation_output("\"show-stats\""))
+  printMsg("Dumping ADML process context information ...")
+  adml_operation("\"context|{}\" >/dev/null".format(logs_dir + "/adml.context"))
+  adml_operation("forceCountersRecord >/dev/null")
+
+  # Move 'counters' and 'test-reports'
+  printMsg("Retrieving counters information ...")
+  for f in glob.glob(agents_ADML_dir + "/counters/*"):
+    shutil.copy(f, logs_dir + "/counters")
+  printMsg("Retrieving tests reports information ...")
+  for f in glob.glob(agents_ADML_dir + "/test-reports/*"):
+    shutil.copy(f, logs_dir + "/test-reports")
+
+  # Debug:
+  printMsg("Retrieving debug information ...")
+  for f in glob.glob(agents_ADML_dir + "/launcher.trace*"):
+    shutil.copy(f, logs_dir + "/debug")
+
+  # Backup tests directory itself (some replacements could be happened):
+  target_once_executed = logs_dir + "/debug/" + os.path.basename(abs_tdir)
+  shutil.copytree(abs_tdir, target_once_executed) # , symlinks=True)
+
+  return statesOUTPUT, verdictOUTPUT, verdictRC
+
+
+def interactive_execution():
+  printMsg("Starting interactive execution of test cases")
+
+  stay = True
+  while stay:
+    print("--------------------------------------------")
+    adml_operation("\"test|summary-counts\"")
+    print("--------------------------------------------")
+
+    print ('''
+    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.  Reset statistics & counters
+    10. Auto reset
+    11. Reports configuration
+    ''')
+
+    opt=input("Input option: \n")
+    if opt=="h0":
+      print("Just exit this menu")
+    elif opt=="0":
+      stay = False
+
+    elif opt=="h1":
+      print("Updates the current test pointer position.")
+    elif opt=="1":
+      _id=input("Input the test case id: ")
+      adml_operation("\"test|goto|{}\"".format(_id))
+
+    elif opt=="h2":
+      print("Show programmed test case for id provided, current 'in-process' test case when missing.")
+    elif opt=="2":
+      _id=input("Input the test case id [empty will show current]: ")
+      if _id: _id="|" + _id
+      adml_operation("\"test|look{}\"".format(_id))
+
+    elif opt=="h3":
+      print('''
+Starts/resume the provided number of test ticks per second (ttps). The ADML starts
+with the event trigger system suspended, and this operation is neccessary to begin
+those cases which need this time event (internal triggering). Some other test cases
+could be started through external events (first test case event could be programmed
+to wait specific message), but is not usual this external mode and neither usual to
+mix triggering types. Normally, you will pause/stop new test launchs providing 0 as
+ttps value, and also you could dynamically modify the load rate updating that value.
+If a test case has N messages then 'ttps * N' will be the virtual number of messages
+managed per second when no bottleneck exists.
+
+Provide 0 in order to stop the timer triggering.
+
+The timer manager resolution currently harcoded allows a maximum  of 50 events
+per second. To reach greater rates ADML will join synchronously the needed number of
+new time-triggered test cases per a single event, writting a warning-level trace to
+advice about the risk of burst sendings and recommend launching multiple instances
+to achieve such load with a lower rate per instance.''')
+    elif opt=="3":
+      ttps=input("Input the test ticks per second (0: stops the test rate clock): ")
+      adml_operation("\"test|ttps|{}\"".format(ttps))
+
+    elif opt=="h4":
+      print('''
+Forces the execution of the next test case(s) without waiting for test manager tick.
+Provide an integer value for 'sync-amount' to send a burst synchronous amount of the
+next programmed test cases (1 by default). This event works regardless the timer tick
+function, but it is normally used with the test manager tick stopped.''')
+    elif opt=="4":
+      amount=input("Input the number of tests to execute synchronously [1 by default]: ")
+      if not amount: amount=1
+      adml_operation("\"test|next|{}\"".format(amount))
+
+    elif opt=="h5":
+      print('''
+In-progress limit of test cases. No new test cases will be launched over this value
+(test Manager tick work will be ignored). Zero-value is equivalent to stop the clock.
+tick, -1 is used to specify 'no limit'. If missing amount, the limit and current
+amount of in-progress test cases will be shown.
+
+Default is 1 to ensure sequential execution for testcases (function test mode).
+For system test, it is recommended to set '-1' (no limit).''')
+    elif opt=="5":
+      print("\nTypical 'in-progress limit' values:")
+      print("-1: no limit")
+      print(" 0: similar to stop the test rate clock")
+      print(" 1: sequential execution for testcases (function test mode)\n")
+      limit=input("Input the desired in-progress limit amount [show current by default]: ")
+      if limit: limit = "|" + limit
+      adml_operation("\"test|ip-limit{}\"".format(limit))
+
+    elif opt=="h6":
+      print('''
+Makes interactive a specific test case id. The amount is the margin of execution steps
+to be done. With amount of '0' the test case is selected to be interactive, but no step
+is executed. Then you have to interact with positive amounts (usually 1), executing the
+provided number of steps if they are ready and fulfill the needed conditions. The value
+of 0, implies no execution steps margin, which could be useful to 'freeze' a test in the
+middle of its execution.  You could also provide -1 to make it non-interactive resuming
+it from the current step.
+By default, current test case id is selected for interaction.''')
+    elif opt=="6":
+      amount=input("Input the number of steps to execute (-1: resumes the test case; 0: freezes it) [1 by default]: ")
+      if not amount: amount = 1
+      test_id=input("Input the test id [current by default (remember to set a valid pointer with 'go to')]: ")
+      if test_id: test_id = "|" + test_id
+      adml_operation("\"test|interact|{}{}\"".format(amount, test_id))
+
+    elif opt=="h7":
+      print('''
+Reset the test case for id provided, all the tests when missing. It could be hard/soft:
+- hard: you probably may need to stop the load rate before. This operation initializes
+  all test cases regardless their states.
+- soft: only for finished cases (those with 'Success' or 'Failed' states). It does not
+  affect to test cases with 'InProgress' state.''')
+    elif opt=="7":
+      rtype=input("Input the reset type (soft/hard) [hard]: ")
+      if not rtype: rtype = "hard"
+      test_id=input("Input the test id [all tests by default]: ")
+      if test_id: test_id = "|" + test_id
+      adml_operation("\"test|reset|{}{}\"".format(rtype, test_id))
+
+    elif opt=="h8":
+      print('''
+Restarts the whole programmed test list when finished the amount number of times (repeats
+forever if value -1 is provided). This is disabled by default (amount = 0): testing trigger
+system will enter suspended state until new ttps operation is received and a soft reset has
+been done before. Test cases state & data will be reset (when achieved again), but general
+statistics and counters will continue measuring until reset with 'collect' operation.''')
+    elif opt=="8":
+      amount=input("Input the number of cycle repeats (-1: repeats forever; 0: disables repetitions): ")
+      adml_operation("\"test|repeats|{}\"".format(amount))
+
+    elif opt=="h9":
+      print('''
+Reset statistics and counters to start a new test stage of performance measurement.
+This affects to final logs retrieved on exit (option 0).''')
+    elif opt=="9":
+      adml_operation("\"collect\"")
+
+    elif opt=="h10":
+      print('''
+When cycling, current test cases can be soft (default) or hard reset. If no timeout has
+been configured for the test case, hard reset could prevent stuck on the next cycle for
+those test cases still in progress.''')
+    elif opt=="10":
+      rtype=input("Input the reset type (soft/hard) [hard]: ")
+      if not rtype: rtype = "hard"
+      adml_operation("\"test|auto-reset|{}\"".format(rtype))
+
+    elif opt=="h11":
+      print('''
+Enables/disables report generation for a certain test case state: initialized, in-progress,
+failed or success (also 'all' and 'none' reserved words could be used). This applies to report
+summary (final logs retrieved) and automatic dumps during testing where only failed or
+successful states will appear: every time a test case is finished its xml representation will
+be dump on a file under (logs test-reports) with the name:
+
+   'cycle-<cycle id>.testcase-<test case id>.xml'.
+
+By default, all the states are disabled to avoid IO overload. In most of cases not all the
+tests are going to fail then you could enable only such failed dumps. Anyway you could set
+the reports visibility to fit your needs in a given situation.
+
+Also you could enable hexadecimal representation for diameter messages on reports.''')
+    elif opt=="11":
+      print("\nInput the report target operation, capital letters to enable:")
+      rtype=input("(I/i)nitialized, in-(P/p)rogress, (F/f)ailed, (S/s)uccess, (A/a)ll, (N/n)one [A: all will be enabled by default]: ")
+      if not rtype: rtype = "A"
+      target = None
+      enable = "no"
+      upper_rtype = rtype.upper()
+      if (upper_rtype == rtype): enable = "yes"
+      if (upper_rtype == "I" ):
+        target="initialized"
+      elif (upper_rtype == "P"):
+        target="in-progress"
+      elif (upper_rtype == "F"):
+        target="failed"
+      elif (upper_rtype == "S"):
+        target="success"
+      elif (upper_rtype == "A"):
+        target="all"
+      elif (upper_rtype == "N"):
+        target="none"
+
+      if not target:
+        print("Invalid target. Try again !")
+      else:
+        print(" - Target selected: " + target)
+        print(" - Enable:          " + enable)
+        adml_operation("\"test|report|{}|{}\"".format(target, enable))
+
+      enableHex=input("\nEnable/Disable hexadecimal dumps for diameter messages in test reports (yes/no) [no]: ")
+      if not enableHex: enableHex = "no"
+      adml_operation("\"test|report-hex|{}\"".format(enableHex))
+
+    elif opt !="":
+      print("\nInvalid choice. Try again !")
+
+
+    if stay: input("\nPress ENTER to continue ...\n")
+
+
+
+#####################
+#      M A I N      #
+#####################
+
+if __name__ == "__main__":
+
+  # Agents:
+  abspath = os.path.abspath(__file__)
+  dname = os.path.dirname(abspath)
+  agents_KAFKA_dir = dname + "/agents/KAFKA"
+  agents_HTTPMOCK_dir = dname + "/agents/HTTPMOCK"
+  agents_ADML_dir = dname + "/agents/ADML"
+
+  # Injected environment variables (could be used in system_cmd commands:
+  os.putenv("AGENTS_DIR", dname + "/agents")
+
+  arguments = parse_arguments()
+
+  tdir = arguments.tests_dir
+  keep = arguments.keep_list_if_exists
+  stopAdml = arguments.stop_adml_at_the_end
+  interactive = arguments.interactive
+  dryrun = arguments.dry_run
+  iplimit = arguments.ip_limit
+  ttps = arguments.ttps
+
+  # Tests list: 'launcher.list':
+  llist = tdir + "/launcher.list"
+  llist_current = llist + ".updated"
+
+  # Auxiliary:
+  abs_tdir = get_parent_dir(llist)
+  llist_exists = os.path.exists(llist)
+
+  # Create the current list of test cases:
+  list_content = None
+  list_content = os.popen("find {} -mindepth 2 -name \"*.yml\" | sort -t'/'".format(tdir)).read()
+
+  # Performance:
+  if not ttps: ttps = 50
+  if not iplimit: iplimit = 1
+  if iplimit == "auto":
+    printMsg("AUTO IP-LIMIT ALGORITHM IS UNDER CONSTRUCTION (configuring value 1) ... ")
+    iplimit = 1
+
+  GIpLimit = iplimit
+
+
+  if llist_exists:
+    # Detect updates:
+    writeFileContent(llist_current, list_content)
+    if filecmp.cmp(llist, llist_current):
+      os.remove(llist_current)
+    else:
+      printMsg("The list '{}' has been created".format(llist_current))
+      printMsg("Warning: detected local changes (may be commented test cases) in '{}' regarding current tests list '{}'".format(llist, llist_current))
+
+    # Respect existing list (except if -k is not provided):
+    if keep:
+      printMsg("Reuse the current list '{}'".format(llist))
+      with open(llist) as llist_file:
+        list_content = llist_file.read()
+
+  if not llist_exists or not keep:
+    writeFileContent(llist, list_content)
+    printMsg("The list '{}' has been created".format(llist))
+
+
+  try:
+    global GTcNumber
+    global GProvisionContent
+    global GIdsVsDescs
+    GTcNumber = 0
+    GProvisionContent = ""
+    GIdsVsDescs = ""
+
+    provision_begin(iplimit)
+    provision_tests(list_content)
+
+    llist_of_operations = abs_tdir + "/launcher.list.adml_operations"
+    lids = abs_tdir + "/launcher.ids"
+    writeFileContent(llist_of_operations, GProvisionContent)
+    writeFileContent(lids, GIdsVsDescs)
+
+    if dryrun:
+      if interactive: printMsg("Interactive mode is not reached at dry-run mode")
+      _exit("Dry-run execution finished", 0)
+
+    rc = start_agents()
+    if (rc != 0):
+      _exit("Failed to initiate ADML director!", 1)
+
+    # Provision test cases in ADML:
+    adml_operation("-f " + llist_of_operations + " >/dev/null")
+
+
+    if (interactive):
+      interactive_execution()
+
+    else:
+      # Initial time:
+      t_initial = datetime.datetime.now()
+
+      # Start test cases:
+      adml_operation("\"test|ttps|" + str(ttps) + "\"")
+
+      # Estimated time to finish: worst case is timeout (we assume maximum timeout of 15 seconds per test case):
+      # worstTime = 15*GTcNumber
+
+      # We will check the progress: initialized test cases must be 0 again (cycle completed).
+      # But this only is guaranteed if all the tests define a timeout. If not, a wait step could stuck
+      #  any test eternally (worstTime is used to exit the following loop):
+      cycle = 0
+      #sleepTime=15
+      #maxCycles=GTcNumber
+      # Better resolution:
+      #sleepTime=1
+      maxCycles=15*GTcNumber
+
+      while True:
+        cycle += 1
+        #time.sleep(sleepTime)
+        time.sleep(1)
+        output = adml_operation_output("\"test|finished\"")
+        if "test|finished => {}".format(GTcNumber) in output: break
+        if (cycle > maxCycles): break
+        #printMsg("Checking progress ({}/{}) ...".format(cycle, maxCycles))
+        if not (cycle % 60): printMsg("Working ...")
+
+      # Final time:
+      t_final = datetime.datetime.now()
+      t_elapsed = t_final - t_initial
+      elapsedOUTPUT = "\nElapsed time in seconds: {}\n\n".format(t_elapsed.total_seconds())
+
+    # Collect results at tests-directory:
+    statesOUTPUT, verdictOUTPUT, verdictRC = collect_results(abs_tdir)
+
+    # Stop ADML:
+    if stopAdml:
+      printMsg("Stoping ADML director ...")
+      time.sleep(6)
+      os.system(agents_ADML_dir + "/stop.sh >/dev/null")
+
+    # Exit with verdict:
+    _exit(statesOUTPUT + elapsedOUTPUT + verdictOUTPUT, verdictRC)
+
+  except Exception as e:
+    _exit(e, 1)
diff --git a/example/diameter/launcher/deployments/aots/loader.py b/example/diameter/launcher/deployments/aots/loader.py
new file mode 100755 (executable)
index 0000000..608566f
--- /dev/null
@@ -0,0 +1,352 @@
+#!/usr/bin/env python
+# Anna Agents-Oriented Testing Setup Loader
+
+# Requires PyYAML: pip install pyyaml
+
+import os, sys, datetime
+from argparse import ArgumentParser
+
+# PyYAML
+from yaml import load, dump
+try:
+    from yaml import CLoader as Loader, CDumper as Dumper
+except ImportError:
+    from yaml import Loader, Dumper
+
+
+def _exit(message = None, rc=0):
+  if (message): printMsg(message)
+  printMsg("Exiting [rc={}]".format(rc))
+  sys.exit(rc)
+
+
+def timeFormat():
+  return '%b.%d.%Y-%H.%M.%S'
+
+
+def printMsg(message):
+  print ("[{}] {}".format(datetime.datetime.now().strftime(timeFormat()), message))
+
+
+class YamlConfigParser():
+  """
+  Yaml parser
+  """
+  def __init__(self, yaml_config_file):
+    """
+    Convert the yaml file into a Python object
+    """
+    self.data = None
+    try:
+      with open(yaml_config_file, 'r') as ss:
+        self.data = load(ss, Loader=Loader)
+        ss.close()
+
+    except Exception:
+     raise
+
+  def getData(self):
+    return self.data
+
+  def show(self, options):
+    output = dump(self.data, Dumper=Dumper)
+    print(output)
+
+
+def writeFileContent(filename, content):
+  _file = open(filename, "w")
+  _file.write(content)
+  _file.close()
+
+
+def getNodeValue(dictionary, key):
+
+  value = dictionary[key]
+  printMsg("  {}: {}".format(key, value))
+  return value
+
+
+def configure_agent_adml(node, description, application_id, dictionary, _type, origin_host, origin_realm, address):
+
+  # Agent ADML configuration is basically generation of services:
+
+  if (_type == "client"):
+    stacks='''  <stack id="{application_id}" dictionary="{dictionary}" fixMode="Always" ignoreFlagsOnValidation="yes"/>'''.format(application_id=application_id, dictionary=dictionary)
+
+    nodes='''  <node originHost="{origin_host}" applicationId="{application_id}" entity="{address}" cer="cer.{application_id}.xml" answersTimeout="300000" dumpLog="yes"/>'''.format(application_id=application_id, dictionary=dictionary, origin_host=origin_host, address=address)
+
+    ce_path = agents_ADML_dir + "/cer.{}.xml".format(application_id)
+    ce_content='''<message version="1" name="CER" application-id="0">
+   <avp name="Origin-Host" data="{origin_host}"/>
+   <avp name="Origin-Realm" data="{origin_realm}"/>
+   <avp name="Host-IP-Address" data="1|127.0.0.1"/>
+   <avp name="Vendor-Id" data="10415"/>
+   <avp name="Product-Name" data="{description}"/>
+   <avp name="Auth-Application-Id" data="{application_id}"/>
+</message>'''.format(application_id=application_id, origin_host=origin_host, origin_realm=origin_realm, description=description)
+
+  elif (_type == "server"):
+    stacks='''  <stack id="{application_id}" dictionary="{dictionary}" fixMode="Never"/>'''.format(application_id=application_id, dictionary=dictionary)
+
+    nodes='''  <node originHost="{origin_host}" applicationId="{application_id}" diameterServer="{address}" diameterServerSessions="10" cea="cea.{application_id}.xml" answersTimeout="300000" dumpLog="yes"/>'''.format(application_id=application_id, dictionary=dictionary, origin_host=origin_host, address=address)
+
+    ce_path = agents_ADML_dir + "/cea.{}.xml".format(application_id)
+    ce_content='''<message version="1" name="CEA" application-id="0">
+   <avp name="Result-Code" data="2001" alias="DIAMETER_SUCCESS"/>
+   <avp name="Origin-Host" data="{origin_host}"/>
+   <avp name="Origin-Realm" data="{origin_realm}"/>
+   <avp name="Host-IP-Address" data="1|127.0.0.1"/>
+   <avp name="Vendor-Id" data="10415"/>
+   <avp name="Product-Name" data="{description}"/>
+</message>'''.format(origin_host=origin_host, origin_realm=origin_realm, description=description)
+
+  else:
+    raise Exception("Invalid ADML type '{}' (allowed: client|server)".format(_type))
+
+  # Create cer/cea files:
+  writeFileContent(ce_path, ce_content)
+
+
+  return stacks, nodes
+
+
+def configure_agent_kafka(node, description, topic):
+
+  # Producer script:
+  content = '''#!/bin/bash
+# Positional arguments: json, delay, debug indicator
+[ -z "$3" ] && echo "Usage: $0 <json file> <delay in milliseconds: 0 is no delay> <debug indicator: yes|no>" && exit 1
+
+json=$1
+delay=""
+[ "$2" != "0" ] && delay="-w $2"
+debug=""
+[ "$3" = "yes" ] && debug="-d"
+
+echo "[{description}] Kafka Producer Step"
+cd $(dirname $0)
+python3 Producer.py -n {node} -t {topic} -f $json $delay $debug 2>&1 | tee -a {node}-producer.sh.output
+exit ${{PIPESTATUS[0]}}
+'''.format(description=description, node=node, topic=topic)
+
+  path = agents_KAFKA_dir + "/{}-producer.sh".format(node)
+  writeFileContent(path, content)
+  os.chmod(path, 0o755)
+  #printMsg("Created procedure '{}'".format(path))
+
+  # Consumer script:
+  content = '''#!/bin/bash
+# Positional arguments: json, offset, timeout, debug indicator
+[ -z "$4" ] && echo "Usage: $0 <json file ('any' to skip matching)> <auto offset reset> <timeout in seconds: 0 is no timeout> <debug indicator: yes|no>" && exit 1
+
+file=""
+[ "$1" != "any" ] && file="-f $1"
+offset=$2
+timeout=""
+[ "$3" != "0" ] && timeout="-w $3"
+debug=""
+[ "$4" = "yes" ] && debug="-d"
+
+echo "[{description}] Kafka Consumer Step"
+cd $(dirname $0)
+python3 Consumer.py -n {node} -t {topic} $file -o "$offset" $timeout $debug 2>&1 | tee -a {node}-consumer.sh.output
+exit ${{PIPESTATUS[0]}}
+'''.format(description=description, node=node, topic=topic)
+
+  # We don't use -m (match as regular expression) because some problems have been detected with big json files.
+
+  path = agents_KAFKA_dir + "/{}-consumer.sh".format(node)
+  writeFileContent(path, content)
+  os.chmod(path, 0o755)
+  #printMsg("Created procedure '{}'".format(path))
+
+  # Admin script:
+  content = '''#!/bin/bash
+# Positional arguments: operation, debug indicator
+[ -z "$2" ] && echo "Usage: $0 <operation: clean_topic (at the moment)> <debug indicator: yes|no>" && exit 1
+operation=""
+case $1 in
+  clean_topic)
+    operation="-r"
+    ;;
+esac
+debug=""
+[ "$2" = "yes" ] && debug="-d"
+
+echo "[{description}] Kafka Administration Step"
+cd $(dirname $0)
+python3 Admin.py -n {node} -t {topic} $operation $debug 2>&1 | tee -a {node}-admin.sh.output
+exit ${{PIPESTATUS[0]}}
+'''.format(description=description, node=node, topic=topic)
+
+  path = agents_KAFKA_dir + "/{}-admin.sh".format(node)
+  writeFileContent(path, content)
+  os.chmod(path, 0o755)
+  #printMsg("Created procedure '{}'".format(path))
+
+
+def configure_agent_kafkacpp(node, description, topic):
+
+  # Consumer script:
+  content = '''#!/bin/bash
+# Positional arguments: json, offset, timeout, debug indicator
+[ -z "$4" ] && echo "Usage: $0 <json file ('any' to skip matching)> <auto offset reset> <timeout in seconds: 0 is no timeout> <debug indicator: yes|no>" && exit 1
+
+file=""
+[ "$1" != "any" ] && file="-f $1"
+offset=$2
+timeout=""
+[ "$3" != "0" ] && timeout="-w $3"
+debug=""
+[ "$4" = "yes" ] && debug="-d"
+
+echo "[{description}] Kafkacpp Consumer Step"
+cd $(dirname $0)
+sh Consumer -n {node} -t {topic} $file -o "$offset" $timeout $debug 2>&1 | tee -a {node}-consumer.sh.output
+exit ${{PIPESTATUS[0]}}
+'''.format(description=description, node=node, topic=topic)
+
+  # We don't use -m (match as regular expression) because some problems have been detected with big json files.
+
+  path = agents_KAFKACPP_dir + "/{}-consumer.sh".format(node)
+  writeFileContent(path, content)
+  os.chmod(path, 0o755)
+  #printMsg("Created procedure '{}'".format(path))
+
+
+def configure_agent_httpmock(node, description, address):
+
+  # Provisioning script:
+  content = '''#!/bin/bash
+# Positional arguments: json, method, uri
+[ -z "$3" ] && echo "Usage: $0 <test id> <step id> <test path> <json file> <method: POST|PUT|GET|DELETE> <uri>" && exit 1
+
+echo "[{description}] Http Mock Provision Step"
+cd $(dirname $0)
+sh Provision.sh $1 $2 $3 "{address}" "$4" "$5" "$6" 2>&1 | tee -a {node}-provision.sh.output
+exit ${{PIPESTATUS[0]}}
+'''.format(description=description, node=node, address=address)
+
+  path = agents_HTTPMOCK_dir + "/{}-provision.sh".format(node)
+  writeFileContent(path, content)
+  os.chmod(path, 0o755)
+  #printMsg("Created procedure '{}'".format(path))
+
+
+def configure_agents(agents):
+
+  stacks = ""
+  nodes = ""
+
+  for node in agents:
+
+    if (node == "ADML"): raise Exception('Invalid node id: "ADML" is reserved')
+    if (node == "KAFKA"): raise Exception('Invalid node id: "KAFKA" is reserved')
+    if (node == "KAFKACPP"): raise Exception('Invalid node id: "KAFKACPP" is reserved')
+    if (node == "HTTPMOCK"): raise Exception('Invalid node id: "HTTPMOCK" is reserved')
+
+    printMsg("Node: {}".format(node))
+
+    description = getNodeValue(agents[node], "description")
+    template = getNodeValue(agents[node], "template")
+
+    if (template == "ADML"):
+      # Node file:
+      path = agents_ADML_dir + "/{}.node".format(node)
+      writeFileContent(path, description)
+
+      application_id = getNodeValue(agents[node], "application_id")
+      dictionary = getNodeValue(agents[node], "dictionary")
+      _type = getNodeValue(agents[node], "type")
+      origin_host = getNodeValue(agents[node], "origin_host")
+      origin_realm = getNodeValue(agents[node], "origin_realm")
+      address = getNodeValue(agents[node], "address")
+
+      # Here stacks and nodes are accumulated. Consolidation of 'services.xml' is done at the end of iteration (*)
+      s,n = configure_agent_adml(node, description, application_id, dictionary, _type, origin_host, origin_realm, address)
+      stacks += s
+      stacks += "\n"
+      nodes += n
+      nodes += "\n"
+
+    if (template == "KAFKA"):
+      # Node file:
+      path = agents_KAFKA_dir + "/{}.node".format(node)
+      writeFileContent(path, description)
+
+      topic = getNodeValue(agents[node], "topic")
+
+      configure_agent_kafka(node, description, topic)
+
+
+    if (template == "KAFKACPP"):
+      # Node file:
+      path = agents_KAFKACPP_dir + "/{}.node".format(node)
+      writeFileContent(path, description)
+
+      topic = getNodeValue(agents[node], "topic")
+
+      configure_agent_kafkacpp(node, description, topic)
+
+
+    if (template == "HTTPMOCK"):
+      # Node file:
+      path = agents_HTTPMOCK_dir + "/{}.node".format(node)
+      writeFileContent(path, description)
+
+      address = getNodeValue(agents[node], "address")
+
+      configure_agent_httpmock(node, description, address)
+
+
+  #(*) Consolidate services.xml
+  path = agents_ADML_dir + "/services.xml"
+
+  content='''<services>
+{}
+{}
+</services>'''.format(stacks, nodes)
+
+  writeFileContent(path, content)
+
+
+
+def parse_arguments():
+
+  parser = ArgumentParser(description='Anna Agents-Oriented Testing Setup Loader')
+  parser.add_argument('-f', '--file', help='Agents yaml configuration file', required=True)
+
+
+  arguments = parser.parse_args()
+
+  return arguments
+
+
+#####################
+#      M A I N      #
+#####################
+
+if __name__ == "__main__":
+
+  # Agents:
+  abspath = os.path.abspath(__file__)
+  dname = os.path.dirname(abspath)
+  agents_ADML_dir = dname + "/agents/ADML"
+  agents_KAFKA_dir = dname + "/agents/KAFKA"
+  agents_KAFKACPP_dir = dname + "/agents/KAFKACPP"
+  agents_HTTPMOCK_dir = dname + "/agents/HTTPMOCK"
+
+  arguments = parse_arguments()
+
+  afile = arguments.file
+
+  try:
+    agents = YamlConfigParser(arguments.file)
+
+    configure_agents(agents.getData())
+    #start_agents(agents)
+
+    _exit()
+
+  except Exception as e:
+    _exit(e, 1)
diff --git a/example/diameter/launcher/deployments/aots/tests.example/agents.yml b/example/diameter/launcher/deployments/aots/tests.example/agents.yml
new file mode 100644 (file)
index 0000000..0c65f2c
--- /dev/null
@@ -0,0 +1,14 @@
+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
diff --git a/example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch01-ipv4/1.Initial.yml b/example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch01-ipv4/1.Initial.yml
new file mode 100644 (file)
index 0000000..781ee00
--- /dev/null
@@ -0,0 +1,28 @@
+- action: modify_json_key_value
+  arguments:
+    json: expected.json.template
+    new_json: expected.json
+    kpath_value_list:
+    - kpath: SessionRx
+      value: the_rx_session
+
+- action: SMPC/admin
+  arguments:
+    operation: clean_topic
+
+- action: timeout_ms
+  arguments:
+    value: 3000
+
+- action: AF/send_xml_to_entity
+  arguments:
+    xml: AAR.xml
+
+- action: AF/wait_xml_from_entity
+  arguments:
+    xml: AAA.xml
+
+- action: SMPC/consume_json
+  arguments:
+    json: expected.json
+    auto_offset_reset: earliest
diff --git a/example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch01-ipv4/2.Initial2.yml b/example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch01-ipv4/2.Initial2.yml
new file mode 100644 (file)
index 0000000..90b53c7
--- /dev/null
@@ -0,0 +1,11 @@
+- action: timeout_ms
+  arguments:
+    value: 2000
+
+- action: AF/send_xml_to_entity
+  arguments:
+    xml: AAR2.xml
+
+- action: AF/wait_xml_from_entity
+  arguments:
+    xml: AAA.xml
diff --git a/example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch01-ipv4/AAA.xml b/example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch01-ipv4/AAA.xml
new file mode 100644 (file)
index 0000000..00198ea
--- /dev/null
@@ -0,0 +1,13 @@
+<message version="1" name="AA-Answer" application-id="16777236" hop-by-hop-id="10" end-to-end-id="20">
+ <avp name="Session-Id" data="mytest;afNodeHostname.nodeHostRealm.com;default;1"/>
+ <avp name="Auth-Application-Id" data="16777236"/>
+ <avp name="Origin-Host" data="afNodeHostname.nodeHostRealm.com"/>
+ <avp name="Origin-Realm" data="(none)"/>
+ <avp name="Result-Code" data="2001" alias="DIAMETER_SUCCESS"/>
+ <avp name="Supported-Features" flags="128">
+ <avp name="Vendor-Id" data="10415"/>
+ <avp name="Feature-List-ID" data="1"/>
+ <avp name="Feature-List" data="19"/>
+ </avp>
+ <avp name="Origin-State-Id" data="1510836560"/>
+</message>
diff --git a/example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch01-ipv4/AAR.xml b/example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch01-ipv4/AAR.xml
new file mode 100644 (file)
index 0000000..db6ddae
--- /dev/null
@@ -0,0 +1,68 @@
+<message version="1" name="AA-Request" application-id="16777236" hop-by-hop-id="10" end-to-end-id="20">
+   <avp name="Session-Id" data="mytest;afNodeHostname.nodeHostRealm.com;2;555231"/>
+   <avp name="Destination-Host" data="sapcOwnHostId.operatorRealm.com"/>
+   <avp name="Destination-Realm" data="operatorRealm.com"/>
+   <avp name="Origin-Host" data="afNodeHostname.nodeHostRealm.com"/>
+   <avp name="Origin-Realm" data="nodeHostRealm.com"/>
+   <avp name="Auth-Application-Id" data="16777236"/>
+   <avp name="Origin-State-Id" data="1"/>
+   <avp name="Framed-IP-Address" hex-data="c52a0a20"/>
+   <avp name="AF-Application-Identifier" hex-data="75726e25334175726e2d787878253341336770702d736572766963652e696d732e696373692e6d6d74656c"/>
+   <avp name="Media-Component-Description">
+      <avp name="Media-Component-Number" data="1"/>
+      <avp name="Media-Sub-Component">
+         <avp name="Flow-Number" data="1"/>
+         <avp name="Flow-Description" data="permit in 17 from 48.148.70.168 2234 to 10.95.130.50 2234"/>
+         <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2234 to 48.148.70.168 2234"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Flow-Usage" data="0" alias="NO_INFORMATION"/>
+      </avp>
+      <avp name="Media-Sub-Component">
+         <avp name="Flow-Number" data="2"/>
+         <avp name="Flow-Description" data="permit in 17 from 48.148.70.168 2235 to 10.95.130.50 2235"/>
+         <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2235 to 48.148.70.168 2235"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Flow-Usage" data="1" alias="RTCP"/>
+      </avp>
+      <avp name="AF-Application-Identifier" hex-data="75726e25334175726e2d787878253341336770702d736572766963652e696d732e696373692e6d6d74656c"/>
+      <avp name="Media-Type" data="0" alias="OTHER"/>
+      <avp name="Max-Requested-Bandwidth-UL" data="128000"/>
+      <avp name="Max-Requested-Bandwidth-DL" data="128000"/>
+      <avp name="Flow-Status" data="2" alias="ENABLED"/>
+      <avp name="Reservation-Priority" data="3" alias="PRIORITY-THREE"/>
+      <avp name="RR-Bandwidth" data="64000"/>
+      <avp name="RS-Bandwidth" data="64000"/>
+   </avp>
+   <avp name="Media-Component-Description">
+      <avp name="Media-Component-Number" data="2"/>
+      <avp name="Media-Sub-Component">
+         <avp name="Flow-Number" data="1"/>
+         <avp name="Flow-Description" data="permit in 17 from 48.148.70.168 2236 to 10.95.130.50 2236"/>
+         <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2236 to 48.148.70.168 2236"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Flow-Usage" data="0" alias="NO_INFORMATION"/>
+      </avp>
+      <avp name="Media-Sub-Component">
+         <avp name="Flow-Number" data="2"/>
+         <avp name="Flow-Description" data="permit in 17 from 48.148.70.168 2237 to 10.95.130.50 2237"/>
+         <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2237 to 48.148.70.168 2237"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Flow-Usage" data="1" alias="RTCP"/>
+      </avp>
+      <avp name="AF-Application-Identifier" hex-data="75726e25334175726e2d787878253341336770702d736572766963652e696d732e696373692e6d6d74656c"/>
+      <avp name="Media-Type" data="1" alias="VIDEO"/>
+      <avp name="Max-Requested-Bandwidth-UL" data="128000"/>
+      <avp name="Max-Requested-Bandwidth-DL" data="128000"/>
+      <avp name="Flow-Status" data="2" alias="ENABLED"/>
+      <avp name="Reservation-Priority" data="4" alias="PRIORITY-FOUR"/>
+      <avp name="RR-Bandwidth" data="64000"/>
+      <avp name="RS-Bandwidth" data="64000"/>
+   </avp>
+   <avp name="AF-Charging-Identifier" hex-data="41462d4368617267696e674964656e746966696572"/>
+   <avp name="SIP-Forking-Indication" data="0" alias="SINGLE_DIALOGUE"/>
+   <avp name="Supported-Features" flags="192">
+      <avp name="Vendor-Id" data="10415"/>
+      <avp name="Feature-List-ID" data="1"/>
+      <avp name="Feature-List" data="19"/>
+   </avp>
+</message>
diff --git a/example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch01-ipv4/AAR2.xml b/example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch01-ipv4/AAR2.xml
new file mode 100644 (file)
index 0000000..db6ddae
--- /dev/null
@@ -0,0 +1,68 @@
+<message version="1" name="AA-Request" application-id="16777236" hop-by-hop-id="10" end-to-end-id="20">
+   <avp name="Session-Id" data="mytest;afNodeHostname.nodeHostRealm.com;2;555231"/>
+   <avp name="Destination-Host" data="sapcOwnHostId.operatorRealm.com"/>
+   <avp name="Destination-Realm" data="operatorRealm.com"/>
+   <avp name="Origin-Host" data="afNodeHostname.nodeHostRealm.com"/>
+   <avp name="Origin-Realm" data="nodeHostRealm.com"/>
+   <avp name="Auth-Application-Id" data="16777236"/>
+   <avp name="Origin-State-Id" data="1"/>
+   <avp name="Framed-IP-Address" hex-data="c52a0a20"/>
+   <avp name="AF-Application-Identifier" hex-data="75726e25334175726e2d787878253341336770702d736572766963652e696d732e696373692e6d6d74656c"/>
+   <avp name="Media-Component-Description">
+      <avp name="Media-Component-Number" data="1"/>
+      <avp name="Media-Sub-Component">
+         <avp name="Flow-Number" data="1"/>
+         <avp name="Flow-Description" data="permit in 17 from 48.148.70.168 2234 to 10.95.130.50 2234"/>
+         <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2234 to 48.148.70.168 2234"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Flow-Usage" data="0" alias="NO_INFORMATION"/>
+      </avp>
+      <avp name="Media-Sub-Component">
+         <avp name="Flow-Number" data="2"/>
+         <avp name="Flow-Description" data="permit in 17 from 48.148.70.168 2235 to 10.95.130.50 2235"/>
+         <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2235 to 48.148.70.168 2235"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Flow-Usage" data="1" alias="RTCP"/>
+      </avp>
+      <avp name="AF-Application-Identifier" hex-data="75726e25334175726e2d787878253341336770702d736572766963652e696d732e696373692e6d6d74656c"/>
+      <avp name="Media-Type" data="0" alias="OTHER"/>
+      <avp name="Max-Requested-Bandwidth-UL" data="128000"/>
+      <avp name="Max-Requested-Bandwidth-DL" data="128000"/>
+      <avp name="Flow-Status" data="2" alias="ENABLED"/>
+      <avp name="Reservation-Priority" data="3" alias="PRIORITY-THREE"/>
+      <avp name="RR-Bandwidth" data="64000"/>
+      <avp name="RS-Bandwidth" data="64000"/>
+   </avp>
+   <avp name="Media-Component-Description">
+      <avp name="Media-Component-Number" data="2"/>
+      <avp name="Media-Sub-Component">
+         <avp name="Flow-Number" data="1"/>
+         <avp name="Flow-Description" data="permit in 17 from 48.148.70.168 2236 to 10.95.130.50 2236"/>
+         <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2236 to 48.148.70.168 2236"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Flow-Usage" data="0" alias="NO_INFORMATION"/>
+      </avp>
+      <avp name="Media-Sub-Component">
+         <avp name="Flow-Number" data="2"/>
+         <avp name="Flow-Description" data="permit in 17 from 48.148.70.168 2237 to 10.95.130.50 2237"/>
+         <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2237 to 48.148.70.168 2237"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Flow-Usage" data="1" alias="RTCP"/>
+      </avp>
+      <avp name="AF-Application-Identifier" hex-data="75726e25334175726e2d787878253341336770702d736572766963652e696d732e696373692e6d6d74656c"/>
+      <avp name="Media-Type" data="1" alias="VIDEO"/>
+      <avp name="Max-Requested-Bandwidth-UL" data="128000"/>
+      <avp name="Max-Requested-Bandwidth-DL" data="128000"/>
+      <avp name="Flow-Status" data="2" alias="ENABLED"/>
+      <avp name="Reservation-Priority" data="4" alias="PRIORITY-FOUR"/>
+      <avp name="RR-Bandwidth" data="64000"/>
+      <avp name="RS-Bandwidth" data="64000"/>
+   </avp>
+   <avp name="AF-Charging-Identifier" hex-data="41462d4368617267696e674964656e746966696572"/>
+   <avp name="SIP-Forking-Indication" data="0" alias="SINGLE_DIALOGUE"/>
+   <avp name="Supported-Features" flags="192">
+      <avp name="Vendor-Id" data="10415"/>
+      <avp name="Feature-List-ID" data="1"/>
+      <avp name="Feature-List" data="19"/>
+   </avp>
+</message>
diff --git a/example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch01-ipv4/expected.json.template b/example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch01-ipv4/expected.json.template
new file mode 100644 (file)
index 0000000..eaacfa8
--- /dev/null
@@ -0,0 +1,4 @@
+{
+  "SessionRx":"one_rx_session",
+  "SessionGx":"the_gx_session"
+}
diff --git a/example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch02-ipv6/1.Initial.yml b/example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch02-ipv6/1.Initial.yml
new file mode 100644 (file)
index 0000000..5c6395c
--- /dev/null
@@ -0,0 +1,11 @@
+- action: timeout_ms
+  arguments:
+    value: 1000
+
+- action: AF/send_xml_to_entity
+  arguments:
+    xml: AAR-ipv6.xml
+
+- action: AF/wait_xml_from_entity
+  arguments:
+    xml: AAA.xml
diff --git a/example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch02-ipv6/2.Initial2.yml b/example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch02-ipv6/2.Initial2.yml
new file mode 100644 (file)
index 0000000..614bbee
--- /dev/null
@@ -0,0 +1,11 @@
+- action: timeout_ms
+  arguments:
+    value: 2000
+
+- action: AF/send_xml_to_entity
+  arguments:
+    xml: AAR-ipv6.xml
+
+- action: AF/wait_xml_from_entity
+  arguments:
+    xml: AAA.xml
diff --git a/example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch02-ipv6/3.Initial3.yml b/example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch02-ipv6/3.Initial3.yml
new file mode 100644 (file)
index 0000000..7b94b48
--- /dev/null
@@ -0,0 +1,11 @@
+- action: timeout_ms
+  arguments:
+    value: 3000
+
+- action: AF/send_xml_to_entity
+  arguments:
+    xml: AAR-ipv6.xml
+
+- action: AF/wait_xml_from_entity
+  arguments:
+    xml: AAA.xml
diff --git a/example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch02-ipv6/AAA.xml b/example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch02-ipv6/AAA.xml
new file mode 100644 (file)
index 0000000..00198ea
--- /dev/null
@@ -0,0 +1,13 @@
+<message version="1" name="AA-Answer" application-id="16777236" hop-by-hop-id="10" end-to-end-id="20">
+ <avp name="Session-Id" data="mytest;afNodeHostname.nodeHostRealm.com;default;1"/>
+ <avp name="Auth-Application-Id" data="16777236"/>
+ <avp name="Origin-Host" data="afNodeHostname.nodeHostRealm.com"/>
+ <avp name="Origin-Realm" data="(none)"/>
+ <avp name="Result-Code" data="2001" alias="DIAMETER_SUCCESS"/>
+ <avp name="Supported-Features" flags="128">
+ <avp name="Vendor-Id" data="10415"/>
+ <avp name="Feature-List-ID" data="1"/>
+ <avp name="Feature-List" data="19"/>
+ </avp>
+ <avp name="Origin-State-Id" data="1510836560"/>
+</message>
diff --git a/example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch02-ipv6/AAR-ipv6.xml b/example/diameter/launcher/deployments/aots/tests.example/ts01-general/ch02-ipv6/AAR-ipv6.xml
new file mode 100644 (file)
index 0000000..db6ddae
--- /dev/null
@@ -0,0 +1,68 @@
+<message version="1" name="AA-Request" application-id="16777236" hop-by-hop-id="10" end-to-end-id="20">
+   <avp name="Session-Id" data="mytest;afNodeHostname.nodeHostRealm.com;2;555231"/>
+   <avp name="Destination-Host" data="sapcOwnHostId.operatorRealm.com"/>
+   <avp name="Destination-Realm" data="operatorRealm.com"/>
+   <avp name="Origin-Host" data="afNodeHostname.nodeHostRealm.com"/>
+   <avp name="Origin-Realm" data="nodeHostRealm.com"/>
+   <avp name="Auth-Application-Id" data="16777236"/>
+   <avp name="Origin-State-Id" data="1"/>
+   <avp name="Framed-IP-Address" hex-data="c52a0a20"/>
+   <avp name="AF-Application-Identifier" hex-data="75726e25334175726e2d787878253341336770702d736572766963652e696d732e696373692e6d6d74656c"/>
+   <avp name="Media-Component-Description">
+      <avp name="Media-Component-Number" data="1"/>
+      <avp name="Media-Sub-Component">
+         <avp name="Flow-Number" data="1"/>
+         <avp name="Flow-Description" data="permit in 17 from 48.148.70.168 2234 to 10.95.130.50 2234"/>
+         <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2234 to 48.148.70.168 2234"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Flow-Usage" data="0" alias="NO_INFORMATION"/>
+      </avp>
+      <avp name="Media-Sub-Component">
+         <avp name="Flow-Number" data="2"/>
+         <avp name="Flow-Description" data="permit in 17 from 48.148.70.168 2235 to 10.95.130.50 2235"/>
+         <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2235 to 48.148.70.168 2235"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Flow-Usage" data="1" alias="RTCP"/>
+      </avp>
+      <avp name="AF-Application-Identifier" hex-data="75726e25334175726e2d787878253341336770702d736572766963652e696d732e696373692e6d6d74656c"/>
+      <avp name="Media-Type" data="0" alias="OTHER"/>
+      <avp name="Max-Requested-Bandwidth-UL" data="128000"/>
+      <avp name="Max-Requested-Bandwidth-DL" data="128000"/>
+      <avp name="Flow-Status" data="2" alias="ENABLED"/>
+      <avp name="Reservation-Priority" data="3" alias="PRIORITY-THREE"/>
+      <avp name="RR-Bandwidth" data="64000"/>
+      <avp name="RS-Bandwidth" data="64000"/>
+   </avp>
+   <avp name="Media-Component-Description">
+      <avp name="Media-Component-Number" data="2"/>
+      <avp name="Media-Sub-Component">
+         <avp name="Flow-Number" data="1"/>
+         <avp name="Flow-Description" data="permit in 17 from 48.148.70.168 2236 to 10.95.130.50 2236"/>
+         <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2236 to 48.148.70.168 2236"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Flow-Usage" data="0" alias="NO_INFORMATION"/>
+      </avp>
+      <avp name="Media-Sub-Component">
+         <avp name="Flow-Number" data="2"/>
+         <avp name="Flow-Description" data="permit in 17 from 48.148.70.168 2237 to 10.95.130.50 2237"/>
+         <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2237 to 48.148.70.168 2237"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Flow-Usage" data="1" alias="RTCP"/>
+      </avp>
+      <avp name="AF-Application-Identifier" hex-data="75726e25334175726e2d787878253341336770702d736572766963652e696d732e696373692e6d6d74656c"/>
+      <avp name="Media-Type" data="1" alias="VIDEO"/>
+      <avp name="Max-Requested-Bandwidth-UL" data="128000"/>
+      <avp name="Max-Requested-Bandwidth-DL" data="128000"/>
+      <avp name="Flow-Status" data="2" alias="ENABLED"/>
+      <avp name="Reservation-Priority" data="4" alias="PRIORITY-FOUR"/>
+      <avp name="RR-Bandwidth" data="64000"/>
+      <avp name="RS-Bandwidth" data="64000"/>
+   </avp>
+   <avp name="AF-Charging-Identifier" hex-data="41462d4368617267696e674964656e746966696572"/>
+   <avp name="SIP-Forking-Indication" data="0" alias="SINGLE_DIALOGUE"/>
+   <avp name="Supported-Features" flags="192">
+      <avp name="Vendor-Id" data="10415"/>
+      <avp name="Feature-List-ID" data="1"/>
+      <avp name="Feature-List" data="19"/>
+   </avp>
+</message>
diff --git a/example/diameter/launcher/deployments/aots/tests.example/ts02-errors/ch01-protocolErrors/1.MissingRealm.yml b/example/diameter/launcher/deployments/aots/tests.example/ts02-errors/ch01-protocolErrors/1.MissingRealm.yml
new file mode 100644 (file)
index 0000000..78dd722
--- /dev/null
@@ -0,0 +1,11 @@
+- action: timeout_ms
+  arguments:
+    value: 1000
+
+- action: AF/send_xml_to_entity
+  arguments:
+    xml: AAR-bad.xml
+
+- action: AF/wait_xml_from_entity
+  arguments:
+    xml: AAA-bad.xml
diff --git a/example/diameter/launcher/deployments/aots/tests.example/ts02-errors/ch01-protocolErrors/AAA-bad.xml b/example/diameter/launcher/deployments/aots/tests.example/ts02-errors/ch01-protocolErrors/AAA-bad.xml
new file mode 100644 (file)
index 0000000..a43e1de
--- /dev/null
@@ -0,0 +1,13 @@
+<message version="1" name="AA-Answer" application-id="16777236" hop-by-hop-id="10" end-to-end-id="20">
+ <avp name="Session-Id" data="mytest;afNodeHostname.nodeHostRealm.com;default;1"/>
+ <avp name="Auth-Application-Id" data="16777236"/>
+ <avp name="Origin-Host" data="afNodeHostname.nodeHostRealm.com"/>
+ <avp name="Origin-Realm" data="(none)"/>
+ <avp name="Result-Code" data="5005" alias="DIAMETER_MISSING_AVP"/>
+ <avp name="Supported-Features" flags="128">
+ <avp name="Vendor-Id" data="10415"/>
+ <avp name="Feature-List-ID" data="1"/>
+ <avp name="Feature-List" data="19"/>
+ </avp>
+ <avp name="Origin-State-Id" data="1510836560"/>
+</message>
diff --git a/example/diameter/launcher/deployments/aots/tests.example/ts02-errors/ch01-protocolErrors/AAR-bad.xml b/example/diameter/launcher/deployments/aots/tests.example/ts02-errors/ch01-protocolErrors/AAR-bad.xml
new file mode 100644 (file)
index 0000000..40aec3f
--- /dev/null
@@ -0,0 +1,66 @@
+<message version="1" name="AA-Request" application-id="16777236" hop-by-hop-id="10" end-to-end-id="20">
+   <avp name="Session-Id" data="mytest;afNodeHostname.nodeHostRealm.com;2;555231"/>
+   <avp name="Destination-Host" data="sapcOwnHostId.operatorRealm.com"/>
+   <avp name="Origin-Host" data="afNodeHostname.nodeHostRealm.com"/>
+   <avp name="Auth-Application-Id" data="16777236"/>
+   <avp name="Origin-State-Id" data="1"/>
+   <avp name="Framed-IP-Address" hex-data="c52a0a20"/>
+   <avp name="AF-Application-Identifier" hex-data="75726e25334175726e2d787878253341336770702d736572766963652e696d732e696373692e6d6d74656c"/>
+   <avp name="Media-Component-Description">
+      <avp name="Media-Component-Number" data="1"/>
+      <avp name="Media-Sub-Component">
+         <avp name="Flow-Number" data="1"/>
+         <avp name="Flow-Description" data="permit in 17 from 48.148.70.168 2234 to 10.95.130.50 2234"/>
+         <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2234 to 48.148.70.168 2234"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Flow-Usage" data="0" alias="NO_INFORMATION"/>
+      </avp>
+      <avp name="Media-Sub-Component">
+         <avp name="Flow-Number" data="2"/>
+         <avp name="Flow-Description" data="permit in 17 from 48.148.70.168 2235 to 10.95.130.50 2235"/>
+         <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2235 to 48.148.70.168 2235"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Flow-Usage" data="1" alias="RTCP"/>
+      </avp>
+      <avp name="AF-Application-Identifier" hex-data="75726e25334175726e2d787878253341336770702d736572766963652e696d732e696373692e6d6d74656c"/>
+      <avp name="Media-Type" data="0" alias="OTHER"/>
+      <avp name="Max-Requested-Bandwidth-UL" data="128000"/>
+      <avp name="Max-Requested-Bandwidth-DL" data="128000"/>
+      <avp name="Flow-Status" data="2" alias="ENABLED"/>
+      <avp name="Reservation-Priority" data="3" alias="PRIORITY-THREE"/>
+      <avp name="RR-Bandwidth" data="64000"/>
+      <avp name="RS-Bandwidth" data="64000"/>
+   </avp>
+   <avp name="Media-Component-Description">
+      <avp name="Media-Component-Number" data="2"/>
+      <avp name="Media-Sub-Component">
+         <avp name="Flow-Number" data="1"/>
+         <avp name="Flow-Description" data="permit in 17 from 48.148.70.168 2236 to 10.95.130.50 2236"/>
+         <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2236 to 48.148.70.168 2236"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Flow-Usage" data="0" alias="NO_INFORMATION"/>
+      </avp>
+      <avp name="Media-Sub-Component">
+         <avp name="Flow-Number" data="2"/>
+         <avp name="Flow-Description" data="permit in 17 from 48.148.70.168 2237 to 10.95.130.50 2237"/>
+         <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2237 to 48.148.70.168 2237"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Flow-Usage" data="1" alias="RTCP"/>
+      </avp>
+      <avp name="AF-Application-Identifier" hex-data="75726e25334175726e2d787878253341336770702d736572766963652e696d732e696373692e6d6d74656c"/>
+      <avp name="Media-Type" data="1" alias="VIDEO"/>
+      <avp name="Max-Requested-Bandwidth-UL" data="128000"/>
+      <avp name="Max-Requested-Bandwidth-DL" data="128000"/>
+      <avp name="Flow-Status" data="2" alias="ENABLED"/>
+      <avp name="Reservation-Priority" data="4" alias="PRIORITY-FOUR"/>
+      <avp name="RR-Bandwidth" data="64000"/>
+      <avp name="RS-Bandwidth" data="64000"/>
+   </avp>
+   <avp name="AF-Charging-Identifier" hex-data="41462d4368617267696e674964656e746966696572"/>
+   <avp name="SIP-Forking-Indication" data="0" alias="SINGLE_DIALOGUE"/>
+   <avp name="Supported-Features" flags="192">
+      <avp name="Vendor-Id" data="10415"/>
+      <avp name="Feature-List-ID" data="1"/>
+      <avp name="Feature-List" data="19"/>
+   </avp>
+</message>
index 4f11ee2..ca439a6 100644 (file)
@@ -26,11 +26,15 @@ int main(int argc, const char** argv) {
   anna::Logger::setLevel(anna::Logger::Warning);
 
   // Current working directory and absolute trace path file:
   anna::Logger::setLevel(anna::Logger::Warning);
 
   // Current working directory and absolute trace path file:
-  char c_exe[ PATH_MAX ];
-  ssize_t count = readlink( "/proc/self/exe", c_exe, PATH_MAX );
-  std::string exe(c_exe, (count > 0) ? count : 0 );
-  std::string cwd = exe.substr(0, exe.find_last_of("/"));
-  std::string trace_file = cwd + "/launcher.trace";
+  char c_cwd[ PATH_MAX ];
+  std::string trace_file;
+  if (getcwd(c_cwd, sizeof(c_cwd)) != NULL) {
+    trace_file = c_cwd;
+    trace_file += "/launcher.trace";
+  } else {
+     perror("getcwd() error");
+     return 1;
+  }
 
   anna::Logger::initialize("launcher", new TraceWriter(trace_file.c_str(), 2048000));
   anna::time::functions::initialize(); // before application instantiation (it have a anna::time object)
 
   anna::Logger::initialize("launcher", new TraceWriter(trace_file.c_str(), 2048000));
   anna::time::functions::initialize(); // before application instantiation (it have a anna::time object)
index acd3a54..6ec64ad 100644 (file)
 #include <map>
 
 // Project
 #include <map>
 
 // Project
-#include <anna/diameter/defines.hpp>
 #include <anna/core/DataBlock.hpp>
 #include <anna/core/util/Millisecond.hpp>
 #include <anna/core/DataBlock.hpp>
 #include <anna/core/util/Millisecond.hpp>
-#include <anna/testing/TestCondition.hpp>
 
 
+// Diameter-specific
+#include <anna/diameter/defines.hpp>
+#include <anna/testing/TestDiameterCondition.hpp>
 
 
 namespace anna {
 
 
 namespace anna {
@@ -37,7 +38,7 @@ namespace anna {
 namespace testing {
 
   class TestStep;
 namespace testing {
 
   class TestStep;
-  class TestStepWait;
+  class TestStepWaitDiameter;
 class TestCase {
 
   void assertInitialized() const throw(anna::RuntimeException);
 class TestCase {
 
   void assertInitialized() const throw(anna::RuntimeException);
@@ -59,20 +60,26 @@ public:
     void clear() throw();
     int events() const throw() { return a_events.size(); }
     anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
     void clear() throw();
     int events() const throw() { return a_events.size(); }
     anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
+    std::string asString() const throw();
   };
 
   };
 
-  TestCase(unsigned int id);
+  TestCase(unsigned int id, const std::string &description = "");
   ~TestCase();
 
   struct State { enum _v { Initialized, InProgress, Failed, Success }; };
   static const char* asText(const State::_v state) throw();
   const State::_v &getState() const throw() { return a_state; }
   ~TestCase();
 
   struct State { enum _v { Initialized, InProgress, Failed, Success }; };
   static const char* asText(const State::_v state) throw();
   const State::_v &getState() const throw() { return a_state; }
-  const anna::Millisecond &getStartTimestamp() const throw() { return a_startTime; }
+  const anna::Millisecond &getStartTimestamp() const throw() { return a_startTimestamp; }
+  const anna::Millisecond &getFinishTimestamp() const throw() { return a_finishTimestamp; }
+  anna::Millisecond getLapseMs() const throw();
+  anna::Millisecond getProcessingTimeMs() const throw();
   void addDebugSummaryHint(const std::string &hint) throw() { a_debugSummary.addHint(hint); }
   void setState(const State::_v &state) throw();
   bool isFinished() const throw() { return (getState() == State::Failed || getState() == State::Success); }
   bool inProgress() const throw() { return (getState() == State::InProgress); }
   void addDebugSummaryHint(const std::string &hint) throw() { a_debugSummary.addHint(hint); }
   void setState(const State::_v &state) throw();
   bool isFinished() const throw() { return (getState() == State::Failed || getState() == State::Success); }
   bool inProgress() const throw() { return (getState() == State::InProgress); }
-  bool hasSameCondition(const TestCondition &condition) const throw();
+  bool isFailed() const throw() { return (getState() == State::Failed); }
+  bool hasSameCondition(const TestDiameterCondition &condition) const throw();
+  const DebugSummary & getDebugSummary() const throw() { return a_debugSummary; }
 
   // Interactivity:
   void makeInteractive(bool yes = true) throw() { a_interactiveAmount = (yes ? 0:-1); }
 
   // Interactivity:
   void makeInteractive(bool yes = true) throw() { a_interactiveAmount = (yes ? 0:-1); }
@@ -87,17 +94,20 @@ public:
 
   // Step type & information
   void addTimeout(const anna::Millisecond &timeout) throw(anna::RuntimeException);
 
   // Step type & information
   void addTimeout(const anna::Millisecond &timeout) throw(anna::RuntimeException);
-  void addSendxml2e(const anna::DataBlock &db, anna::diameter::comm::OriginHost *host, int stepNumber) throw(anna::RuntimeException);
-  void addSendxml2c(const anna::DataBlock &db, anna::diameter::comm::OriginHost *host, int stepNumber) throw(anna::RuntimeException);
   void addDelay(const anna::Millisecond &delay) throw(anna::RuntimeException);
   void addDelay(const anna::Millisecond &delay) throw(anna::RuntimeException);
-  void addWait(bool fromEntity,
+  void addWaitDiameter(bool fromEntity,
                 const std::string &code, const std::string &bitR, const std::string &hopByHop, const std::string &applicationId,
                 const std::string &sessionId, const std::string &resultCode,
                 const std::string &msisdn, const std::string &imsi, const std::string &serviceContextId) throw(anna::RuntimeException);
                 const std::string &code, const std::string &bitR, const std::string &hopByHop, const std::string &applicationId,
                 const std::string &sessionId, const std::string &resultCode,
                 const std::string &msisdn, const std::string &imsi, const std::string &serviceContextId) throw(anna::RuntimeException);
-  void addWaitAnswer(bool fromEntity, int stepNumber) throw(anna::RuntimeException);
-  void addWaitRegexpHex(bool fromEntity, const std::string &regexp) throw(anna::RuntimeException);
-  void addWaitRegexpXml(bool fromEntity, const std::string &regexp) throw(anna::RuntimeException);
   void addCommand(const std::string &cmd) throw(anna::RuntimeException);
   void addCommand(const std::string &cmd) throw(anna::RuntimeException);
+  void addIpLimit(unsigned int ipLimit) throw(anna::RuntimeException);
+
+  // Diameter-specifc
+  void addSendDiameterXml2e(const anna::DataBlock &db, anna::diameter::comm::OriginHost *host, int stepNumber) throw(anna::RuntimeException);
+  void addSendDiameterXml2c(const anna::DataBlock &db, anna::diameter::comm::OriginHost *host, int stepNumber) throw(anna::RuntimeException);
+  void addWaitDiameterAnswer(bool fromEntity, int stepNumber) throw(anna::RuntimeException);
+  void addWaitDiameterRegexpHex(bool fromEntity, const std::string &regexp) throw(anna::RuntimeException);
+  void addWaitDiameterRegexpXml(bool fromEntity, const std::string &regexp) throw(anna::RuntimeException);
 
 
   // Process:
 
 
   // Process:
@@ -109,14 +119,17 @@ public:
   bool reset(bool hard /* hard reset includes in-progress test cases */) throw();
 
   // getters
   bool reset(bool hard /* hard reset includes in-progress test cases */) throw();
 
   // getters
-  const anna::Millisecond &getStartTime() const throw() { return a_startTime; }
   const unsigned int &getId() const throw() { return a_id; }
   const unsigned int &getId() const throw() { return a_id; }
+  const std::string &getDescription() const throw() { return a_description; }
+
+  // setters
+  void setDescription(const std::string &description) throw() { a_description = description; }
 
   //helpers
   int steps() const throw() { return a_steps.size(); }
   void addStep(TestStep *step) throw() { a_steps.push_back(step); }
 
 
   //helpers
   int steps() const throw() { return a_steps.size(); }
   void addStep(TestStep *step) throw() { a_steps.push_back(step); }
 
-  TestStepWait *searchNextWaitConditionFulfilled(const anna::DataBlock &message, bool waitFromEntity) throw();
+  TestStepWaitDiameter *searchNextWaitConditionFulfilled(const anna::DataBlock &message, bool waitFromEntity) throw();
       // When a message arrives, we identify the test case by mean the Session-Id. Then, from the current step iterator (included),
       //  we search for a fulfilling condition for that message. The first found, is 'completed' and then breaks the search.
   const TestStep *getStep(int stepNumber) const throw();
       // When a message arrives, we identify the test case by mean the Session-Id. Then, from the current step iterator (included),
       //  we search for a fulfilling condition for that message. The first found, is 'completed' and then breaks the search.
   const TestStep *getStep(int stepNumber) const throw();
@@ -128,11 +141,13 @@ public:
 private:
   // private members:
   unsigned int a_id;
 private:
   // private members:
   unsigned int a_id;
+  std::string a_description;
   std::vector<TestStep*> a_steps;
   std::vector<TestStep*>::const_iterator a_stepsIt;
   std::map<anna::diameter::HopByHop, TestStep*> a_hopByHops; // for wait-answer
   State::_v a_state;
   std::vector<TestStep*> a_steps;
   std::vector<TestStep*>::const_iterator a_stepsIt;
   std::map<anna::diameter::HopByHop, TestStep*> a_hopByHops; // for wait-answer
   State::_v a_state;
-  anna::Millisecond a_startTime;
+  anna::Millisecond a_startTimestamp;
+  anna::Millisecond a_finishTimestamp;
   DebugSummary a_debugSummary; // used when a test case has failed, uncovered message conditions, and any other hint.
   int a_interactiveAmount;
 
   DebugSummary a_debugSummary; // used when a test case has failed, uncovered message conditions, and any other hint.
   int a_interactiveAmount;
 
diff --git a/include/anna/testing/TestCondition.hpp b/include/anna/testing/TestCondition.hpp
deleted file mode 100644 (file)
index ad29dc3..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-// 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 anna_testing_TestCondition_hpp
-#define anna_testing_TestCondition_hpp
-
-// Standard
-#include <string>
-
-// Project
-#include <anna/core/DataBlock.hpp>
-
-
-namespace anna {
-  namespace xml {
-    class Node;
-  }
-
-namespace testing {
-
-// TODO: fix types (code to int, etc.)
-class TestCondition {
-
-  public:
-
-    // RegexpXml = Regexp against XML representation for incoming messages
-    // RegexpHex = Regexp against HEX representation for incoming messages
-    // Fields = Compare specific message fields
-    struct Type { enum _v { RegexpXml, RegexpHex, Fields }; };
-    static const char* asText(const Type::_v type) throw();
-
-    TestCondition() : a_rcvFromEntity(true),
-
-                      a_regexp(""),
-
-                      a_code(""), a_bitR(""), a_hopByHop(""), a_applicationId(""),
-                      a_sessionId(""), a_resultCode(""),
-                      a_msisdn(""), a_imsi(""), a_serviceContextId("") { a_type = Type::Fields; }
-
-
-    // Source of the received message
-    void setReceivedFromEntity(bool rfe) throw() { a_rcvFromEntity = rfe; }
-    bool receivedFromEntity() const throw() { return a_rcvFromEntity; }
-
-    // Regexp
-    void setRegexpXml(const std::string &regexp) throw() { a_regexp = regexp; a_type = Type::RegexpXml; }
-    void setRegexpHex(const std::string &regexp) throw() { a_regexp = regexp; a_type = Type::RegexpHex; }
-
-    // Fields
-    void setCode(const std::string &value) throw() { a_code = value; a_type = Type::Fields; }
-    void setBitR(const std::string &value) throw() { a_bitR = value; a_type = Type::Fields; }
-    void setHopByHop(const std::string &value) throw() { a_hopByHop = value; a_type = Type::Fields; }
-    void setApplicationId(const std::string &value) throw() { a_applicationId = value; a_type = Type::Fields; }
-    void setSessionId(const std::string &value) throw() { a_sessionId = value; a_type = Type::Fields; }
-    void setResultCode(const std::string &value) throw() { a_resultCode = value; a_type = Type::Fields; }
-    void setMsisdn(const std::string &value) throw() { a_msisdn = value; a_type = Type::Fields; }
-    void setImsi(const std::string &value) throw() { a_imsi = value; a_type = Type::Fields; }
-    void setServiceContextId(const std::string &value) throw() { a_serviceContextId = value; a_type = Type::Fields; }
-
-    bool exists() const throw();
-    friend bool operator==(const TestCondition &c1, const TestCondition &c2) throw() {
-
-      if (c1.getType() != c2.getType()) return false;
-
-      if (c1.getType() == TestCondition::Type::RegexpXml || c1.getType() == TestCondition::Type::RegexpHex) {
-        if (c1.getRegexp() != c2.getRegexp()) return false;
-      }
-      else {
-        if (c1.getCode() != c2.getCode()) return false;
-        if (c1.getBitR() != c2.getBitR()) return false;
-        if (c1.getHopByHop() != c2.getHopByHop()) return false;
-        if (c1.getApplicationId() != c2.getApplicationId()) return false;
-        if (c1.getSessionId() != c2.getSessionId()) return false;
-        if (c1.getResultCode() != c2.getResultCode()) return false;
-        if (c1.getMsisdn() != c2.getMsisdn()) return false;
-        if (c1.getImsi() != c2.getImsi()) return false;
-        if (c1.getServiceContextId() != c2.getServiceContextId()) return false;
-      }
-
-      return true;
-    }
-
-
-    const Type::_v &getType() const throw() { return a_type; }
-
-    // Regexp:
-    const std::string & getRegexp() const throw() { return a_regexp; }
-    // Basic
-    const std::string & getCode() const throw() { return a_code; }
-    const std::string & getBitR() const throw() { return a_bitR; }
-    const std::string & getHopByHop() const throw() { return a_hopByHop; }
-    const std::string & getApplicationId() const throw() { return a_applicationId; }
-    const std::string & getSessionId() const throw() { return a_sessionId; }
-    const std::string & getResultCode() const throw() { return a_resultCode; }
-    const std::string & getMsisdn() const throw() { return a_msisdn; }
-    const std::string & getImsi() const throw() { return a_imsi; }
-    const std::string & getServiceContextId() const throw() { return a_serviceContextId; }
-
-
-    bool comply (const anna::DataBlock &message) const throw();
-
-    anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
-
-
-  private:
-
-    // Source of the received message
-    bool a_rcvFromEntity;
-
-    // Type
-    Type::_v a_type;
-
-    // Generic:
-    std::string a_regexp;
-
-    // Basic:
-    std::string a_code;
-    std::string a_bitR;
-    std::string a_hopByHop;
-    std::string a_applicationId;
-    std::string a_sessionId;
-    std::string a_resultCode;
-    std::string a_msisdn;
-    std::string a_imsi;
-    std::string a_serviceContextId;
-};
-
-}
-}
-
-#endif
-
diff --git a/include/anna/testing/TestDiameterCondition.hpp b/include/anna/testing/TestDiameterCondition.hpp
new file mode 100644 (file)
index 0000000..64ddc14
--- /dev/null
@@ -0,0 +1,137 @@
+// 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 anna_testing_TestDiameterCondition_hpp
+#define anna_testing_TestDiameterCondition_hpp
+
+// Standard
+#include <string>
+
+// Project
+#include <anna/core/DataBlock.hpp>
+
+
+namespace anna {
+  namespace xml {
+    class Node;
+  }
+
+namespace testing {
+
+// TODO: fix types (code to int, etc.)
+class TestDiameterCondition {
+
+  public:
+
+    // RegexpXml = Regexp against XML representation for incoming messages
+    // RegexpHex = Regexp against HEX representation for incoming messages
+    // Fields = Compare specific message fields
+    struct Type { enum _v { RegexpXml, RegexpHex, Fields }; };
+    static const char* asText(const Type::_v type) throw();
+
+    TestDiameterCondition() : a_rcvFromEntity(true),
+
+                      a_regexp(""),
+
+                      a_code(""), a_bitR(""), a_hopByHop(""), a_applicationId(""),
+                      a_sessionId(""), a_resultCode(""),
+                      a_msisdn(""), a_imsi(""), a_serviceContextId("") { a_type = Type::Fields; }
+
+
+    // Source of the received message
+    void setReceivedFromEntity(bool rfe) throw() { a_rcvFromEntity = rfe; }
+    bool receivedFromEntity() const throw() { return a_rcvFromEntity; }
+
+    // Regexp
+    void setRegexpXml(const std::string &regexp) throw() { a_regexp = regexp; a_type = Type::RegexpXml; }
+    void setRegexpHex(const std::string &regexp) throw() { a_regexp = regexp; a_type = Type::RegexpHex; }
+
+    // Fields
+    void setCode(const std::string &value) throw() { a_code = value; a_type = Type::Fields; }
+    void setBitR(const std::string &value) throw() { a_bitR = value; a_type = Type::Fields; }
+    void setHopByHop(const std::string &value) throw() { a_hopByHop = value; a_type = Type::Fields; }
+    void setApplicationId(const std::string &value) throw() { a_applicationId = value; a_type = Type::Fields; }
+    void setSessionId(const std::string &value) throw() { a_sessionId = value; a_type = Type::Fields; }
+    void setResultCode(const std::string &value) throw() { a_resultCode = value; a_type = Type::Fields; }
+    void setMsisdn(const std::string &value) throw() { a_msisdn = value; a_type = Type::Fields; }
+    void setImsi(const std::string &value) throw() { a_imsi = value; a_type = Type::Fields; }
+    void setServiceContextId(const std::string &value) throw() { a_serviceContextId = value; a_type = Type::Fields; }
+
+    bool exists() const throw();
+    friend bool operator==(const TestDiameterCondition &c1, const TestDiameterCondition &c2) throw() {
+
+      if (c1.getType() != c2.getType()) return false;
+
+      if (c1.getType() == TestDiameterCondition::Type::RegexpXml || c1.getType() == TestDiameterCondition::Type::RegexpHex) {
+        if (c1.getRegexp() != c2.getRegexp()) return false;
+      }
+      else {
+        if (c1.getCode() != c2.getCode()) return false;
+        if (c1.getBitR() != c2.getBitR()) return false;
+        if (c1.getHopByHop() != c2.getHopByHop()) return false;
+        if (c1.getApplicationId() != c2.getApplicationId()) return false;
+        if (c1.getSessionId() != c2.getSessionId()) return false;
+        if (c1.getResultCode() != c2.getResultCode()) return false;
+        if (c1.getMsisdn() != c2.getMsisdn()) return false;
+        if (c1.getImsi() != c2.getImsi()) return false;
+        if (c1.getServiceContextId() != c2.getServiceContextId()) return false;
+      }
+
+      return true;
+    }
+
+
+    const Type::_v &getType() const throw() { return a_type; }
+
+    // Regexp:
+    const std::string & getRegexp() const throw() { return a_regexp; }
+    // Basic
+    const std::string & getCode() const throw() { return a_code; }
+    const std::string & getBitR() const throw() { return a_bitR; }
+    const std::string & getHopByHop() const throw() { return a_hopByHop; }
+    const std::string & getApplicationId() const throw() { return a_applicationId; }
+    const std::string & getSessionId() const throw() { return a_sessionId; }
+    const std::string & getResultCode() const throw() { return a_resultCode; }
+    const std::string & getMsisdn() const throw() { return a_msisdn; }
+    const std::string & getImsi() const throw() { return a_imsi; }
+    const std::string & getServiceContextId() const throw() { return a_serviceContextId; }
+
+
+    bool comply (const anna::DataBlock &message) const throw();
+
+    anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
+
+
+  private:
+
+    // Source of the received message
+    bool a_rcvFromEntity;
+
+    // Type
+    Type::_v a_type;
+
+    // Generic:
+    std::string a_regexp;
+
+    // Basic:
+    std::string a_code;
+    std::string a_bitR;
+    std::string a_hopByHop;
+    std::string a_applicationId;
+    std::string a_sessionId;
+    std::string a_resultCode;
+    std::string a_msisdn;
+    std::string a_imsi;
+    std::string a_serviceContextId;
+};
+
+}
+}
+
+#endif
+
index 82fdf99..fec33e6 100644 (file)
@@ -63,7 +63,13 @@ class TestManager : public anna::timex::TimeEventObserver, public anna::Singleto
     StatSummary() { clear(); }
     void newTCState(const TestCase::State::_v beginState, const TestCase::State::_v endState) throw();
     void clear() throw();
     StatSummary() { clear(); }
     void newTCState(const TestCase::State::_v beginState, const TestCase::State::_v endState) throw();
     void clear() throw();
+    unsigned int getInitializedCount() const throw() { return a_initializedTcs; }
     unsigned int getInProgressCount() const throw() { return a_inprogressTcs; }
     unsigned int getInProgressCount() const throw() { return a_inprogressTcs; }
+    unsigned int getFailedCount() const throw() { return a_failedTcs; }
+    unsigned int getSuccessCount() const throw() { return a_sucessTcs; }
+    unsigned int getFinishedCount() const throw() { return a_sucessTcs + a_failedTcs; }
+    unsigned int getTotal() const throw() { return (a_initializedTcs + a_inprogressTcs + a_failedTcs + a_sucessTcs); }
+
     anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
   };
 
     anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
   };
 
@@ -77,6 +83,7 @@ class TestManager : public anna::timex::TimeEventObserver, public anna::Singleto
   std::string a_reportsDirectory;
   bool a_dumpInitializedReports, a_dumpInProgressReports, a_dumpFailedReports, a_dumpSuccessReports;
   bool a_dumpHexMessages;
   std::string a_reportsDirectory;
   bool a_dumpInitializedReports, a_dumpInProgressReports, a_dumpFailedReports, a_dumpSuccessReports;
   bool a_dumpHexMessages;
+  bool a_dumpStdout;
 
   // Pool of test cases
   test_pool_t a_testPool;
 
   // Pool of test cases
   test_pool_t a_testPool;
@@ -128,6 +135,8 @@ class TestManager : public anna::timex::TimeEventObserver, public anna::Singleto
     void setDumpHex(bool dh) throw() { a_dumpHexMessages = dh; }
     bool getDumpHex() const throw() { return a_dumpHexMessages; }
 
     void setDumpHex(bool dh) throw() { a_dumpHexMessages = dh; }
     bool getDumpHex() const throw() { return a_dumpHexMessages; }
 
+    void setDumpStdout(bool ds) throw() { a_dumpStdout = ds; }
+    bool getDumpStdout() const throw() { return a_dumpStdout; }
 
     void setDumpInitializedReports(bool enable) throw() { a_dumpInitializedReports = enable; }
     void setDumpInProgressReports(bool enable) throw() { a_dumpInProgressReports = enable; }
 
     void setDumpInitializedReports(bool enable) throw() { a_dumpInitializedReports = enable; }
     void setDumpInProgressReports(bool enable) throw() { a_dumpInProgressReports = enable; }
@@ -160,14 +169,16 @@ class TestManager : public anna::timex::TimeEventObserver, public anna::Singleto
 
 
     unsigned int getInProgressCount() const throw() { return a_statSummary.getInProgressCount(); }
 
 
     unsigned int getInProgressCount() const throw() { return a_statSummary.getInProgressCount(); }
+    unsigned int getInitializedCount() const throw() { return a_statSummary.getInitializedCount(); }
+    unsigned int getFinishedCount() const throw() { return a_statSummary.getFinishedCount(); }
     unsigned int getInProgressLimit() const throw() { return a_inProgressLimit; }
     unsigned int getInProgressLimit() const throw() { return a_inProgressLimit; }
-    void setInProgressLimit(unsigned int limit) throw() { a_inProgressLimit = limit; } // 0 = UINT_MAX (no limit)
+    void setInProgressLimit(unsigned int limit) throw() { a_inProgressLimit = limit; } // -1 = UINT_MAX (no limit)
 
     bool gotoTestCase(unsigned int id) throw();
     TestCase *findTestCase(unsigned int id) const throw(); // id = -1 provides current test case triggered
 
     bool gotoTestCase(unsigned int id) throw();
     TestCase *findTestCase(unsigned int id) const throw(); // id = -1 provides current test case triggered
-    TestCase *getTestCase(unsigned int id) throw(); // creates/reuses a test case
+    TestCase *getTestCase(unsigned int id, const std::string &description = "") throw(); // creates/reuses a test case
 
 
-    // Diameter
+    // Diameter-specific
     TestCase *getDiameterTestCaseFromSessionId(const anna::DataBlock &message, std::string &sessionId) throw();
     TestCase *getDiameterTestCaseFromSubscriberId(const anna::DataBlock &message, std::string &subscriberId) throw();
     void receiveDiameterMessage(const anna::DataBlock &message, const anna::diameter::comm::ClientSession *clientSession) throw(anna::RuntimeException);
     TestCase *getDiameterTestCaseFromSessionId(const anna::DataBlock &message, std::string &sessionId) throw();
     TestCase *getDiameterTestCaseFromSubscriberId(const anna::DataBlock &message, std::string &subscriberId) throw();
     void receiveDiameterMessage(const anna::DataBlock &message, const anna::diameter::comm::ClientSession *clientSession) throw(anna::RuntimeException);
@@ -180,7 +191,11 @@ class TestManager : public anna::timex::TimeEventObserver, public anna::Singleto
     bool execTestCases(int sync_amount) throw();
 
     anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
     bool execTestCases(int sync_amount) throw();
 
     anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
+    anna::xml::Node* junitAsXML(anna::xml::Node* parent) const throw();
     std::string asXMLString() const throw();
     std::string asXMLString() const throw();
+    std::string junitAsXMLString() const throw();
+    std::string summaryCounts() const throw();
+    std::string summaryStates() const throw();
 
     // stats
     void tcsStateStats(const TestCase::State::_v beginState, const TestCase::State::_v endState) throw() {
 
     // stats
     void tcsStateStats(const TestCase::State::_v beginState, const TestCase::State::_v endState) throw() {
index 782ce55..4442f66 100644 (file)
@@ -16,7 +16,7 @@
 
 // Project
 #include <anna/core/DataBlock.hpp>
 
 // Project
 #include <anna/core/DataBlock.hpp>
-#include <anna/testing/TestCondition.hpp>
+#include <anna/testing/TestDiameterCondition.hpp>
 #include <anna/core/util/Millisecond.hpp>
 
 
 #include <anna/core/util/Millisecond.hpp>
 
 
@@ -57,7 +57,7 @@ class TestStep {
     void initialize(TestCase *testCase);
 
   public:
     void initialize(TestCase *testCase);
 
   public:
-    struct Type { enum _v { Unconfigured, Timeout, Sendxml2e, Sendxml2c, Delay, Wait, Cmd }; };
+    struct Type { enum _v { Unconfigured, Timeout, Sendxml2e, Sendxml2c, Delay, Wait, Cmd, IpLimit }; };
     static const char* asText(const Type::_v type) throw();
 
     TestStep(TestCase *testCase) : a_message(true), a_messageCodec(NULL), a_executed(false) { initialize(testCase); }
     static const char* asText(const Type::_v type) throw();
 
     TestStep(TestCase *testCase) : a_message(true), a_messageCodec(NULL), a_executed(false) { initialize(testCase); }
@@ -67,6 +67,7 @@ class TestStep {
     const Type::_v &getType() const throw() { return a_type; }
     const int &getNumber() const throw() { return a_number; }
     bool isCompleted() const throw() { return a_completed; }
     const Type::_v &getType() const throw() { return a_type; }
     const int &getNumber() const throw() { return a_number; }
     bool isCompleted() const throw() { return a_completed; }
+    anna::Millisecond getLapseMs() const throw() { return a_endTimestamp - a_beginTimestamp; }
 
     bool execute() throw();
     void complete() throw();
 
     bool execute() throw();
     void complete() throw();
@@ -110,11 +111,12 @@ class TestStepTimeout : public TestStep {
     bool do_execute() throw();
     void do_complete() throw(); // timeout reached, test case failed
     void do_reset() throw();
     bool do_execute() throw();
     void do_complete() throw(); // timeout reached, test case failed
     void do_reset() throw();
+    void cancelTimer() throw();
     anna::xml::Node* asXML(anna::xml::Node* parent) throw();
 };
 
 
     anna::xml::Node* asXML(anna::xml::Node* parent) throw();
 };
 
 
-class TestStepSendxml : public TestStep {
+class TestStepSendDiameterXml : public TestStep {
 
   protected:
     // possible end points:
 
   protected:
     // possible end points:
@@ -127,11 +129,11 @@ class TestStepSendxml : public TestStep {
     bool a_expired; // a_endTimestamp will be the expiration reception timestamp
 
   public:
     bool a_expired; // a_endTimestamp will be the expiration reception timestamp
 
   public:
-    TestStepSendxml(TestCase *testCase) : TestStep(testCase),
+    TestStepSendDiameterXml(TestCase *testCase) : TestStep(testCase),
       a_expired(false),
       a_originHost(NULL),
       a_waitForRequestStepNumber(-1) {;}
       a_expired(false),
       a_originHost(NULL),
       a_waitForRequestStepNumber(-1) {;}
-    ~TestStepSendxml() {;}
+    ~TestStepSendDiameterXml() {;}
 
     // setter & getters
     void setOriginHost(anna::diameter::comm::OriginHost *host) throw() { a_originHost = host; }
 
     // setter & getters
     void setOriginHost(anna::diameter::comm::OriginHost *host) throw() { a_originHost = host; }
@@ -148,14 +150,14 @@ class TestStepSendxml : public TestStep {
     anna::xml::Node* asXML(anna::xml::Node* parent) throw();
 };
 
     anna::xml::Node* asXML(anna::xml::Node* parent) throw();
 };
 
-class TestStepSendxml2e : public TestStepSendxml {
+class TestStepSendDiameterXml2e : public TestStepSendDiameterXml {
   public:
   public:
-    TestStepSendxml2e(TestCase *testCase) : TestStepSendxml(testCase) { a_type = Type::Sendxml2e; }
+    TestStepSendDiameterXml2e(TestCase *testCase) : TestStepSendDiameterXml(testCase) { a_type = Type::Sendxml2e; }
 };
 
 };
 
-class TestStepSendxml2c : public TestStepSendxml {
+class TestStepSendDiameterXml2c : public TestStepSendDiameterXml {
   public:
   public:
-    TestStepSendxml2c(TestCase *testCase) : TestStepSendxml(testCase) { a_type = Type::Sendxml2c; }
+    TestStepSendDiameterXml2c(TestCase *testCase) : TestStepSendDiameterXml(testCase) { a_type = Type::Sendxml2c; }
 };
 
 
 };
 
 
@@ -174,23 +176,24 @@ class TestStepDelay : public TestStep {
     bool do_execute() throw();
     void do_complete() throw(); // delay reached
     void do_reset() throw();
     bool do_execute() throw();
     void do_complete() throw(); // delay reached
     void do_reset() throw();
+    void cancelTimer() throw();
     anna::xml::Node* asXML(anna::xml::Node* parent) throw();
 };
 
 
     anna::xml::Node* asXML(anna::xml::Node* parent) throw();
 };
 
 
-class TestStepWait : public TestStep {
+class TestStepWaitDiameter : public TestStep {
 
 
-    TestCondition a_condition;
+    TestDiameterCondition a_condition;
     anna::diameter::comm::ClientSession *a_clientSession;
     anna::diameter::comm::ServerSession *a_serverSession;
 
   public:
     anna::diameter::comm::ClientSession *a_clientSession;
     anna::diameter::comm::ServerSession *a_serverSession;
 
   public:
-    TestStepWait(TestCase *testCase) : TestStep(testCase) {
+    TestStepWaitDiameter(TestCase *testCase) : TestStep(testCase) {
       a_type = Type::Wait;
       a_clientSession = NULL;
       a_serverSession = NULL;
     }
       a_type = Type::Wait;
       a_clientSession = NULL;
       a_serverSession = NULL;
     }
-    ~TestStepWait() {;}
+    ~TestStepWaitDiameter() {;}
 
     // setter & getters
     void setCondition(bool fromEntity,
 
     // setter & getters
     void setCondition(bool fromEntity,
@@ -205,7 +208,7 @@ class TestStepWait : public TestStep {
     anna::diameter::comm::ClientSession *getClientSession() const throw() { return a_clientSession; }
     anna::diameter::comm::ServerSession *getServerSession() const throw() { return a_serverSession; }
 
     anna::diameter::comm::ClientSession *getClientSession() const throw() { return a_clientSession; }
     anna::diameter::comm::ServerSession *getServerSession() const throw() { return a_serverSession; }
 
-    const TestCondition &getCondition() const throw() { return a_condition; }
+    const TestDiameterCondition &getCondition() const throw() { return a_condition; }
     //void setMsgDataBlock(const anna::DataBlock &db) throw() { a_message = db; }
     bool fulfilled(const anna::DataBlock &db/*, bool matchSessionId = true*/) throw();
     const anna::DataBlock &getMsgDataBlock() const throw() { return a_message; }
     //void setMsgDataBlock(const anna::DataBlock &db) throw() { a_message = db; }
     bool fulfilled(const anna::DataBlock &db/*, bool matchSessionId = true*/) throw();
     const anna::DataBlock &getMsgDataBlock() const throw() { return a_message; }
@@ -256,6 +259,25 @@ class TestStepCmd : public TestStep {
     anna::xml::Node* asXML(anna::xml::Node* parent) throw();
 };
 
     anna::xml::Node* asXML(anna::xml::Node* parent) throw();
 };
 
+
+class TestStepIpLimit : public TestStep {
+
+    unsigned int a_ipLimit;
+
+  public:
+    TestStepIpLimit(TestCase *testCase) : TestStep(testCase), a_ipLimit(1) { a_type = Type::IpLimit; }
+
+    // setter & getters
+    void setIpLimit(unsigned int limit) throw() { a_ipLimit = limit; }
+    unsigned int getIpLimit() const throw() { return a_ipLimit; }
+
+    // virtuals
+    bool do_execute() throw();
+    void do_complete() throw();
+    void do_reset() throw() {;}
+    anna::xml::Node* asXML(anna::xml::Node* parent) throw();
+};
+
 }
 }
 
 }
 }
 
index 818093b..fd4ed42 100644 (file)
@@ -56,13 +56,27 @@ anna::xml::Node* TestCase::DebugSummary::asXML(anna::xml::Node* parent) const th
 
   return result;
 };
 
   return result;
 };
+
+std::string TestCase::DebugSummary::asString() const throw() {
+  std::string result = "";
+
+  std::vector<event_t>::const_iterator it;
+  for (it = a_events.begin(); it != a_events.end(); it++) {
+    result += anna::functions::asString("[Timestamp: %s] %s", (*it).Timestamp.asString().c_str(), (*it).Hint.c_str());
+  }
+
+  return result;
+};
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 
-TestCase::TestCase(unsigned int id) :
+TestCase::TestCase(unsigned int id, const std::string &description) :
     a_id(id),
     a_id(id),
+    a_description((description != "") ? description : (anna::functions::asString("Testcase_%d", id))),
     a_state(State::Initialized),
     a_state(State::Initialized),
-    a_startTime(0),
+    a_startTimestamp(0),
+    a_finishTimestamp(0),
     a_interactiveAmount(-1) {
 
   /*a_stepsIt = a_steps.end()*/;
     a_interactiveAmount(-1) {
 
   /*a_stepsIt = a_steps.end()*/;
@@ -82,13 +96,24 @@ throw() {
   return text [state];
 }
 
   return text [state];
 }
 
+anna::Millisecond TestCase::getLapseMs() const throw() {
+  return ((a_finishTimestamp >= a_startTimestamp) ? (a_finishTimestamp - a_startTimestamp) : (anna::Millisecond)0);
+}
+
 anna::xml::Node* TestCase::asXML(anna::xml::Node* parent) const
 throw() {
   anna::xml::Node* result = parent->createChild("TestCase");
 
   result->createAttribute("Id", a_id);
 anna::xml::Node* TestCase::asXML(anna::xml::Node* parent) const
 throw() {
   anna::xml::Node* result = parent->createChild("TestCase");
 
   result->createAttribute("Id", a_id);
+  result->createAttribute("Description", a_description);
   result->createAttribute("State", asText(a_state));
   result->createAttribute("State", asText(a_state));
-  result->createAttribute("StartTimestamp", a_startTime.asString());
+  result->createAttribute("StartTimestamp", a_startTimestamp.asString());
+
+  if (a_finishTimestamp != 0) {
+    result->createAttribute("FinishTimestamp", a_finishTimestamp.asString());
+    result->createAttribute("LapseMs", getLapseMs());
+  }
+
   int steps = a_steps.size();
   if (steps != 0) {
     result->createAttribute("NumberOfTestSteps", steps);
   int steps = a_steps.size();
   if (steps != 0) {
     result->createAttribute("NumberOfTestSteps", steps);
@@ -112,12 +137,12 @@ std::string TestCase::asXMLString() const throw() {
   return anna::xml::Compiler().apply(asXML(&root));
 }
 
   return anna::xml::Compiler().apply(asXML(&root));
 }
 
-bool TestCase::hasSameCondition(const TestCondition &condition) const throw() {
+bool TestCase::hasSameCondition(const TestDiameterCondition &condition) const throw() {
   std::vector<TestStep*>::const_iterator it;
   std::vector<TestStep*>::const_iterator it;
-  TestStepWait *step;
+  TestStepWaitDiameter *step;
   for (it = a_steps.begin(); it != a_steps.end(); it++) {
     if ((*it)->getType() != TestStep::Type::Wait) continue;
   for (it = a_steps.begin(); it != a_steps.end(); it++) {
     if ((*it)->getType() != TestStep::Type::Wait) continue;
-    step = (TestStepWait *)(*it);
+    step = (TestStepWaitDiameter *)(*it);
     if (step->getCondition() == condition) return true;
   }
   return false;
     if (step->getCondition() == condition) return true;
   }
   return false;
@@ -136,6 +161,29 @@ void TestCase::setState(const State::_v &state) throw() {
 
 
   if (isFinished()) {
 
 
   if (isFinished()) {
+    const char *literal = "FINISHED Test Case %llu/%llu [%s] => %s";
+    TestManager& testManager (TestManager::instantiate ());
+    LOGDEBUG(anna::Logger::debug(anna::functions::asString(literal, getId(), testManager.tests(), getDescription().c_str(), asText(a_state)), ANNA_FILE_LOCATION));
+
+    if (testManager.getDumpStdout()) {
+      std::cout << std::endl << anna::functions::asString(literal, getId(), testManager.tests(), getDescription().c_str(), asText(a_state)) << std::endl;
+    }
+
+    a_finishTimestamp = anna::functions::millisecond();
+
+    // Cancel existing timers:
+    std::vector<TestStep*>::iterator it;
+    for (it = a_steps.begin(); it != a_steps.end(); it++) {
+      if ((*it)->getType() == TestStep::Type::Timeout) {
+        TestStepTimeout *step = (TestStepTimeout *)(*it);
+        step->cancelTimer();
+      }
+      else if ((*it)->getType() == TestStep::Type::Delay) {
+        TestStepDelay *step = (TestStepDelay *)(*it);
+        step->cancelTimer();
+      }
+    }
+
     if ((getState() == State::Failed) && (!testManager.getDumpFailedReports())) return;
     if ((getState() == State::Success) && (!testManager.getDumpSuccessReports())) return;
     // report file name: cycle-<cycle id>.testcase-<test case id>.xml
     if ((getState() == State::Failed) && (!testManager.getDumpFailedReports())) return;
     if ((getState() == State::Success) && (!testManager.getDumpSuccessReports())) return;
     // report file name: cycle-<cycle id>.testcase-<test case id>.xml
@@ -176,20 +224,28 @@ bool TestCase::done() throw() {
 
 bool TestCase::process() throw() {
   if (steps() == 0) {
 
 bool TestCase::process() throw() {
   if (steps() == 0) {
-    LOGWARNING(anna::Logger::warning(anna::functions::asString("Test case %llu is empty, nothing to execute", a_id), ANNA_FILE_LOCATION));
+    LOGWARNING(anna::Logger::warning(anna::functions::asString("Test case %llu (%s) is empty, nothing to execute", a_id, a_description.c_str()), ANNA_FILE_LOCATION));
     return false;
   }
   if (isFinished()) {
     return false;
   }
   if (isFinished()) {
-    LOGDEBUG(anna::Logger::debug(anna::functions::asString("Test case %llu is finished, nothing done until soft-reset", a_id), ANNA_FILE_LOCATION));
+    LOGDEBUG(anna::Logger::debug(anna::functions::asString("Test case %llu (%s) is finished, nothing done until soft-reset", a_id, a_description.c_str()), ANNA_FILE_LOCATION));
     return false;
   }
 
     return false;
   }
 
+  const char *literal = "PROCESS Test Case %llu/%llu [%s]";
+  TestManager& testManager (TestManager::instantiate ());
+  LOGDEBUG(anna::Logger::debug(anna::functions::asString(literal, getId(), testManager.tests(), getDescription().c_str()), ANNA_FILE_LOCATION));
+
   if (a_state == State::Initialized) {
   if (a_state == State::Initialized) {
+    if (testManager.getDumpStdout()) {
+      std::cout << std::endl << std::endl << anna::functions::asString(literal, getId(), testManager.tests(), getDescription().c_str()) << std::endl;
+    }
+
     a_stepsIt = a_steps.begin();
     setState(State::InProgress);
 
     // For 'wait' steps (not really useful, but better than nothing: begin timestamp on test case start timestamp...):
     a_stepsIt = a_steps.begin();
     setState(State::InProgress);
 
     // For 'wait' steps (not really useful, but better than nothing: begin timestamp on test case start timestamp...):
-    a_startTime = anna::functions::millisecond();
+    a_startTimestamp = anna::functions::millisecond();
   }
 
   // Check end of the test case:
   }
 
   // Check end of the test case:
@@ -224,7 +280,8 @@ bool TestCase::reset(bool hard) throw() {
     (*it)->reset();
 
   a_debugSummary.clear();
     (*it)->reset();
 
   a_debugSummary.clear();
-  a_startTime = 0;
+  a_startTimestamp = 0;
+  a_finishTimestamp = 0;
   a_interactiveAmount = -1;
 
   setState(State::Initialized);
   a_interactiveAmount = -1;
 
   setState(State::Initialized);
@@ -234,7 +291,7 @@ bool TestCase::reset(bool hard) throw() {
 
 void TestCase::assertInitialized() const throw(anna::RuntimeException) {
   if (isFinished())
 
 void TestCase::assertInitialized() const throw(anna::RuntimeException) {
   if (isFinished())
-    throw anna::RuntimeException(anna::functions::asString("Cannot program anymore. The test case %llu has finished. You must reset it to append new steps (or do it during execution, which is also allowed).", a_id), ANNA_FILE_LOCATION);
+    throw anna::RuntimeException(anna::functions::asString("Cannot program anymore. The test case %llu (%s) has finished. You must reset it to append new steps (or do it during execution, which is also allowed).", a_id, a_description.c_str()), ANNA_FILE_LOCATION);
 }
 
 void TestCase::assertMessage(const anna::DataBlock &db, bool toEntity) throw(anna::RuntimeException) {
 }
 
 void TestCase::assertMessage(const anna::DataBlock &db, bool toEntity) throw(anna::RuntimeException) {
@@ -250,7 +307,7 @@ void TestCase::assertMessage(const anna::DataBlock &db, bool toEntity) throw(ann
   if (isRequest) {
     anna::diameter::HopByHop hbh = anna::diameter::codec::functions::getHopByHop(db);
     if (a_hopByHops.find(hbh) != a_hopByHops.end())
   if (isRequest) {
     anna::diameter::HopByHop hbh = anna::diameter::codec::functions::getHopByHop(db);
     if (a_hopByHops.find(hbh) != a_hopByHops.end())
-      throw anna::RuntimeException(anna::functions::asString("Another request has been programmed with the same hop-by-hop (%llu) in this test case (%llu)", hbh, a_id), ANNA_FILE_LOCATION);
+      throw anna::RuntimeException(anna::functions::asString("Another request has been programmed with the same hop-by-hop (%llu) in this test case (%llu, '%s')", hbh, a_id, a_description.c_str()), ANNA_FILE_LOCATION);
     a_hopByHops[hbh] = NULL; // may be assigned to a wait condition
   }
 
     a_hopByHops[hbh] = NULL; // may be assigned to a wait condition
   }
 
@@ -275,50 +332,50 @@ void TestCase::addTimeout(const anna::Millisecond &timeout) throw(anna::RuntimeE
   addStep(step);
 }
 
   addStep(step);
 }
 
-void TestCase::addSendxml2e(const anna::DataBlock &db, anna::diameter::comm::OriginHost *host, int stepNumber) throw(anna::RuntimeException) {
+void TestCase::addSendDiameterXml2e(const anna::DataBlock &db, anna::diameter::comm::OriginHost *host, int stepNumber) throw(anna::RuntimeException) {
   assertInitialized();
   assertMessage(db, true /* to entity */);
 
   if (stepNumber != -1) {
     const TestStep *stepReferred = getStep(stepNumber);
     if (!stepReferred)
   assertInitialized();
   assertMessage(db, true /* to entity */);
 
   if (stepNumber != -1) {
     const TestStep *stepReferred = getStep(stepNumber);
     if (!stepReferred)
-      throw anna::RuntimeException(anna::functions::asString("Step number (%d) do not exists (test case %llu)", stepNumber, a_id), ANNA_FILE_LOCATION);
+      throw anna::RuntimeException(anna::functions::asString("Step number (%d) do not exists (test case %llu, '%s')", stepNumber, a_id, a_description.c_str()), ANNA_FILE_LOCATION);
 
     if (stepReferred->getType() != TestStep::Type::Wait)
 
     if (stepReferred->getType() != TestStep::Type::Wait)
-      throw anna::RuntimeException(anna::functions::asString("Step number (%d) must refer to a 'wait' step (test case %llu)", stepNumber, a_id), ANNA_FILE_LOCATION);
+      throw anna::RuntimeException(anna::functions::asString("Step number (%d) must refer to a 'wait' step (test case %llu, '%s')", stepNumber, a_id, a_description.c_str()), ANNA_FILE_LOCATION);
 
 
-    const TestCondition &tc = (static_cast<const TestStepWait*>(stepReferred))->getCondition();
+    const TestDiameterCondition &tc = (static_cast<const TestStepWaitDiameter*>(stepReferred))->getCondition();
     if (tc.getCode() == "0") { // if regexp used, is not possible to detect this kind of errors
     if (tc.getCode() == "0") { // if regexp used, is not possible to detect this kind of errors
-      throw anna::RuntimeException(anna::functions::asString("Step number (%d) must refer to a 'wait for request' step (test case %llu)", stepNumber, a_id), ANNA_FILE_LOCATION);
+      throw anna::RuntimeException(anna::functions::asString("Step number (%d) must refer to a 'wait for request' step (test case %llu, '%s')", stepNumber, a_id, a_description.c_str()), ANNA_FILE_LOCATION);
     }
   }
 
     }
   }
 
-  TestStepSendxml2e *step = new TestStepSendxml2e(this);
+  TestStepSendDiameterXml2e *step = new TestStepSendDiameterXml2e(this);
   step->setMsgDataBlock(db);
   step->setOriginHost(host);
   step->setWaitForRequestStepNumber(stepNumber); // -1 means, no reference
   addStep(step);
 }
 
   step->setMsgDataBlock(db);
   step->setOriginHost(host);
   step->setWaitForRequestStepNumber(stepNumber); // -1 means, no reference
   addStep(step);
 }
 
-void TestCase::addSendxml2c(const anna::DataBlock &db, anna::diameter::comm::OriginHost *host, int stepNumber) throw(anna::RuntimeException) {
+void TestCase::addSendDiameterXml2c(const anna::DataBlock &db, anna::diameter::comm::OriginHost *host, int stepNumber) throw(anna::RuntimeException) {
   assertInitialized();
   assertMessage(db, false /* to client */);
 
   if (stepNumber != -1) {
     const TestStep *stepReferred = getStep(stepNumber);
     if (!stepReferred)
   assertInitialized();
   assertMessage(db, false /* to client */);
 
   if (stepNumber != -1) {
     const TestStep *stepReferred = getStep(stepNumber);
     if (!stepReferred)
-      throw anna::RuntimeException(anna::functions::asString("Step number (%d) do not exists (test case %llu)", stepNumber, a_id), ANNA_FILE_LOCATION);
+      throw anna::RuntimeException(anna::functions::asString("Step number (%d) do not exists (test case %llu, '%s')", stepNumber, a_id, a_description.c_str()), ANNA_FILE_LOCATION);
 
     if (stepReferred->getType() != TestStep::Type::Wait)
 
     if (stepReferred->getType() != TestStep::Type::Wait)
-      throw anna::RuntimeException(anna::functions::asString("Step number (%d) must refer to a 'wait' step (test case %llu)", stepNumber, a_id), ANNA_FILE_LOCATION);
+      throw anna::RuntimeException(anna::functions::asString("Step number (%d) must refer to a 'wait' step (test case %llu, '%s')", stepNumber, a_id, a_description.c_str()), ANNA_FILE_LOCATION);
 
 
-    const TestCondition &tc = (static_cast<const TestStepWait*>(stepReferred))->getCondition();
+    const TestDiameterCondition &tc = (static_cast<const TestStepWaitDiameter*>(stepReferred))->getCondition();
     if (tc.getCode() == "0") { // if regexp used, is not possible to detect this kind of errors
     if (tc.getCode() == "0") { // if regexp used, is not possible to detect this kind of errors
-      throw anna::RuntimeException(anna::functions::asString("Step number (%d) must refer to a 'wait for request' step (test case %llu)", stepNumber, a_id), ANNA_FILE_LOCATION);
+      throw anna::RuntimeException(anna::functions::asString("Step number (%d) must refer to a 'wait for request' step (test case %llu, '%s')", stepNumber, a_id, a_description.c_str()), ANNA_FILE_LOCATION);
     }
   }
 
     }
   }
 
-  TestStepSendxml2c *step = new TestStepSendxml2c(this);
+  TestStepSendDiameterXml2c *step = new TestStepSendDiameterXml2c(this);
   step->setMsgDataBlock(db);
   step->setOriginHost(host);
   step->setWaitForRequestStepNumber(stepNumber); // -1 means, no reference
   step->setMsgDataBlock(db);
   step->setOriginHost(host);
   step->setWaitForRequestStepNumber(stepNumber); // -1 means, no reference
@@ -332,84 +389,84 @@ void TestCase::addDelay(const anna::Millisecond &delay) throw(anna::RuntimeExcep
   addStep(step);
 }
 
   addStep(step);
 }
 
-void TestCase::addWait(bool fromEntity,
+void TestCase::addWaitDiameter(bool fromEntity,
               const std::string &code, const std::string &bitR, const std::string &hopByHop, const std::string &applicationId,
               const std::string &sessionId, const std::string &resultCode,
               const std::string &msisdn, const std::string &imsi, const std::string &serviceContextId) throw(anna::RuntimeException) {
   assertInitialized();
   std::string usedHopByHop = hopByHop;
               const std::string &code, const std::string &bitR, const std::string &hopByHop, const std::string &applicationId,
               const std::string &sessionId, const std::string &resultCode,
               const std::string &msisdn, const std::string &imsi, const std::string &serviceContextId) throw(anna::RuntimeException) {
   assertInitialized();
   std::string usedHopByHop = hopByHop;
-  TestStepWait *step = NULL;
+  TestStepWaitDiameter *step = NULL;
 
   // Check basic conditions:
   if (bitR == "1") {
     if (resultCode != "")
 
   // Check basic conditions:
   if (bitR == "1") {
     if (resultCode != "")
-      throw anna::RuntimeException(anna::functions::asString("You cannot specify Result-Code (%s) for a wait condition of a diameter request message (test case %llu)", resultCode.c_str(), a_id), ANNA_FILE_LOCATION);
+      throw anna::RuntimeException(anna::functions::asString("You cannot specify Result-Code (%s) for a wait condition of a diameter request message (test case %llu, '%s')", resultCode.c_str(), a_id, a_description.c_str()), ANNA_FILE_LOCATION);
     if (hopByHop != "")
     if (hopByHop != "")
-      throw anna::RuntimeException(anna::functions::asString("You cannot specify Hop-by-hop (%s) for a wait condition of a diameter request message (test case %llu)", hopByHop.c_str(), a_id), ANNA_FILE_LOCATION);
+      throw anna::RuntimeException(anna::functions::asString("You cannot specify Hop-by-hop (%s) for a wait condition of a diameter request message (test case %llu, '%s')", hopByHop.c_str(), a_id, a_description.c_str()), ANNA_FILE_LOCATION);
   }
   else {
     if (hopByHop != "") {
       if (hopByHop[0] == '#') {
         if (steps() == 0)
   }
   else {
     if (hopByHop != "") {
       if (hopByHop[0] == '#') {
         if (steps() == 0)
-          throw anna::RuntimeException(anna::functions::asString("No steps has been programmed, step reference is nonsense (test case %llu)", a_id), ANNA_FILE_LOCATION);
+          throw anna::RuntimeException(anna::functions::asString("No steps has been programmed, step reference is nonsense (test case %llu, '%s')", a_id, a_description.c_str()), ANNA_FILE_LOCATION);
 
         int stepNumber = atoi(hopByHop.substr(1).c_str());
 
         const TestStep *stepReferred = getStep(stepNumber);
         if (!stepReferred)
 
         int stepNumber = atoi(hopByHop.substr(1).c_str());
 
         const TestStep *stepReferred = getStep(stepNumber);
         if (!stepReferred)
-          throw anna::RuntimeException(anna::functions::asString("Step reference number (%d) do not exists (test case %llu)", stepNumber, a_id), ANNA_FILE_LOCATION);
+          throw anna::RuntimeException(anna::functions::asString("Step reference number (%d) do not exists (test case %llu, '%s')", stepNumber, a_id, a_description.c_str()), ANNA_FILE_LOCATION);
 
         if (stepReferred->getType() != TestStep::Type::Sendxml2e && stepReferred->getType() != TestStep::Type::Sendxml2c)
 
         if (stepReferred->getType() != TestStep::Type::Sendxml2e && stepReferred->getType() != TestStep::Type::Sendxml2c)
-          throw anna::RuntimeException(anna::functions::asString("Step number must refer to a 'sendxml2e' or 'sendxml2c' step (test case %llu)", a_id), ANNA_FILE_LOCATION);
+          throw anna::RuntimeException(anna::functions::asString("Step number must refer to a 'sendxml2e' or 'sendxml2c' step (test case %llu, '%s')", a_id, a_description.c_str()), ANNA_FILE_LOCATION);
 
 
-        const anna::DataBlock &db = (static_cast<const TestStepSendxml*>(stepReferred))->getMsgDataBlock();
+        const anna::DataBlock &db = (static_cast<const TestStepSendDiameterXml*>(stepReferred))->getMsgDataBlock();
         bool isAnswer = anna::diameter::codec::functions::isAnswer(db);
         if (isAnswer)
         bool isAnswer = anna::diameter::codec::functions::isAnswer(db);
         if (isAnswer)
-          throw anna::RuntimeException(anna::functions::asString("Step number must refer to a request message (test case %llu)", a_id), ANNA_FILE_LOCATION);
+          throw anna::RuntimeException(anna::functions::asString("Step number must refer to a request message (test case %llu, '%s')", a_id, a_description.c_str()), ANNA_FILE_LOCATION);
 
         // Hop-by-hop:
         anna::diameter::HopByHop hbh = anna::diameter::codec::functions::getHopByHop(db);
         usedHopByHop = anna::functions::asString(hbh);
 
         // Hop-by-hop:
         anna::diameter::HopByHop hbh = anna::diameter::codec::functions::getHopByHop(db);
         usedHopByHop = anna::functions::asString(hbh);
-        step = new TestStepWait(this);
+        step = new TestStepWaitDiameter(this);
         a_hopByHops[hbh /* always exists: is the info we calculated above */] = step;
       }
     }
   }
 
         a_hopByHops[hbh /* always exists: is the info we calculated above */] = step;
       }
     }
   }
 
-  if (!step) step = new TestStepWait(this);
+  if (!step) step = new TestStepWaitDiameter(this);
   step->setCondition(fromEntity, code, bitR, usedHopByHop, applicationId, sessionId, resultCode, msisdn, imsi, serviceContextId);
 
   LOGINFORMATION(
     if (hasSameCondition(step->getCondition()))
   step->setCondition(fromEntity, code, bitR, usedHopByHop, applicationId, sessionId, resultCode, msisdn, imsi, serviceContextId);
 
   LOGINFORMATION(
     if (hasSameCondition(step->getCondition()))
-      anna::Logger::information(anna::functions::asString("The same wait condition has already been programmed in this test case (%llu). Are you sure ?", a_id), ANNA_FILE_LOCATION);
+      anna::Logger::information(anna::functions::asString("The same wait condition has already been programmed in this test case (%llu, '%s'). Are you sure ?", a_id, a_description.c_str()), ANNA_FILE_LOCATION);
   );
 
   addStep(step);
 }
 
   );
 
   addStep(step);
 }
 
-void TestCase::addWaitRegexpHex(bool fromEntity, const std::string &regexp) throw(anna::RuntimeException) {
+void TestCase::addWaitDiameterRegexpHex(bool fromEntity, const std::string &regexp) throw(anna::RuntimeException) {
   assertInitialized();
 
   assertInitialized();
 
-  TestStepWait *step = new TestStepWait(this);
+  TestStepWaitDiameter *step = new TestStepWaitDiameter(this);
   step->setConditionRegexpHex(fromEntity, regexp);
 
   LOGINFORMATION(
     if (hasSameCondition(step->getCondition()))
   step->setConditionRegexpHex(fromEntity, regexp);
 
   LOGINFORMATION(
     if (hasSameCondition(step->getCondition()))
-      anna::Logger::information(anna::functions::asString("The same wait condition has already been programmed in this test case (%llu). Are you sure ?", a_id), ANNA_FILE_LOCATION);
+      anna::Logger::information(anna::functions::asString("The same wait condition has already been programmed in this test case (%llu, '%s'). Are you sure ?", a_id, a_description.c_str()), ANNA_FILE_LOCATION);
   );
 
   addStep(step);
 }
 
   );
 
   addStep(step);
 }
 
-void TestCase::addWaitRegexpXml(bool fromEntity, const std::string &regexp) throw(anna::RuntimeException) {
+void TestCase::addWaitDiameterRegexpXml(bool fromEntity, const std::string &regexp) throw(anna::RuntimeException) {
   assertInitialized();
 
   assertInitialized();
 
-  TestStepWait *step = new TestStepWait(this);
+  TestStepWaitDiameter *step = new TestStepWaitDiameter(this);
   step->setConditionRegexpXml(fromEntity, regexp);
 
   LOGINFORMATION(
     if (hasSameCondition(step->getCondition()))
   step->setConditionRegexpXml(fromEntity, regexp);
 
   LOGINFORMATION(
     if (hasSameCondition(step->getCondition()))
-      anna::Logger::information(anna::functions::asString("The same wait condition has already been programmed in this test case (%llu). Are you sure ?", a_id), ANNA_FILE_LOCATION);
+      anna::Logger::information(anna::functions::asString("The same wait condition has already been programmed in this test case (%llu, '%s'). Are you sure ?", a_id, a_description.c_str()), ANNA_FILE_LOCATION);
   );
 
   addStep(step);
   );
 
   addStep(step);
@@ -424,13 +481,22 @@ void TestCase::addCommand(const std::string &cmd) throw(anna::RuntimeException)
   addStep(step);
 }
 
   addStep(step);
 }
 
-TestStepWait *TestCase::searchNextWaitConditionFulfilled(const anna::DataBlock &message, bool waitFromEntity) throw() {
+void TestCase::addIpLimit(unsigned int ipLimit) throw(anna::RuntimeException) {
+  assertInitialized();
+
+  TestStepIpLimit *step = new TestStepIpLimit(this);
+  step->setIpLimit(ipLimit);
+
+  addStep(step);
+}
+
+TestStepWaitDiameter *TestCase::searchNextWaitConditionFulfilled(const anna::DataBlock &message, bool waitFromEntity) throw() {
 
 
-  TestStepWait *result;
+  TestStepWaitDiameter *result;
   for (std::vector<TestStep*>::const_iterator it = a_stepsIt /* current */; it != a_steps.end(); it++) {
     if ((*it)->getType() != TestStep::Type::Wait) continue;
     if ((*it)->isCompleted()) continue;
   for (std::vector<TestStep*>::const_iterator it = a_stepsIt /* current */; it != a_steps.end(); it++) {
     if ((*it)->getType() != TestStep::Type::Wait) continue;
     if ((*it)->isCompleted()) continue;
-    result = (TestStepWait*)(*it);
+    result = (TestStepWaitDiameter*)(*it);
     if ((result->getCondition().receivedFromEntity() == waitFromEntity) && (result->fulfilled(message)))
       return result;
   }
     if ((result->getCondition().receivedFromEntity() == waitFromEntity) && (result->fulfilled(message)))
       return result;
   }
index fcb3c08..6f0bba0 100644 (file)
@@ -10,7 +10,7 @@
 #include <algorithm>
 
 // Project
 #include <algorithm>
 
 // Project
-#include <anna/testing/TestCondition.hpp>
+#include <anna/testing/TestDiameterCondition.hpp>
 
 #include <anna/xml/Node.hpp>
 #include <anna/xml/Compiler.hpp>
 
 #include <anna/xml/Node.hpp>
 #include <anna/xml/Compiler.hpp>
 using namespace anna::testing;
 
 
 using namespace anna::testing;
 
 
-const char* TestCondition::asText(const Type::_v type)
+const char* TestDiameterCondition::asText(const Type::_v type)
 throw() {
   static const char* text [] = { "RegexpXml", "RegexpHex", "Fields" };
   return text [type];
 }
 
 throw() {
   static const char* text [] = { "RegexpXml", "RegexpHex", "Fields" };
   return text [type];
 }
 
-bool TestCondition::exists() const throw() {
+bool TestDiameterCondition::exists() const throw() {
   if (a_type != Type::Fields) return (getRegexp() != "");
 
   return (a_code != "" || a_bitR != "" || a_hopByHop != "" || a_applicationId != "" || a_sessionId != "" || a_resultCode != "" || a_msisdn != "" || a_imsi != "" || a_serviceContextId != "");
 }
 
   if (a_type != Type::Fields) return (getRegexp() != "");
 
   return (a_code != "" || a_bitR != "" || a_hopByHop != "" || a_applicationId != "" || a_sessionId != "" || a_resultCode != "" || a_msisdn != "" || a_imsi != "" || a_serviceContextId != "");
 }
 
-bool TestCondition::comply(const anna::DataBlock &message) const throw() {
+bool TestDiameterCondition::comply(const anna::DataBlock &message) const throw() {
 
   if (a_type == Type::RegexpXml) {
     anna::diameter::codec::Message codecMsg;
 
   if (a_type == Type::RegexpXml) {
     anna::diameter::codec::Message codecMsg;
@@ -55,7 +55,7 @@ bool TestCondition::comply(const anna::DataBlock &message) const throw() {
     std::string msgString = codecMsg.asXMLString();
     msgString.erase(std::remove(msgString.begin(), msgString.end(), '\n'), msgString.end());
 
     std::string msgString = codecMsg.asXMLString();
     msgString.erase(std::remove(msgString.begin(), msgString.end(), '\n'), msgString.end());
 
-    return re.isLike(msgString); 
+    return re.isLike(msgString);
   }
   else if (a_type == Type::RegexpHex) {
     anna::RegularExpression re(getRegexp());
   }
   else if (a_type == Type::RegexpHex) {
     anna::RegularExpression re(getRegexp());
@@ -147,9 +147,9 @@ bool TestCondition::comply(const anna::DataBlock &message) const throw() {
   return true;
 }
 
   return true;
 }
 
-anna::xml::Node* TestCondition::asXML(anna::xml::Node* parent) const
+anna::xml::Node* TestDiameterCondition::asXML(anna::xml::Node* parent) const
 throw() {
 throw() {
-  anna::xml::Node* result = parent->createChild("TestCondition");
+  anna::xml::Node* result = parent->createChild("TestDiameterCondition");
   if (!exists()) return result;
 
   if (a_type == Type::RegexpXml) {
   if (!exists()) return result;
 
   if (a_type == Type::RegexpXml) {
index 5f0f6f6..20a29a2 100644 (file)
@@ -20,6 +20,7 @@
 #include <anna/diameter/helpers/base/functions.hpp>
 #include <anna/diameter/helpers/dcca/functions.hpp>
 #include <anna/comm/functions.hpp>
 #include <anna/diameter/helpers/base/functions.hpp>
 #include <anna/diameter/helpers/dcca/functions.hpp>
 #include <anna/comm/functions.hpp>
+#include <anna/core/functions.hpp>
 #include <anna/diameter.comm/ClientSession.hpp>
 #include <anna/diameter.comm/ServerSession.hpp>
 #include <anna/testing/TestStep.hpp>
 #include <anna/diameter.comm/ClientSession.hpp>
 #include <anna/diameter.comm/ServerSession.hpp>
 #include <anna/testing/TestStep.hpp>
@@ -91,6 +92,7 @@ TestManager::TestManager() :
   a_dumpSuccessReports = false;
 
   a_dumpHexMessages = false;
   a_dumpSuccessReports = false;
 
   a_dumpHexMessages = false;
+  a_dumpStdout = false;
   a_synchronousAmount = 1;
   a_poolRepeats = 0; // repeat disabled by default
   a_poolCycle = 1;
   a_synchronousAmount = 1;
   a_poolRepeats = 0; // repeat disabled by default
   a_poolCycle = 1;
@@ -266,12 +268,12 @@ TestCase *TestManager::findTestCase(unsigned int id) const throw() { // id = -1
   return NULL;
 }
 
   return NULL;
 }
 
-TestCase *TestManager::getTestCase(unsigned int id) throw() {
+TestCase *TestManager::getTestCase(unsigned int id, const std::string &description) throw() {
 
   test_pool_nc_it it = a_testPool.find(id);
   if (it != a_testPool.end()) return it->second;
 
 
   test_pool_nc_it it = a_testPool.find(id);
   if (it != a_testPool.end()) return it->second;
 
-  TestCase *result = new TestCase(id);
+  TestCase *result = new TestCase(id, description);
   a_testPool[id] = result;
   return result;
 }
   a_testPool[id] = result;
   return result;
 }
@@ -428,7 +430,7 @@ void TestManager::receiveDiameterMessage(const anna::DataBlock &message, const a
   }
 
   // Work with Test case:
   }
 
   // Work with Test case:
-  TestStepWait *tsw = tc->searchNextWaitConditionFulfilled(message, true /* comes from entity */);
+  TestStepWaitDiameter *tsw = tc->searchNextWaitConditionFulfilled(message, true /* comes from entity */);
   if (!tsw) { // store as 'uncovered'
     std::string hint = "Uncovered condition for received message from entity over Session-Id '"; hint += sessionId; hint += "'; ";
 
   if (!tsw) { // store as 'uncovered'
     std::string hint = "Uncovered condition for received message from entity over Session-Id '"; hint += sessionId; hint += "'; ";
 
@@ -469,7 +471,7 @@ void TestManager::receiveDiameterMessage(const anna::DataBlock &message, const a
   }
 
   // Work with Test case:
   }
 
   // Work with Test case:
-  TestStepWait *tsw = tc->searchNextWaitConditionFulfilled(message, false /* comes from client */);
+  TestStepWaitDiameter *tsw = tc->searchNextWaitConditionFulfilled(message, false /* comes from client */);
   if (!tsw) { // store as 'uncovered'
     std::string hint = "Uncovered condition for received message from client over Session-Id '"; hint += sessionId; hint += "'; ";
 
   if (!tsw) { // store as 'uncovered'
     std::string hint = "Uncovered condition for received message from client over Session-Id '"; hint += sessionId; hint += "'; ";
 
@@ -512,6 +514,7 @@ throw() {
   result->createAttribute("DumpFailedReports", (a_dumpFailedReports ? "yes":"no"));
   result->createAttribute("DumpSuccessReports", (a_dumpSuccessReports ? "yes":"no"));
   result->createAttribute("DumpHexMessages", (a_dumpHexMessages ? "yes":"no"));
   result->createAttribute("DumpFailedReports", (a_dumpFailedReports ? "yes":"no"));
   result->createAttribute("DumpSuccessReports", (a_dumpSuccessReports ? "yes":"no"));
   result->createAttribute("DumpHexMessages", (a_dumpHexMessages ? "yes":"no"));
+  result->createAttribute("DumpStdout", (a_dumpStdout ? "yes":"no"));
   result->createAttribute("AutoResetHard", (a_autoResetHard ? "yes":"no"));
   result->createAttribute("ReportsDirectory", a_reportsDirectory);
   if (a_clock) {
   result->createAttribute("AutoResetHard", (a_autoResetHard ? "yes":"no"));
   result->createAttribute("ReportsDirectory", a_reportsDirectory);
   if (a_clock) {
@@ -536,8 +539,210 @@ throw() {
   return result;
 }
 
   return result;
 }
 
+anna::xml::Node* TestManager::junitAsXML(anna::xml::Node* parent) const
+throw() {
+  // if only a single testsuite element is present, the testsuites element can be omitted
+  //anna::xml::Node* result = parent->createChild("testsuites");
+  anna::xml::Node* result = parent->createChild("testsuite");
+
+/*
+https://stackoverflow.com/questions/4922867/what-is-the-junit-xml-format-specification-that-hudson-supports
+
+JUnit XSD file
+==============
+https://windyroad.com.au/dl/Open%20Source/JUnit.xsd
+
+EXAMPLES
+========
+<?xml version="1.0" encoding="UTF-8"?>
+<testsuites disabled="" errors="" failures="" name="" tests="" time="">
+    <testsuite disabled="" errors="" failures="" hostname="" id=""
+               name="" package="" skipped="" tests="" time="" timestamp="">
+        <properties>
+            <property name="" value=""/>
+        </properties>
+        <testcase assertions="" classname="" name="" status="" time="">
+            <skipped/>
+            <error message="" type=""/>
+            <failure message="" type=""/>
+            <system-out/>
+            <system-err/>
+        </testcase>
+        <system-out/>
+        <system-err/>
+    </testsuite>
+</testsuites>
+
+Some of these items can occur multiple times:
+
+There can only be one testsuites element, since that's how XML works, but there can be multiple testsuite elements within the testsuites element.
+Each properties element can have multiple property children.
+Each testsuite element can have multiple testcase children.
+Each testcase element can have multiple error, failure, system-out, or system-err children:
+
+<testsuite tests="3">
+    <testcase classname="foo1" name="ASuccessfulTest"/>
+    <testcase classname="foo2" name="AnotherSuccessfulTest"/>
+    <testcase classname="foo3" name="AFailingTest">
+        <failure type="NotEnoughFoo"> details about failure </failure>
+    </testcase>
+</testsuite>
+
+*/
+
+/*
+  <testsuite name=""      <!-- Full (class) name of the test for non-aggregated testsuite documents.
+                               Class name without the package for aggregated testsuites documents. Required -->
+         tests=""     <!-- The total number of tests in the suite, required. -->
+
+         disabled=""  <!-- the total number of disabled tests in the suite. optional -->
+             errors=""    <!-- The total number of tests in the suite that errored. An errored test is one that had an unanticipated problem,
+                               for example an unchecked throwable; or a problem with the implementation of the test. optional -->
+             failures=""  <!-- The total number of tests in the suite that failed. A failure is a test which the code has explicitly failed
+                               by using the mechanisms for that purpose. e.g., via an assertEquals. optional -->
+             hostname=""  <!-- Host on which the tests were executed. 'localhost' should be used if the hostname cannot be determined. optional -->
+         id=""        <!-- Starts at 0 for the first testsuite and is incremented by 1 for each following testsuite -->
+         package=""   <!-- Derived from testsuite/@name in the non-aggregated documents. optional -->
+         skipped=""   <!-- The total number of skipped tests. optional -->
+         time=""      <!-- Time taken (in seconds) to execute the tests in the suite. optional -->
+         timestamp="" <!-- when the test was executed in ISO 8601 format (2014-01-21T16:17:18). Timezone may not be specified. optional -->
+         >
+*/
+  result->createAttribute("name", "ADML Testing Main Testsuite");
+  int poolSize = a_testPool.size();
+  result->createAttribute("tests", poolSize);
+
+  result->createAttribute("errors", 0);
+  result->createAttribute("failures", a_statSummary.getFailedCount());
+  result->createAttribute("hostname", anna::functions::getHostname());
+  result->createAttribute("skipped", a_statSummary.getInitializedCount());
+
+  // Testcases:
+
+/*
+    <!-- testcase can appear multiple times, see /testsuites/testsuite@tests -->
+    <testcase name=""       <!-- Name of the test method, required. -->
+          assertions="" <!-- number of assertions in the test case. optional -->
+          classname=""  <!-- Full class name for the class the test method is in. required -->
+          status=""
+          time=""       <!-- Time taken (in seconds) to execute the test. optional -->
+          >
+
+*/
+  int testcasesLapseMs = 0;
+  int startTimestampMs = 0;
+  double secs;
+  if (poolSize != 0) {
+    test_pool_it it_min = a_testPool.begin();
+    test_pool_it it_max = a_testPool.end();
+    startTimestampMs = (int)((*it_min).second->getStartTimestamp());
+    std::string debugSummary;
+
+    for (test_pool_it it = it_min; it != it_max; it++) {
+      anna::xml::Node* testcase = result->createChild("testcase");
+      auto tc = (*it).second;
+
+      testcasesLapseMs = (int)(tc->getLapseMs());
+      testcase->createAttribute("classname", "adml_testcase");
+      testcase->createAttribute("description", tc->getDescription());
+      testcase->createAttribute("status", TestCase::asText(tc->getState()));
+
+      secs = testcasesLapseMs / 1000.0;
+      testcase->createAttribute("time", anna::functions::asString(secs, "%.2f"));
+
+      if (tc->isFailed()) {
+        anna::xml::Node* failure = testcase->createChild("failure");
+        std::string text = "";
+        debugSummary = tc->getDebugSummary().asString();
+        if (debugSummary != "") {
+          text += "[Testcase debug summary]";
+          text += "<!--";
+          text += debugSummary;
+          text += "-->";
+        }
+        text += "[Testcase information (xml dump)]";
+        text += "<!--";
+        text += tc->asXMLString();
+        text += "-->";
+        failure->createText(text);
+      }
+    }
+  }
+
+  time_t startTimestamp = startTimestampMs/1000;
+  time(&startTimestamp);
+  char buf[sizeof "2011-10-08T07:07:09Z"];
+  strftime(buf, sizeof buf, "%FT%TZ", gmtime(&startTimestamp));
+  // this will work too, if your compiler doesn't support %F or %T:
+  //     //strftime(buf, sizeof buf, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
+  //         std::cout << buf << "\n";
+  result->createAttribute("timestamp", buf);
+
+
+  return result;
+}
+
 std::string TestManager::asXMLString() const throw() {
   anna::xml::Node root("root");
   return anna::xml::Compiler().apply(asXML(&root));
 }
 
 std::string TestManager::asXMLString() const throw() {
   anna::xml::Node root("root");
   return anna::xml::Compiler().apply(asXML(&root));
 }
 
+std::string TestManager::junitAsXMLString() const throw() {
+  anna::xml::Node root("root");
+  return anna::xml::Compiler().apply(junitAsXML(&root));
+}
+
+std::string TestManager::summaryCounts() const throw() {
+
+  std::string result= "\nSummary Counts:\n";
+  unsigned int total = a_statSummary.getTotal();
+  result += "\nTotal:       " + anna::functions::asString(total);
+  result += "\nInitialized: " + anna::functions::asString(a_statSummary.getInitializedCount());
+  result += "\nIn Progress: " + anna::functions::asString(a_statSummary.getInProgressCount());
+  unsigned int failed = a_statSummary.getFailedCount();
+  unsigned int success = a_statSummary.getSuccessCount();
+  result += "\nFailed:      " + anna::functions::asString(failed);
+  result += "\nSuccess:     " + anna::functions::asString(success);
+  std::string verdict = ((failed == 0) && (success > 0) && (total == success)) ? "PASS":"FAIL";
+  if (total != 0) {
+    result += std::string("\n\nVERDICT:     ") + verdict;
+  }
+
+  return result;
+}
+
+std::string TestManager::summaryStates() const throw() {
+
+  std::string result = "\nSummary States:\n";
+  const char *literal = "\n[%s] %s";
+  const char *secsLiteral = " (%.2f secs)";
+
+  int testcasesLapseMs = 0;
+  int startTimestampMs = 0;
+  double secs;
+  int poolSize = a_testPool.size();
+  if (poolSize != 0) {
+    test_pool_it it_min = a_testPool.begin();
+    test_pool_it it_max = a_testPool.end();
+    startTimestampMs = (int)((*it_min).second->getStartTimestamp());
+
+    for (test_pool_it it = it_min; it != it_max; it++) {
+      auto tc = (*it).second;
+
+      testcasesLapseMs = (int)(tc->getLapseMs());
+
+      result += anna::functions::asString(literal, TestCase::asText(tc->getState()), tc->getDescription().c_str());
+
+      if (testcasesLapseMs >= 0) {
+        secs = testcasesLapseMs / 1000.0;
+        result += anna::functions::asString(secsLiteral, secs);
+      }
+    }
+  }
+  else {
+    result +="\nNo test cases programmed !";
+  }
+
+  return result;
+}
+
index 07ed6b3..d975ddd 100644 (file)
@@ -15,6 +15,7 @@
 #include <stdio.h>     // perror
 #include <stdlib.h>    // exit
 #include <sys/wait.h>  // waitpid, pid_t, WNOHANG
 #include <stdio.h>     // perror
 #include <stdlib.h>    // exit
 #include <sys/wait.h>  // waitpid, pid_t, WNOHANG
+#include <climits>
 
 // cmd with fork:
 #include <sys/types.h>
 
 // cmd with fork:
 #include <sys/types.h>
@@ -65,6 +66,7 @@ namespace {
     sigemptyset(&sa.sa_mask);
     sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
     if (sigaction(SIGCHLD, &sa, 0) != -1) {
     sigemptyset(&sa.sa_mask);
     sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
     if (sigaction(SIGCHLD, &sa, 0) != -1) {
+
       status = system(cmd.c_str());
       /* POPEN version:
           char readbuf[256];
       status = system(cmd.c_str());
       /* POPEN version:
           char readbuf[256];
@@ -199,7 +201,7 @@ bool TestStep::decodeMessage(bool trust) throw() {
 
 const char* TestStep::asText(const Type::_v type)
 throw() {
 
 const char* TestStep::asText(const Type::_v type)
 throw() {
-  static const char* text [] = { "Unconfigured", "Timeout", "Sendxml2e", "Sendxml2c", "Delay", "Wait", "Command" };
+  static const char* text [] = { "Unconfigured", "Timeout", "SendDiameterXmlToEntity", "SendDiameterXmlToClient", "Delay", "Wait", "Command", "IpLimit" };
   return text [type];
 }
 
   return text [type];
 }
 
@@ -223,6 +225,9 @@ throw() {
   //  if (a_endTimestamp != 0 && deltaMs > 0) s_aux += anna::functions::asString(" [%.3f]", deltaMs/1000.0);
   result->createAttribute("EndTimestamp", s_aux);
 
   //  if (a_endTimestamp != 0 && deltaMs > 0) s_aux += anna::functions::asString(" [%.3f]", deltaMs/1000.0);
   result->createAttribute("EndTimestamp", s_aux);
 
+  if (a_beginTimestamp != 0 && a_endTimestamp != 0)
+    result->createAttribute("LapseMilliseconds", getLapseMs());
+
   return result;
 }
 
   return result;
 }
 
@@ -241,21 +246,47 @@ bool TestStep::execute() throw() {
     if (a_executed) return false; // avoid repeating (this implies amount consumption)
   }
 
     if (a_executed) return false; // avoid repeating (this implies amount consumption)
   }
 
-  LOGDEBUG(anna::Logger::debug(anna::functions::asString("EXECUTING %s (step number %d) for Test Case %llu (%p)", asText(a_type), a_number, a_testCase->getId(), this), ANNA_FILE_LOCATION));
+  const char *literal = "Execute %s Test Step %d/%d (test case %llu)";
+
+  TestManager& testManager (TestManager::instantiate ());
+  if (testManager.getDumpStdout()) {
+    bool dump = (a_type == Type::Wait) ? (!a_completed) : true;
+    if (dump) {
+    std::cout << std::endl << "=> " << anna::functions::asString(literal, asText(a_type), a_number, a_testCase->steps(), a_testCase->getId()) << std::endl;
+    }
+  }
+
+  LOGDEBUG(anna::Logger::debug(anna::functions::asString(literal, asText(a_type), a_number, a_testCase->steps(), a_testCase->getId()), ANNA_FILE_LOCATION));
   setBeginTimestamp(anna::functions::millisecond());
   a_executed = true;
   return do_execute();
 }
 
 void TestStep::complete() throw() {
   setBeginTimestamp(anna::functions::millisecond());
   a_executed = true;
   return do_execute();
 }
 
 void TestStep::complete() throw() {
-  LOGDEBUG(anna::Logger::debug(anna::functions::asString("COMPLETE %s (step number %d) for Test Case %llu (%p)", asText(a_type), a_number, a_testCase->getId(), this), ANNA_FILE_LOCATION));
+
+  const char *literal = "Complete %s Test Step %d/%d (test case %llu)";
+
+  TestManager& testManager (TestManager::instantiate ());
+  if (testManager.getDumpStdout()) {
+    std::cout << "=> " << anna::functions::asString(literal, asText(a_type), a_number, a_testCase->steps(), a_testCase->getId()) << std::endl;
+  }
+
+  LOGDEBUG(anna::Logger::debug(anna::functions::asString(literal, asText(a_type), a_number, a_testCase->steps(), a_testCase->getId()), ANNA_FILE_LOCATION));
   a_completed = true;
   setEndTimestamp(anna::functions::millisecond());
   do_complete();
 }
 
 void TestStep::reset() throw() {
   a_completed = true;
   setEndTimestamp(anna::functions::millisecond());
   do_complete();
 }
 
 void TestStep::reset() throw() {
-  LOGDEBUG(anna::Logger::debug(anna::functions::asString("RESET %s (step number %d) for Test Case %llu (%p)", asText(a_type), a_number, a_testCase->getId(), this), ANNA_FILE_LOCATION));
+
+  const char *literal = "Reset %s Test Step %d/%d (test case %llu)";
+
+  TestManager& testManager (TestManager::instantiate ());
+  if (testManager.getDumpStdout()) {
+    std::cout << "=> " << anna::functions::asString(literal, asText(a_type), a_number, a_testCase->steps(), a_testCase->getId()) << std::endl;
+  }
+
+  LOGDEBUG(anna::Logger::debug(anna::functions::asString(literal, asText(a_type), a_number, a_testCase->steps(), a_testCase->getId()), ANNA_FILE_LOCATION));
   // type and testCase kept
   a_completed = false;
   a_executed = false;
   // type and testCase kept
   a_completed = false;
   a_executed = false;
@@ -300,16 +331,23 @@ void TestStepTimeout::do_complete() throw() {
   if (stepNumber == a_testCase->steps()) {
     a_testCase->addDebugSummaryHint(anna::functions::asString("Timeout expired (step number %d) but it was the last test case step", stepNumber)); // before report (when set Failed state)
     a_testCase->setState(TestCase::State::Success);
   if (stepNumber == a_testCase->steps()) {
     a_testCase->addDebugSummaryHint(anna::functions::asString("Timeout expired (step number %d) but it was the last test case step", stepNumber)); // before report (when set Failed state)
     a_testCase->setState(TestCase::State::Success);
+    if (TestManager::instantiate().getDumpStdout()) {
+      std::cout << "Expired Timeout Test LAST Step: considered SUCCESSFUL TEST CASE" << std::endl;
+    }
   }
   else if (a_testCase->getState() == TestCase::State::InProgress) { // sure
     a_testCase->addDebugSummaryHint(anna::functions::asString("Timeout expired (step number %d) before test case finished", stepNumber)); // before report (when set Failed state)
     a_testCase->setState(TestCase::State::Failed);
   }
   else if (a_testCase->getState() == TestCase::State::InProgress) { // sure
     a_testCase->addDebugSummaryHint(anna::functions::asString("Timeout expired (step number %d) before test case finished", stepNumber)); // before report (when set Failed state)
     a_testCase->setState(TestCase::State::Failed);
+    if (TestManager::instantiate().getDumpStdout()) {
+      std::cout << "Expired Timeout Test Step: FAILED TEST CASE" << std::endl;
+    }
   }
 
   a_timer = NULL;
 }
 
   }
 
   a_timer = NULL;
 }
 
-void TestStepTimeout::do_reset() throw() {
+void TestStepTimeout::cancelTimer() throw() {
+  if (!a_timer) return;
   try {
     TestManager::instantiate().cancelTimer(a_timer);
   }
   try {
     TestManager::instantiate().cancelTimer(a_timer);
   }
@@ -320,13 +358,17 @@ void TestStepTimeout::do_reset() throw() {
   //a_timeout = 0; THIS IS CONFIGURATION INFO
 }
 
   //a_timeout = 0; THIS IS CONFIGURATION INFO
 }
 
+void TestStepTimeout::do_reset() throw() {
+  cancelTimer();
+}
+
 ////////////////////////////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////////////////////////////
-// TestStepSendxml
+// TestStepSendDiameterXml
 ////////////////////////////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////////////////////////////
-anna::xml::Node* TestStepSendxml::asXML(anna::xml::Node* parent)
+anna::xml::Node* TestStepSendDiameterXml::asXML(anna::xml::Node* parent)
 throw() {
   anna::xml::Node* result = TestStep::asXML(parent);
 throw() {
   anna::xml::Node* result = TestStep::asXML(parent);
-  //parent->createChild("TestStepSendxml");
+  //parent->createChild("TestStepSendDiameterXml");
   std::string msg = "", xmlmsg = "";
 
   // Message
   std::string msg = "", xmlmsg = "";
 
   // Message
@@ -357,12 +399,12 @@ throw() {
   return result;
 }
 
   return result;
 }
 
-bool TestStepSendxml::do_execute() throw() {
+bool TestStepSendDiameterXml::do_execute() throw() {
   bool success = false;
   std::string failReason = "Error sending diameter message";
   anna::diameter::comm::Entity *entity = a_originHost->getEntity(); // by default
   anna::diameter::comm::LocalServer *localServer = a_originHost->getDiameterServer(); // by default
   bool success = false;
   std::string failReason = "Error sending diameter message";
   anna::diameter::comm::Entity *entity = a_originHost->getEntity(); // by default
   anna::diameter::comm::LocalServer *localServer = a_originHost->getDiameterServer(); // by default
-  const TestStepWait *tsw = NULL;
+  const TestStepWaitDiameter *tsw = NULL;
   anna::diameter::comm::Message *msg = a_originHost->createCommMessage();
 
   try {
   anna::diameter::comm::Message *msg = a_originHost->createCommMessage();
 
   try {
@@ -375,7 +417,7 @@ bool TestStepSendxml::do_execute() throw() {
       );
 
       // Referenced request in the 'wait for request step':
       );
 
       // Referenced request in the 'wait for request step':
-      tsw = static_cast<const TestStepWait*>(a_testCase->getStep(a_waitForRequestStepNumber));
+      tsw = static_cast<const TestStepWaitDiameter*>(a_testCase->getStep(a_waitForRequestStepNumber));
       const anna::DataBlock &referenceRequest = tsw->getMsgDataBlock();
       std::string sessionIdReferenceRequest = anna::diameter::helpers::base::functions::getSessionId(referenceRequest);
 
       const anna::DataBlock &referenceRequest = tsw->getMsgDataBlock();
       std::string sessionIdReferenceRequest = anna::diameter::helpers::base::functions::getSessionId(referenceRequest);
 
@@ -491,10 +533,14 @@ bool TestStepSendxml::do_execute() throw() {
     complete();
   }
 
     complete();
   }
 
+  if (TestManager::instantiate().getDumpStdout()) {
+    std::cout << "Executed SendDiameterXml Test Step: " << (success ? "OK":"ERROR") << std::endl;
+  }
+
   return success; // go next if sent was OK
 }
 
   return success; // go next if sent was OK
 }
 
-void TestStepSendxml::do_reset() throw() {
+void TestStepSendDiameterXml::do_reset() throw() {
   a_expired = false;
   //a_message.clear();
   //a_messageAlreadyDecoded = false;
   a_expired = false;
   //a_message.clear();
   //a_messageAlreadyDecoded = false;
@@ -530,11 +576,15 @@ bool TestStepDelay::do_execute() throw() {
 void TestStepDelay::do_complete() throw() {
   if (a_delay == 0) return; // special case
   a_timer = NULL;
 void TestStepDelay::do_complete() throw() {
   if (a_delay == 0) return; // special case
   a_timer = NULL;
+  if (TestManager::instantiate().getDumpStdout()) {
+    std::cout << "Consumed Delay Test Step (" << a_delay << " milliseconds)" << std::endl;
+  }
   next(); // next() invoked here because execute() is always false for delay and never advance the iterator
   // TODO, avoid this recursion
 }
 
   next(); // next() invoked here because execute() is always false for delay and never advance the iterator
   // TODO, avoid this recursion
 }
 
-void TestStepDelay::do_reset() throw() {
+void TestStepDelay::cancelTimer() throw() {
+  if (!a_timer) return;
   if (a_delay == 0) return; // special case
   try {
     TestManager::instantiate().cancelTimer(a_timer);
   if (a_delay == 0) return; // special case
   try {
     TestManager::instantiate().cancelTimer(a_timer);
@@ -546,10 +596,15 @@ void TestStepDelay::do_reset() throw() {
   //a_delay = 0; THIS IS CONFIGURATION INFO
 }
 
   //a_delay = 0; THIS IS CONFIGURATION INFO
 }
 
+void TestStepDelay::do_reset() throw() {
+  cancelTimer();
+}
+
+
 ////////////////////////////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////////////////////////////
-// TestStepWait
+// TestStepWaitDiameter
 ////////////////////////////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////////////////////////////
-void TestStepWait::setCondition(bool fromEntity,
+void TestStepWaitDiameter::setCondition(bool fromEntity,
     const std::string &code, const std::string &bitR, const std::string &hopByHop, const std::string &applicationId,
     const std::string &sessionId, const std::string &resultCode,
     const std::string &msisdn, const std::string &imsi, const std::string &serviceContextId) throw() {
     const std::string &code, const std::string &bitR, const std::string &hopByHop, const std::string &applicationId,
     const std::string &sessionId, const std::string &resultCode,
     const std::string &msisdn, const std::string &imsi, const std::string &serviceContextId) throw() {
@@ -565,20 +620,20 @@ void TestStepWait::setCondition(bool fromEntity,
   a_condition.setServiceContextId(serviceContextId);
 }
 
   a_condition.setServiceContextId(serviceContextId);
 }
 
-void TestStepWait::setConditionRegexpHex(bool fromEntity, const std::string &regexp) throw() {
+void TestStepWaitDiameter::setConditionRegexpHex(bool fromEntity, const std::string &regexp) throw() {
   a_condition.setReceivedFromEntity(fromEntity);
   a_condition.setRegexpHex(regexp);
 }
 
   a_condition.setReceivedFromEntity(fromEntity);
   a_condition.setRegexpHex(regexp);
 }
 
-void TestStepWait::setConditionRegexpXml(bool fromEntity, const std::string &regexp) throw() {
+void TestStepWaitDiameter::setConditionRegexpXml(bool fromEntity, const std::string &regexp) throw() {
   a_condition.setReceivedFromEntity(fromEntity);
   a_condition.setRegexpXml(regexp);
 }
 
   a_condition.setReceivedFromEntity(fromEntity);
   a_condition.setRegexpXml(regexp);
 }
 
-anna::xml::Node* TestStepWait::asXML(anna::xml::Node* parent)
+anna::xml::Node* TestStepWaitDiameter::asXML(anna::xml::Node* parent)
 throw() {
   anna::xml::Node* result = TestStep::asXML(parent);
 throw() {
   anna::xml::Node* result = TestStep::asXML(parent);
-  //parent->createChild("TestStepWait");
+  //parent->createChild("TestStepWaitDiameter");
   std::string msg = "", xmlmsg = "";
 
   // Condition
   std::string msg = "", xmlmsg = "";
 
   // Condition
@@ -616,26 +671,35 @@ throw() {
   return result;
 }
 
   return result;
 }
 
-bool TestStepWait::do_execute() throw() {
+bool TestStepWaitDiameter::do_execute() throw() {
   return a_completed;
 }
 
   return a_completed;
 }
 
-void TestStepWait::do_complete() throw() {
+void TestStepWaitDiameter::do_complete() throw() {
   //a_testCase->process(); // next() not invoked; we only want to reactivate the test case
   // avoid stack overflow: we will process the test case externally when incoming message is fulfilled (TestCase.cpp), and TestManager is noticed
 }
 
   //a_testCase->process(); // next() not invoked; we only want to reactivate the test case
   // avoid stack overflow: we will process the test case externally when incoming message is fulfilled (TestCase.cpp), and TestManager is noticed
 }
 
-bool TestStepWait::fulfilled(const anna::DataBlock &db/*, bool matchSessionId*/) throw() {
+bool TestStepWaitDiameter::fulfilled(const anna::DataBlock &db/*, bool matchSessionId*/) throw() {
   if (a_condition.comply(db/*, matchSessionId*/)) {
     a_message = db; // store matched
     complete();
   if (a_condition.comply(db/*, matchSessionId*/)) {
     a_message = db; // store matched
     complete();
+
+    if (TestManager::instantiate().getDumpStdout()) {
+      std::cout << "Fulfilled Wait diameter Test Step" << std::endl;
+    }
+
     return true;
   }
 
     return true;
   }
 
+  if (TestManager::instantiate().getDumpStdout()) {
+    std::cout << "NOT Fulfilled Wait diameter Test Step" << std::endl;
+  }
+
   return false;
 }
 
   return false;
 }
 
-void TestStepWait::do_reset() throw() {
+void TestStepWaitDiameter::do_reset() throw() {
   a_message.clear();
   a_clientSession = NULL;
   a_serverSession = NULL;
   a_message.clear();
   a_clientSession = NULL;
   a_serverSession = NULL;
@@ -689,6 +753,10 @@ bool TestStepCmd::do_execute() throw() {
 
 void TestStepCmd::do_complete() throw() {
 
 
 void TestStepCmd::do_complete() throw() {
 
+  if (TestManager::instantiate().getDumpStdout()) {
+    std::cout << "Executed Command Test Step (" << a_script << ") [rc=" << a_resultCode << "]" << std::endl;
+  }
+
   a_threadRunning = false;
   if (a_threadDeprecated) {
     a_threadDeprecated = false;
   a_threadRunning = false;
   if (a_threadDeprecated) {
     a_threadDeprecated = false;
@@ -724,3 +792,25 @@ void TestStepCmd::do_reset() throw() {
   a_childPid = -1;
 }
 
   a_childPid = -1;
 }
 
+anna::xml::Node* TestStepIpLimit::asXML(anna::xml::Node* parent)
+throw() {
+  anna::xml::Node* result = TestStep::asXML(parent);
+  std::string limit = (a_ipLimit != UINT_MAX) ? anna::functions::asString(a_ipLimit) : "<no limit>";
+  result->createAttribute("IpLimit", limit);
+
+  return result;
+}
+
+bool TestStepIpLimit::do_execute() throw() {
+  TestManager::instantiate().setInProgressLimit(a_ipLimit);
+  complete();
+  return true; // go next
+}
+
+void TestStepIpLimit::do_complete() throw() {
+  if (TestManager::instantiate().getDumpStdout()) {
+    std::string limit = (a_ipLimit != UINT_MAX) ? anna::functions::asString(a_ipLimit) : "<no limit>";
+    std::cout << "Executed IpLimit Test Step (value = " << limit << ")" << std::endl;
+  }
+}
+