Auto-reset hard/soft for further cycles
[anna.git] / example / diameter / launcher / Launcher.cpp
1 // ANNA - Anna is Not Nothingness Anymore                                                         //
2 //                                                                                                //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
4 //                                                                                                //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite                           //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
7
8
9 // Standard
10 #include <sstream>      // std::istringstream
11 #include <iostream>     // std::cout
12 #include <math.h> // ceil
13 #include <climits>
14 #include <unistd.h> // chdir
15 //#include <regex> TODO: use this from gcc4.9.0: http://stackoverflow.com/questions/8060025/is-this-c11-regex-error-me-or-the-compiler
16 #include <stdio.h>
17
18 // Project
19 #include <anna/timex/Engine.hpp>
20 #include <anna/statistics/Engine.hpp>
21 #include <anna/diameter/codec/functions.hpp>
22 #include <anna/diameter/codec/Engine.hpp>
23 #include <anna/diameter/codec/EngineManager.hpp>
24 #include <anna/http/Transport.hpp>
25 #include <anna/diameter/stack/Engine.hpp>
26 #include <anna/diameter/helpers/base/functions.hpp>
27 #include <anna/time/functions.hpp>
28 #include <anna/diameter.comm/ApplicationMessageOamModule.hpp>
29 #include <anna/testing/defines.hpp>
30 #include <anna/xml/xml.hpp>
31 #include <anna/diameter.comm/OriginHost.hpp>
32 #include <anna/diameter.comm/OriginHostManager.hpp>
33 #include <Procedure.hpp>
34
35 // Process
36 #include <Launcher.hpp>
37 #include <MyDiameterEngine.hpp>
38 #include <anna/testing/TestManager.hpp>
39 #include <anna/testing/TestCase.hpp>
40
41
42 #define SIGUSR2_TASKS_INPUT_FILENAME "sigusr2.in"
43 #define SIGUSR2_TASKS_OUTPUT_FILENAME "sigusr2.out"
44
45
46 const char *ServicesDTD = "\
47 <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
48 <!-- Diameter services DTD -->\n\
49 \n\
50 <!ELEMENT services (stack*, node*)>\n\
51 \n\
52 <!ELEMENT stack EMPTY>\n\
53 <!ATTLIST stack id CDATA #REQUIRED dictionary CDATA #REQUIRED validationMode (BeforeEncoding | AfterDecoding | Always | Never) #IMPLIED validationDepth (Complete | FirstError) #IMPLIED fixMode (BeforeEncoding | AfterDecoding | Always | Never) #IMPLIED ignoreFlagsOnValidation (yes | no) #IMPLIED>\n\
54 <!--\n\
55    Stack record\n\
56 \n\
57     id:                      Normally the id corresponds to the Application-Id for which the dictionary provided is designed\n\
58                              (in multistack applications, it shall be mandatory respect such association to know the stack used\n\
59                              for processed messages).\n\
60     dictionary:              Path to the dictionary file.\n\
61     validationMode:          Sets the validation mode. Default is 'AfterDecoding'.\n\
62     validationDepth:         Sets the validation depth. Default is 'FirstError'.\n\
63     fixMode:                 Sets the fix mode. Default is 'BeforeEncoding'.\n\
64     ignoreFlagsOnValidation: Ignore flags during message validation. Default is 'no'.\n\
65 -->\n\
66 \n\
67 <!ELEMENT node EMPTY>\n\
68 <!ATTLIST node originHost CDATA #REQUIRED applicationId CDATA #REQUIRED originRealm CDATA #IMPLIED cer CDATA #IMPLIED dwr CDATA #IMPLIED cea CDATA #IMPLIED allowedInactivityTime CDATA #IMPLIED tcpConnectDelay CDATA #IMPLIED answersTimeout CDATA #IMPLIED ceaTimeout CDATA #IMPLIED watchdogPeriod CDATA #IMPLIED entity CDATA #IMPLIED entityServerSessions CDATA #IMPLIED diameterServer CDATA #IMPLIED diameterServerSessions CDATA #IMPLIED balance (yes | no) #IMPLIED sessionBasedModelsClientSocketSelection (SessionIdLowPart | SessionIdHighPart | SessionIdOptionalPart | RoundRobin) #IMPLIED retries CDATA #IMPLIED log CDATA #IMPLIED splitLog (yes | no) #IMPLIED detailedLog (yes | no) #IMPLIED dumpLog (yes | no) #IMPLIED burstLog (yes | no) #IMPLIED>\n\
69 <!--\n\
70    Node record\n\
71 \n\
72    originHost:                              Node identifier as diameter application host name.\n\
73    applicationId:                           The Application-Id provided must exists as a registered 'stack id'.\n\
74    originRealm:                             Origin-Realm (domain-name internally used by default).\n\
75 \n\
76 \n\
77    - Diameter clients:\n\
78 \n\
79    entity:                                  Target diameter entity (comma-separated '<address>:<port>' format).\n\
80                                             For example: 10.20.30.40:3868,10.20.30.41:3868. If missing, no entity will be enabled.\n\
81    entityServerSessions:                    Diameter entity server sessions (0: diameter entity disabled). Default value of 1.\n\
82    cer:                                     User defined CER path file to be encoded to establish diameter connections.\n\
83                                             If missing, will be harcoded.\n\
84    dwr:                                     User defined DWR path file to be encoded for diameter protocol keep alive.\n\
85                                             If missing, will be harcoded.\n\
86    tcpConnectDelay:                         Milliseconds to wait TCP connect to any server. If missing, default value of 200 will\n\
87                                             be assigned\n\
88    ceaTimeout:                              Milliseconds to wait CEA from diameter server. If missing, default value of 'answersTimeout'\n\
89                                             will be assigned.\n\
90    watchdogPeriod:                          Milliseconds for watchdog timer (Tw) for diameter keep-alive procedure. If missing, default\n\
91                                             value of 30000 will be assigned.\n\
92    balance:                                 Balance over entity servers instead of doing standard behaviour (first primary, secondary\n\
93                                             if fails, etc.). Default value 'no'.\n\
94    sessionBasedModelsClientSocketSelection: By default, round-robin will be applied for IEC model (SMS/MMS), and Session-Id Low Part\n\
95                                             will be analyzed for ECUR/SCUR model (data, voice and content). You could change ECUR/SCUR\n\
96                                             analysis behaviour providing 'SessionIdHighPart', 'SessionIdOptionalPart' (atoi applied;\n\
97                                             usually subscriber id data, i.e. MSISDN or IMSI) and 'RoundRobin' (also 'SessionIdLowPart').\n\
98 \n\
99 \n\
100    - Diameter servers:\n\
101 \n\
102    diameterServer:                          Diameter own server address in '<address>:<port>' format. For example: 10.20.30.40:3868.\n\
103                                             If missing, no local server will be enabled.\n\
104    diameterServerSessions:                  Diameter own server available connections (0: diameter server disabled). Default value of 1.\n\
105                                             Negative value implies no limit accepting incoming connections.\n\
106    cea:                                     User defined CEA path file to be encoded to answer client CERs.\n\
107                                             If missing, will be harcoded.\n\
108    allowedInactivityTime:                   Milliseconds for the maximum allowed inactivity time on server sessions born over the\n\
109                                             local server before being reset. If missing, default value of 90000 will be assigned.\n\
110 \n\
111 \n\
112    - General:\n\
113 \n\
114    answersTimeout:                          Milliseconds to wait pending application answers from diameter peers. If missing,\n\
115                                             default value of 10000 will be assigned.\n\
116    retries:                                 Expired responses will cause a number of request retransmissions. Disabled by default (0 retries).\n\
117    log:                                     Process log file (operations result, traffic log, etc.). By default '<originHost>.launcher.log'.\n\
118                                             Empty string or \"null\" name, to disable. Warning: there is no rotation for log files\n\
119                                             (use logrotate or whatever you consider).\n\
120    splitLog:                                Splits log file (appends to log filename, extensions with the type of event: see help on\n\
121                                             startup information-level traces). No log files for code/decode and load operations are created.\n\
122                                             Default value 'no'.\n\
123    detailedLog:                             Insert detailed information at log files (timestamps, communication resources, etc.). Useful\n\
124                                             to analyze the messages flow along the sockets (specially on 'balance' mode). Default 'no'.\n\
125    dumpLog:                                 Write to disk every incoming/outcoming message named as:\n\
126                                                '<unix ms timestamp>.<originHost>.<hop by hop>.<end to end>.<message code>.<request|answer>.<type of event>.xml'\n\
127                                             Default value 'no'.\n\
128    burstLog:                                Burst operations log file. By default '<originHost>.launcher.burst'. Empty string or \"null\" name, to disable.\n\
129                                             Warning: there is no rotation for log files (use logrotate or whatever). Output: dot (.) for each\n\
130                                             burst message sent/pushed, cross (x) for popped ones, and order number when multiple of 1% of burst\n\
131                                             list size, plus OTA requests when changed.\n\
132 \n\
133 -->\n\
134 \n\
135 ";
136
137
138 Launcher::Launcher() : anna::comm::Application("launcher", "DiameterLauncher", "1.1"), a_communicator(NULL) {
139   a_timeEngine = NULL;
140   a_counterRecorder = NULL;
141   a_admlMinResolution = 2 * anna::timex::Engine::minResolution; // 2*10 = 20 ms; 1000/20 = 50 ticks per second;
142   //a_admlMinResolution = (anna::Millisecond)100;
143   a_counterRecorderClock = NULL;
144
145   a_workingNode = NULL;
146
147   a_httpServerSocket = NULL;
148 }
149
150
151 std::string Launcher::getSignalUSR2InputFile() const throw() {
152   return (a_initialWorkingDirectory + "/" + SIGUSR2_TASKS_INPUT_FILENAME);
153 }
154
155 std::string Launcher::getSignalUSR2OutputFile() const throw() {
156   return (a_initialWorkingDirectory + "/" + SIGUSR2_TASKS_OUTPUT_FILENAME);
157 }
158
159
160 void Launcher::servicesFromXML(const anna::xml::Node* servicesNode, bool eventOperation) throw(anna::RuntimeException) {
161
162   CommandLine& cl(anna::CommandLine::instantiate());
163   bool allLogsDisabled = cl.exists("disableLogs");
164
165     //<!ATTLIST stack id CDATA #REQUIRED dictionary CDATA #REQUIRED>
166   const anna::xml::Attribute *id, *dictionary;
167
168   // <!ATTLIST node originHost CDATA #REQUIRED applicationId CDATA #REQUIRED cer CDATA #IMPLIED dwr CDATA #IMPLIED allowedInactivityTime CDATA #IMPLIED tcpConnectDelay CDATA #IMPLIED answersTimeout CDATA #IMPLIED ceaTimeout CDATA #IMPLIED watchdogPeriod CDATA #IMPLIED entity CDATA #IMPLIED entityServerSessions CDATA #IMPLIED diameterServer CDATA #IMPLIED diameterServerSessions CDATA #IMPLIED balance (yes | no) #IMPLIED sessionBasedModelsClientSocketSelection (SessionIdLowPart | SessionIdHighPart | SessionIdOptionalPart | RoundRobin) #IMPLIED retries CDATA #IMPLIED log CDATA #IMPLIED splitLog (yes | no) #IMPLIED detailedLog (yes | no) #IMPLIED dumpLog (yes | no) #IMPLIED burstLog (yes | no) #IMPLIED>
169   const anna::xml::Attribute *originHost, *appId, *originRealm, *cer, *dwr, *cea, *allowedInactivityTime, *tcpConnectDelay,
170   *answersTimeout, *ceaTimeout, *watchdogPeriod, *entity, *entityServerSessions,
171   *diameterServer, *diameterServerSessions, *balance, *sessionBasedModelsClientSocketSelection,
172   *retries, *log, *splitLog, *detailedLog, *dumpLog, *burstLog;
173   // Never clear services content from here (append new data from xml). At the moment no node removing is implemented in this process
174
175   // Stacks
176   anna::diameter::stack::Engine &stackEngine = anna::diameter::stack::Engine::instantiate();
177   anna::diameter::stack::Dictionary *d;
178   const anna::diameter::stack::Dictionary *bpd = NULL; // base protocol dictionary
179
180   // Codec engine manager:
181   anna::diameter::codec::EngineManager &em = anna::diameter::codec::EngineManager::instantiate();
182   anna::diameter::codec::Engine *ce;
183
184   ///////////////////////////////////////////
185   // APPLICATION MESSAGE OAM MODULE SCOPES //
186   ///////////////////////////////////////////
187   // We will register a scope per stack id registered. The counters will be dynamically registered at count method.
188   anna::diameter::comm::ApplicationMessageOamModule & appMsgOamModule = anna::diameter::comm::ApplicationMessageOamModule::instantiate();
189   appMsgOamModule.enableCounters(); // this special module is disabled by default (the only)
190   static int scope_id = 3;
191   unsigned int id_value;
192   std::string codecEngineName;
193
194   for(anna::xml::Node::const_child_iterator it = servicesNode->child_begin(); it != servicesNode->child_end(); it++) {
195     std::string nodeName = (*it)->getName();
196
197     if(nodeName == "stack") {
198       // Input data:
199       id = (*it)->getAttribute("id");
200       dictionary = (*it)->getAttribute("dictionary");
201       id_value = id->getIntegerValue();
202
203       if (stackEngine.getDictionary(id_value)) { // Ignore (but don't fail) dictionary load with same stack id already registered
204         LOGWARNING(anna::Logger::warning(anna::functions::asString("Ignore dictionary load for stack id already registered: %llu", id_value), ANNA_FILE_LOCATION));
205         continue;
206       }
207
208       try {
209         d = stackEngine.createDictionary(id_value, dictionary->getValue());
210         LOGDEBUG(anna::Logger::debug(anna::functions::asString("Created dictionary (%p) for stack id %llu", d, id_value), ANNA_FILE_LOCATION));
211
212         // OAM module for counters:
213         appMsgOamModule.createStackCounterScope(scope_id, id_value /* application-id */);
214         scope_id++;
215
216       } catch(anna::RuntimeException &ex) {
217         //_exit(ex.asString());
218         throw ex;
219       }
220
221       bpd = d; // base protocol dictionary in case of monostack. If multistack, will be calculated later
222
223       // Create codec engine and register it in the codec engine manager:
224       codecEngineName = anna::functions::asString("CodecEngineForStackId_%llu", id_value);
225       ce = new anna::diameter::codec::Engine(codecEngineName.c_str(), d);
226       em.registerCodecEngine(id_value, ce);
227
228       // Codec engine configuration:
229       const anna::xml::Attribute *vm_attr = (*it)->getAttribute("validationMode", false /* no exception */);
230       const anna::xml::Attribute *vd_attr = (*it)->getAttribute("validationDepth", false /* no exception */);
231       const anna::xml::Attribute *fm_attr = (*it)->getAttribute("fixMode", false /* no exception */);
232       const anna::xml::Attribute *if_attr = (*it)->getAttribute("ignoreFlagsOnValidation", false /* no exception */);
233
234       std::string vm_value = vm_attr ? vm_attr->getValue() : "AfterDecoding";
235       std::string vd_value = vd_attr ? vd_attr->getValue() : "FirstError";
236       std::string fm_value = fm_attr ? fm_attr->getValue() : "BeforeEncoding";
237
238       anna::diameter::codec::Engine::ValidationMode::_v vm;
239       if (vm_value == "BeforeEncoding") vm = anna::diameter::codec::Engine::ValidationMode::BeforeEncoding;
240       else if (vm_value == "AfterDecoding") vm = anna::diameter::codec::Engine::ValidationMode::AfterDecoding;
241       else if (vm_value == "Always") vm = anna::diameter::codec::Engine::ValidationMode::Always;
242       else if (vm_value == "Never") vm = anna::diameter::codec::Engine::ValidationMode::Never;
243       ce->setValidationMode(vm);
244
245       anna::diameter::codec::Engine::ValidationDepth::_v vd;
246       if (vd_value == "Complete") vd = anna::diameter::codec::Engine::ValidationDepth::Complete;
247       else if (vd_value == "FirstError") vd = anna::diameter::codec::Engine::ValidationDepth::FirstError;
248       ce->setValidationDepth(vd);
249
250       anna::diameter::codec::Engine::FixMode::_v fm;
251       if (fm_value == "BeforeEncoding") fm = anna::diameter::codec::Engine::FixMode::BeforeEncoding;
252       else if (fm_value == "AfterDecoding") fm = anna::diameter::codec::Engine::FixMode::AfterDecoding;
253       else if (fm_value == "Always") fm = anna::diameter::codec::Engine::FixMode::Always;
254       else if (fm_value == "Never") fm = anna::diameter::codec::Engine::FixMode::Never;
255       ce->setFixMode(fm);
256
257       bool if_value = (if_attr ? (if_attr->getValue() == "yes") : false);
258       ce->ignoreFlagsOnValidation(if_value);
259     }
260   }
261
262   // Show loaded stacks:
263   std::cout << "Stacks currently loaded:" << std::endl;
264   std::cout << anna::functions::tab(stackEngine.asString(false /* light */)) << std::endl;
265
266   // Basic checking for multistack:
267   bool multistack = (stackEngine.stack_size() > 1);
268   if (multistack) {
269     bpd = stackEngine.getDictionary(0);
270     if(!bpd)
271       throw anna::RuntimeException("In multistack applications is mandatory register a stack id = 0 using a dictionary which contains the needed elements to build base protocol messages (CER/A, DWR/A, DPR/A, STR/A, etc.)", ANNA_FILE_LOCATION);
272   }
273
274   // REALMS:
275   for(anna::xml::Node::const_child_iterator it = servicesNode->child_begin(); it != servicesNode->child_end(); it++) {
276     std::string nodeName = (*it)->getName();
277
278     if(nodeName == "node") {
279       // Input data:
280       originHost = (*it)->getAttribute("originHost");
281       appId = (*it)->getAttribute("applicationId");
282       originRealm = (*it)->getAttribute("originRealm", false /* no exception */);
283       cer = (*it)->getAttribute("cer", false /* no exception */);
284       dwr = (*it)->getAttribute("dwr", false /* no exception */);
285       cea = (*it)->getAttribute("cea", false /* no exception */);
286       allowedInactivityTime = (*it)->getAttribute("allowedInactivityTime", false /* no exception */);
287       tcpConnectDelay = (*it)->getAttribute("tcpConnectDelay", false /* no exception */);
288       answersTimeout = (*it)->getAttribute("answersTimeout", false /* no exception */);
289       ceaTimeout = (*it)->getAttribute("ceaTimeout", false /* no exception */);
290       watchdogPeriod = (*it)->getAttribute("watchdogPeriod", false /* no exception */);
291       entity = (*it)->getAttribute("entity", false /* no exception */);
292       entityServerSessions = (*it)->getAttribute("entityServerSessions", false /* no exception */);
293       diameterServer = (*it)->getAttribute("diameterServer", false /* no exception */);
294       diameterServerSessions = (*it)->getAttribute("diameterServerSessions", false /* no exception */);
295       balance = (*it)->getAttribute("balance", false /* no exception */); // (yes | no)
296       sessionBasedModelsClientSocketSelection = (*it)->getAttribute("sessionBasedModelsClientSocketSelection", false /* no exception */); // (SessionIdHighPart | SessionIdOptionalPart | RoundRobin)
297       retries = (*it)->getAttribute("retries", false /* no exception */);
298       log = (*it)->getAttribute("log", false /* no exception */);
299       splitLog = (*it)->getAttribute("splitLog", false /* no exception */); // (yes | no)
300       detailedLog = (*it)->getAttribute("detailedLog", false /* no exception */); // (yes | no)
301       dumpLog = (*it)->getAttribute("dumpLog", false /* no exception */); // (yes | no)
302       burstLog = (*it)->getAttribute("burstLog", false /* no exception */); // (yes | no)
303
304       // Basic checkings:
305       anna::diameter::comm::OriginHostManager &ohm = anna::diameter::comm::OriginHostManager::instantiate();
306       anna::diameter::comm::OriginHost *oh = ohm.getOriginHost(originHost->getValue());
307       if (oh) {
308         std::string msg = "Already registered such Origin-Host: "; msg += originHost->getValue();
309         throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
310       }
311
312       unsigned int applicationId = appId->getIntegerValue();
313       if (!stackEngine.getDictionary(applicationId)) {
314         std::string msg = "Cannot found a registered stack id with the value of applicationId provided: "; msg += appId->getValue();
315         throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
316       }
317
318       // Engine time measures checking & assignment:
319       anna::Millisecond allowedInactivityTimeMs(90000);
320       anna::Millisecond tcpConnectDelayMs(200);
321       anna::Millisecond answersTimeoutMs(10000);
322       anna::Millisecond ceaTimeoutMs(10000);
323       anna::Millisecond watchdogPeriodMs(30000);
324
325       if (allowedInactivityTime) allowedInactivityTimeMs = checkTimeMeasure("allowedInactivityTime", allowedInactivityTime->getValue());
326       if (tcpConnectDelay)       tcpConnectDelayMs =       checkTimeMeasure("tcpConnectDelay",       tcpConnectDelay->getValue());
327       if (answersTimeout)        answersTimeoutMs =        checkTimeMeasure("answersTimeout",        answersTimeout->getValue());
328       if (ceaTimeout)            ceaTimeoutMs =            checkTimeMeasure("ceaTimeout",            ceaTimeout->getValue());
329       if (watchdogPeriod)        watchdogPeriodMs =        checkTimeMeasure("watchdogPeriod",        watchdogPeriod->getValue());
330
331       // Checking command line parameters
332       std::string sessionBasedModelsType;
333       anna::diameter::comm::Entity::SessionBasedModelsType::_v sessionBasedModelsTypeEnum;
334       if(sessionBasedModelsClientSocketSelection) {
335         sessionBasedModelsType = sessionBasedModelsClientSocketSelection->getValue();
336         if (sessionBasedModelsType == "RoundRobin") {
337           sessionBasedModelsTypeEnum = anna::diameter::comm::Entity::SessionBasedModelsType::RoundRobin;
338         }
339         else if (sessionBasedModelsType == "SessionIdOptionalPart") {
340           sessionBasedModelsTypeEnum = anna::diameter::comm::Entity::SessionBasedModelsType::SessionIdOptionalPart;
341         }
342         else if (sessionBasedModelsType == "SessionIdHighPart") {
343           sessionBasedModelsTypeEnum = anna::diameter::comm::Entity::SessionBasedModelsType::SessionIdHighPart;
344         }
345         else if (sessionBasedModelsType == "SessionIdLowPart") {
346           sessionBasedModelsTypeEnum = anna::diameter::comm::Entity::SessionBasedModelsType::SessionIdLowPart;
347         }
348         else {
349           throw anna::RuntimeException("Parameter 'sessionBasedModelsClientSocketSelection' only accepts 'SessionIdLowPart'/'SessionIdHighPart'/'SessionIdOptionalPart'/'RoundRobin' as parameter values", ANNA_FILE_LOCATION);
350         }
351       }
352
353       int retransmissions = retries ? retries->getIntegerValue() : 0;
354       if(retransmissions < 0) {
355         throw anna::RuntimeException("Parameter 'retries' must be non-negative", ANNA_FILE_LOCATION);
356       }
357
358       /////////////////////////////////////////////////////////////////////////////////////////////
359       // Diameter communication engine:
360       std::string commEngineName = originHost->getValue() + "_DiameterCommEngine";
361       MyDiameterEngine *commEngine = new MyDiameterEngine(commEngineName.c_str(), bpd);
362       commEngine->setAutoBind(false);  // allow to create client-sessions without binding them, in order to set timeouts.
363       commEngine->setMaxConnectionDelay(tcpConnectDelayMs);
364       commEngine->setWatchdogPeriod(watchdogPeriodMs);
365       commEngine->setOriginHostName(originHost->getValue());
366       if (originRealm) commEngine->setOriginRealmName(originRealm->getValue());
367
368       // Origin host node:
369       a_workingNode = new anna::diameter::comm::OriginHost((anna::diameter::comm::Engine*)commEngine, applicationId);
370       a_workingNode->setRequestRetransmissions(retransmissions);
371       /////////////////////////////////////////////////////////////////////////////////////////////
372
373
374       // Diameter entity:
375       if(entity) {
376         int sessions = entityServerSessions ? entityServerSessions->getIntegerValue() : 1;
377
378         if(sessions > 0) {
379           // Number of sessions:
380           commEngine->setNumberOfClientSessionsPerServer(sessions);
381
382           // Client CER and DWR
383           std::string cerPathfile = cer ? cer->getValue() : "";
384           std::string dwrPathfile = dwr ? dwr->getValue() : "";
385           commEngine->setClientCERandDWR(cerPathfile, dwrPathfile);
386
387           // Register one entity for this engine:
388           a_workingNode->createEntity(entity->getValue(), ceaTimeoutMs, answersTimeoutMs);
389           a_workingNode->getEntity()->setSessionBasedModelsType(sessionBasedModelsTypeEnum);
390           a_workingNode->getEntity()->setBalance(balance ? (balance->getValue() == "yes") : false); // for sendings
391           if (eventOperation) a_workingNode->getEntity()->bind();
392         }
393       }
394
395       // Diameter Server:
396       if(diameterServer) {
397         // Server CEA
398         std::string ceaPathfile = cea ? cea->getValue() : "";
399
400         int sessions = diameterServerSessions ? diameterServerSessions->getIntegerValue() : 1;
401         a_workingNode->createDiameterServer(diameterServer->getValue(), sessions, allowedInactivityTimeMs, answersTimeoutMs, ceaPathfile);
402       }
403
404       // Logs:
405       if (!allLogsDisabled) {
406         std::string host = commEngine->getOriginHostName();
407         std::string s_log = host + ".launcher.log"; if (log) s_log = log->getValue();
408         bool b_splitLog = (splitLog ? (splitLog->getValue() == "yes") : false);
409         bool b_detailedLog = (detailedLog ? (detailedLog->getValue() == "yes") : false);
410         bool b_dumpLog = (dumpLog ? (dumpLog->getValue() == "yes") : false);
411         std::string s_burstLog = host + ".launcher.burst"; if (burstLog) s_burstLog = burstLog->getValue();
412         a_workingNode->setLogs(s_log, b_splitLog, b_detailedLog, b_dumpLog, s_burstLog);
413       }
414
415
416       // Lazy initialization for comm engine:
417       if (eventOperation) commEngine->lazyInitialize();
418
419       // Node and Codec Engine registration ///////////////////////////////////////////////////////
420       ohm.registerOriginHost(originHost->getValue(), a_workingNode);
421       /////////////////////////////////////////////////////////////////////////////////////////////
422     }
423   }
424
425   if (!uniqueOriginHost())
426     a_workingNode = NULL; // by default, mode auto
427
428   // Diameter comm engines which are loaded after application start (via management operation 'services') are not really started,
429   //  but this don't care because application startComponents() -> initialize() -> do_initialize() -> do nothing.
430   // And when stopped, running state is not checked and it will be stopped anyway.
431 }
432
433
434 void Launcher::loadServices(const std::string & xmlPathFile, bool eventOperation) throw(anna::RuntimeException) {
435
436   if (xmlPathFile == "null" || xmlPathFile == "") {
437     LOGWARNING(anna::Logger::warning("Ignoring services configuration on start: empty or 'null' string provided as xml file. Use management interface (operation 'services') in order to add services", ANNA_FILE_LOCATION));
438     return;
439   }
440
441   LOGDEBUG(
442       std::string trace = "Loading ADML services file '";
443   trace += xmlPathFile;
444   trace += "'";
445   anna::Logger::debug(trace, ANNA_FILE_LOCATION);
446   );
447   anna::xml::DocumentFile xmlDocument; // has private copy constructor defined but not implemented to avoid inhenrit/copy (is very heavy)
448   anna::xml::DTDMemory xmlDTD;
449   const anna::xml::Node *rootNode;
450   xmlDocument.initialize(xmlPathFile.c_str()); // fail here is i/o error
451   xmlDTD.initialize(ServicesDTD);
452   try {
453     rootNode = xmlDocument.parse(xmlDTD); // Parsing: fail here if xml violates dtd
454   }
455   catch (anna::RuntimeException &ex) {
456     LOGWARNING(
457         std::string msg = "Services DTD schema:\n\n";
458     msg += ServicesDTD;
459     anna::Logger::warning(msg, ANNA_FILE_LOCATION);
460     );
461     throw ex;
462   }
463
464   LOGDEBUG(
465       std::string trace = "Loaded XML file (";
466   trace += xmlPathFile;
467   trace += "):\n";
468   trace += anna::xml::Compiler().apply(rootNode);
469   anna::Logger::debug(trace, ANNA_FILE_LOCATION);
470   );
471   servicesFromXML(rootNode, eventOperation);
472 }
473
474
475 anna::Millisecond Launcher::checkTimeMeasure(const std::string &parameter, const std::string &value) throw(anna::RuntimeException) {
476
477   if(anna::functions::isLike("^[0-9]+$", value)) {  // para incluir numeros decimales: ^[0-9]+(.[0-9]+)?$
478     int msecs;
479     std::istringstream ( value ) >> msecs;
480
481     if(msecs > a_timeEngine->getMaxTimeout()) { // 600000 ms
482       std::string msg = "Configuration value for '";
483       msg += parameter;
484       msg += "' ("; msg += value; msg += " msecs) is greater than allowed max timeout for timming engine: ";
485       msg += anna::functions::asString(a_timeEngine->getMaxTimeout());
486       throw RuntimeException(msg, ANNA_FILE_LOCATION);
487     }
488
489     if(msecs > 300000) {
490       std::string msg = "Configuration value for '";
491       msg += parameter;
492       msg += "' ("; msg += value; msg += " msecs) is perhaps very big (over 5 minutes).";
493       LOGWARNING(anna::Logger::warning(msg, ANNA_FILE_LOCATION));
494     }
495
496     if(msecs <= a_timeEngine->getResolution()) {
497       std::string msg = "Configuration value for '";
498       msg += parameter;
499       msg += "' ("; msg += value; msg += " msecs) as any other time measure, must be greater than timming engine resolution: ";
500       msg += anna::functions::asString(a_timeEngine->getResolution());
501       throw RuntimeException(msg, ANNA_FILE_LOCATION);
502     }
503
504     return (anna::Millisecond)msecs; // ok
505   }
506
507   // Non-integer exception:
508   std::string msg = "Configuration error for '";
509   msg += parameter;
510   msg += "' = '";
511   msg += value;
512   msg += "': must be a non-negative integer number";
513   throw RuntimeException(msg, ANNA_FILE_LOCATION);
514 }
515
516 bool Launcher::setWorkingNode(const std::string &name) throw() {
517   bool result = false;
518
519   anna::diameter::comm::OriginHostManager &ohm = anna::diameter::comm::OriginHostManager::instantiate();
520   anna::diameter::comm::OriginHost *oh = ohm.getOriginHost(name);
521
522   if (oh) {
523     a_workingNode = const_cast<anna::diameter::comm::OriginHost*>(oh);
524     result = true;
525   }
526
527   return result;
528 }
529
530 anna::diameter::comm::OriginHost *Launcher::getOriginHost(const std::string &name) const throw(anna::RuntimeException) {
531   anna::diameter::comm::OriginHostManager &ohm = anna::diameter::comm::OriginHostManager::instantiate();
532   anna::diameter::comm::OriginHost *result = ohm.getOriginHost(name);
533
534   if (!result)
535   throw anna::RuntimeException(anna::functions::asString("There is no origin host registered as '%s' (set Origin-Host avp correctly or force a specific host with 'node' operation)", name.c_str()), ANNA_FILE_LOCATION);
536
537   return result;
538 }
539
540 anna::diameter::comm::OriginHost *Launcher::getOriginHost(const anna::diameter::codec::Message &message) const throw(anna::RuntimeException) {
541   std::string originHost = message.getAvp(anna::diameter::helpers::base::AVPID__Origin_Host)->getDiameterIdentity()->getValue();
542   return (getOriginHost(originHost));
543 }
544
545 bool Launcher::uniqueOriginHost() const throw() {
546   anna::diameter::comm::OriginHostManager &ohm = anna::diameter::comm::OriginHostManager::instantiate();
547   return (ohm.size() == 1);
548 }
549
550
551 void Launcher::updateOperatedOriginHostWithMessage(const anna::diameter::codec::Message &message) throw(anna::RuntimeException) {
552   if (!a_operatedHost) // priority for working node by mean 'node' operation
553     a_operatedHost = getOriginHost(message);
554 }
555
556 anna::diameter::comm::OriginHost *Launcher::getWorkingNode() const throw(anna::RuntimeException) {
557   if(!a_workingNode)
558     throw anna::RuntimeException("Working node not identified (try to load services)", ANNA_FILE_LOCATION);
559
560   return a_workingNode;
561 }
562
563 anna::diameter::comm::OriginHost *Launcher::getOperatedHost() const throw(anna::RuntimeException) {
564   if(!a_operatedHost)
565     throw anna::RuntimeException("Node not identified (try to force a specific Origin-Host with 'node' operation)", ANNA_FILE_LOCATION);
566
567   return a_operatedHost;
568 }
569
570 MyDiameterEntity *Launcher::getOperatedEntity() const throw(anna::RuntimeException) {
571   MyDiameterEntity *result = (MyDiameterEntity *)(getOperatedHost()->getEntity());
572   if (!result)
573     throw anna::RuntimeException("No entity configured for the operated node", ANNA_FILE_LOCATION);
574   return result;
575 }
576
577 MyLocalServer *Launcher::getOperatedServer() const throw(anna::RuntimeException) {
578   MyLocalServer *result = (MyLocalServer *)(getOperatedHost()->getDiameterServer());
579   if (!result)
580     throw anna::RuntimeException("No local server configured for the operated node", ANNA_FILE_LOCATION);
581   return result;
582 }
583
584 MyDiameterEngine *Launcher::getOperatedEngine() const throw(anna::RuntimeException) {
585   return (MyDiameterEngine *)getOperatedHost()->getCommEngine(); // never will be NULL
586 }
587
588 void Launcher::initialize()
589 throw(anna::RuntimeException) {
590   anna::comm::Application::initialize();
591   CommandLine& cl(anna::CommandLine::instantiate());
592   anna::comm::Communicator::WorkMode::_v workMode(anna::comm::Communicator::WorkMode::Single);
593   a_communicator = new MyCommunicator(workMode);
594   a_timeEngine = new anna::timex::Engine((anna::Millisecond)600000, a_admlMinResolution);
595   anna::testing::TestManager::instantiate().setTimerController(a_timeEngine);
596
597   // Counters record procedure:
598   const char *varname = "cntRecordPeriod";
599   anna::Millisecond cntRecordPeriod = (cl.exists(varname)) ? checkTimeMeasure(varname, cl.getValue(varname)) : (anna::Millisecond)300000;
600   if(cntRecordPeriod != 0) {
601     a_counterRecorderClock = new MyCounterRecorderClock("Counters record procedure clock", cntRecordPeriod); // clock
602     std::string cntDir = ".";
603     if(cl.exists("cntDir")) cntDir = cl.getValue("cntDir");
604     a_counterRecorder = new MyCounterRecorder(cntDir + anna::functions::asString("/Counters.Pid%d", (int)getPid()));
605   }
606
607   // Testing framework:
608   std::string tmDir = ".";
609   if(cl.exists("tmDir")) tmDir = cl.getValue("tmDir");
610   anna::testing::TestManager::instantiate().setReportsDirectory(tmDir);
611
612   // Tracing:
613   if(cl.exists("trace"))
614     anna::Logger::setLevel(anna::Logger::asLevel(cl.getValue("trace")));
615
616   // Load launcher services:
617   loadServices(cl.getValue("services")); // before run (have components to be created)
618 }
619
620 void Launcher::run()
621 throw(anna::RuntimeException) {
622   LOGMETHOD(anna::TraceMethod tm("Launcher", "run", ANNA_FILE_LOCATION));
623   CommandLine& cl(anna::CommandLine::instantiate());
624   anna::diameter::stack::Engine::instantiate();
625
626   // Start time:
627   a_start_time.setNow();
628
629   // Initial working directory:
630   char cwd[1024];
631   if (getcwd(cwd, sizeof(cwd)) == NULL)
632     throw anna::RuntimeException("Cannot retrieve initial working directory !!", ANNA_FILE_LOCATION);
633   a_initialWorkingDirectory = cwd;
634
635   // Statistics:
636   anna::statistics::Engine::instantiate().enable();
637
638   LOGINFORMATION(
639       // Help on startup traces:
640       anna::Logger::information(help(), ANNA_FILE_LOCATION);
641   // Test messages dtd:
642   std::string msg = "\n                     ------------- TESTMESSAGES DTD -------------\n";
643   msg += anna::diameter::codec::MessageDTD;
644   anna::Logger::information(msg, ANNA_FILE_LOCATION);
645   );
646
647   // HTTP Server:
648   if(cl.exists("httpServer")) {
649     anna::comm::Network& network = anna::comm::Network::instantiate();
650     std::string address;
651     int port;
652     anna::functions::getAddressAndPortFromSocketLiteral(cl.getValue("httpServer"), address, port);
653     //const anna::comm::Device* device = network.find(Device::asAddress(address)); // here provide IP
654     const anna::comm::Device* device = *((network.resolve(address)->device_begin())); // trick to solve
655     a_httpServerSocket = new anna::comm::ServerSocket(anna::comm::INetAddress(device, port), cl.exists("httpServerShared") /* shared bind */, &anna::http::Transport::getFactory());
656   }
657
658   ///////////////////////////////
659   // Diameter library COUNTERS //
660   ///////////////////////////////
661   anna::diameter::comm::OamModule & oamDiameterComm = anna::diameter::comm::OamModule::instantiate();
662   oamDiameterComm.initializeCounterScope(1);  // 1000 - 1999
663   oamDiameterComm.enableCounters();
664   oamDiameterComm.enableAlarms();
665   anna::diameter::codec::OamModule & oamDiameterCodec = anna::diameter::codec::OamModule::instantiate();
666   oamDiameterCodec.initializeCounterScope(2);  // 2000 - 2999
667   oamDiameterCodec.enableCounters();
668   oamDiameterCodec.enableAlarms();
669   /////////////////
670   // COMM MODULE //
671   /////////////////
672   /* Main events */
673   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestReceived, "" /* get defaults for enum type*/, 0 /*1000*/);
674   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerReceived,                 "", 1 /*1001*/);
675   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestReceivedOnClientSession, "", 2 /*1002*/);
676   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerReceivedOnClientSession,  "", 3 /*1003*/);
677   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestReceivedOnServerSession, "", 4 /* etc. */);
678   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerReceivedOnServerSession,  "", 5);
679   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentOK,                  "", 6);
680   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentNOK,                 "", 7);
681   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerSentOK,                   "", 8);
682   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerSentNOK,                  "", 9);
683   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentOnClientSessionOK,   "", 10);
684   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentOnClientSessionNOK,  "", 11);
685   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerSentOnClientSessionOK,    "", 12);
686   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerSentOnClientSessionNOK,   "", 13);
687   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentOnServerSessionOK,   "", 14);
688   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentOnServerSessionNOK,  "", 15);
689   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerSentOnServerSessionOK,    "", 16);
690   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerSentOnServerSessionNOK,   "", 17);
691   /* Diameter Base (capabilities exchange & keep alive) */
692   // as client
693   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::CERSentOK,   "", 18);
694   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::CERSentNOK,  "", 19);
695   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::CEAReceived, "", 20);
696   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DWRSentOK,   "", 21);
697   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DWRSentNOK,  "", 22);
698   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DWAReceived, "", 23);
699   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DPRSentOK,   "", 24);
700   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DPRSentNOK,  "", 25);
701   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DPAReceived, "", 26);
702   // as server
703   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::CERReceived, "", 27);
704   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::CEASentOK,   "", 28);
705   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::CEASentNOK,  "", 29);
706   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DWRReceived, "", 30);
707   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DWASentOK,   "", 31);
708   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DWASentNOK,  "", 32);
709   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DPRReceived, "", 33);
710   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DPASentOK,   "", 34);
711   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DPASentNOK,  "", 35);
712   /* server socket operations (enable/disable listening port for any local server) */
713   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::ServerSocketsOpened, "", 36);
714   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::ServerSocketsClosed, "", 37);
715   /* Connectivity */
716   // clients
717   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::UnableToDeliverOverEntity,                  "", 38);
718   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::LostAvailabilityOverClientSession,          "", 39);
719   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RecoveredAvailabilityOverClientSession,     "", 40);
720   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::LostAvailabilityOverServer,                 "", 41);
721   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RecoveredAvailabilityOverServer,            "", 42);
722   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::LostAvailabilityOverEntity,                 "", 43);
723   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RecoveredAvailabilityOverEntity,            "", 44);
724   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::LostAvailabilityOverEngineForEntities,      "", 45);
725   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RecoveredAvailabilityOverEngineForEntities, "", 46);
726   // servers
727   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::UnableToDeliverToClient,                                    "", 47);
728   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::LostConnectionForServerSession,                             "", 48);
729   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::UnbindConnectionForServerSessionDueToInactivityTimeAnomaly, "", 49);
730   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::CreatedConnectionForServerSession,                          "", 50);
731   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::LostAvailabilityOverLocalServer,                            "", 51);
732   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RecoveredAvailabilityOverLocalServer,                       "", 52);
733   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::LostAvailabilityOverEngineForLocalServers,                  "", 53);
734   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RecoveredAvailabilityOverEngineForLocalServers,             "", 54);
735   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentExpired,  "", 55);
736   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentOnClientSessionExpired,  "", 56);
737   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentOnServerSessionExpired,  "", 57);
738   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestRetransmitted,  "", 58);
739   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestRetransmittedOnClientSession,  "", 59);
740   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestRetransmittedOnServerSession,  "", 60);
741   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerReceivedUnknown,  "", 61);
742   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerReceivedOnClientSessionUnknown,  "", 62);
743   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerReceivedOnServerSessionUnknown,  "", 63);
744   //////////////////
745   // CODEC MODULE //
746   //////////////////
747   /* Avp decoding */
748   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::AvpDecode__NotEnoughBytesToCoverAvpHeaderLength,                          "", 0 /*2000*/);
749   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::AvpDecode__IncoherenceBetweenActivatedVBitAndZeroedVendorIDValueReceived, "", 1 /*2001*/);
750   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::AvpDecode__IncorrectLength,                                               "", 2 /*2002*/);
751   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::AvpDecode__DataPartInconsistence,                                         "", 3 /*2003*/);
752   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::AvpDecode__UnknownAvpWithMandatoryBit,                                    "", 4 /*2004*/);
753   /* Message decoding */
754   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::MessageDecode__NotEnoughBytesToCoverMessageHeaderLength, "", 5 /*2005*/);
755   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::MessageDecode__NotEnoughBytesToCoverMessageLength,       "", 6 /*2006*/);
756   /* Avp validation */
757   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::AvpValidation__EnumeratedAvpWithValueDoesNotComplyRestriction, "", 10 /*2010*/);
758   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::AvpValidation__AvpFlagsDoesNotFulfillTheDefinedFlagRules,      "", 11 /*2011*/);
759   /* Message validation */
760   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::MessageValidation__UnknownOperationUnableToValidate, "", 12 /*2012*/);
761   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::MessageValidation__OperationHaveIncoherentFlags,     "", 13 /*2013*/);
762   /* Level validation */
763   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::LevelValidation__MissingFixedRule,                                       "", 14 /*2014*/);
764   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::LevelValidation__FailedRuleForCardinality,                               "", 15 /*2015*/);
765   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::LevelValidation__FailedRuleForCardinalityLessThanNeeded,                 "", 16 /*2016*/);
766   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::LevelValidation__FailedRuleForCardinalityMoreThanNeeded,                 "", 17 /*2017*/);
767   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::LevelValidation__FailedGenericAvpRuleForCardinalityFoundDisregardedItem, "", 18 /*2018*/);
768   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::LevelValidation__FoundDisregardedItemsAndGenericAVPWasNotSpecified,      "", 19 /*2019*/);
769
770
771   /////////////////////////////////
772   // Counter recorder associated //
773   /////////////////////////////////
774   if(a_counterRecorderClock) {
775     oamDiameterComm.setCounterRecorder(a_counterRecorder);
776     oamDiameterCodec.setCounterRecorder(a_counterRecorder);
777     anna::diameter::comm::ApplicationMessageOamModule::instantiate().setCounterRecorder(a_counterRecorder);
778     a_timeEngine->activate(a_counterRecorderClock); // start clock
779   }
780
781   /////////////////////////////
782   // Log statistics concepts //
783   /////////////////////////////
784   if(cl.exists("logStatisticSamples"))
785     logStatisticsSamples(cl.getValue("logStatisticSamples"));
786
787   // Start client connections //////////////////////////////////////////////////////////////////////////////////
788   MyDiameterEntity *entity;
789   anna::diameter::comm::OriginHostManager &ohm = anna::diameter::comm::OriginHostManager::instantiate();
790   for (diameter::comm::origin_hosts_it it = ohm.begin(); it != ohm.end(); it++) {
791     entity = (MyDiameterEntity *)(it->second->getEntity());
792     if (entity) entity->bind();
793   }
794
795   // Go into communicator poll
796   // Reconnection period (tcp reconnect retry time):
797   const char *varname = "reconnectionPeriod";
798   anna::Millisecond reconnectionPeriod = (cl.exists(varname)) ? checkTimeMeasure(varname, cl.getValue(varname)) : (anna::Millisecond)10000;
799
800   a_communicator->setRecoveryTime(reconnectionPeriod);
801   if(cl.exists("httpServer")) a_communicator->attach(a_httpServerSocket);  // HTTP
802   a_communicator->accept();
803 }
804
805 bool Launcher::getDataBlockFromHexFile(const std::string &pathfile, anna::DataBlock &db) const throw(anna::RuntimeException) {
806   // Get hex string
807   static char buffer[8192];
808   std::ifstream infile(pathfile.c_str(), std::ifstream::in);
809
810   if(infile.is_open()) {
811     infile >> buffer;
812     std::string hexString(buffer, strlen(buffer));
813     // Allow colon separator in hex string: we have to remove them before processing with 'fromHexString':
814     hexString.erase(std::remove(hexString.begin(), hexString.end(), ':'), hexString.end());
815     LOGDEBUG(
816         std::string msg = "Hex string (remove colons if exists): ";
817     msg += hexString;
818     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
819     );
820
821     anna::functions::fromHexString(hexString, db); // could launch exception
822     // Close file
823     infile.close();
824     return true;
825   }
826
827   return false;
828 }
829
830 bool Launcher::getContentFromFile(const std::string &pathfile, std::string &content) const throw(anna::RuntimeException) {
831
832   std::ifstream inFile(pathfile.c_str(), std::ifstream::in);
833   if(!inFile.good()) {
834     throw RuntimeException(anna::functions::asString("Unable to open file '%s'", pathfile.c_str()), ANNA_FILE_LOCATION);
835   }
836
837   std::stringstream strStream;
838   strStream << inFile.rdbuf(); //read the file
839   content = strStream.str(); // holds the content of the file
840   inFile.close();
841
842   return true;
843 }
844
845 void Launcher::resetStatistics() throw() {
846   if (a_workingNode) {
847     a_workingNode->getCommEngine()->resetStatistics();
848   }
849   else {
850     anna::diameter::comm::OriginHostManager &ohm = anna::diameter::comm::OriginHostManager::instantiate();
851     for (diameter::comm::origin_hosts_it it = ohm.begin(); it != ohm.end(); it++) {
852       it->second->getCommEngine()->resetStatistics();
853     }
854   }
855 }
856
857 void Launcher::resetCounters() throw() {
858   anna::diameter::comm::OamModule::instantiate().resetCounters();
859   anna::diameter::comm::ApplicationMessageOamModule::instantiate().resetCounters();
860   anna::diameter::codec::OamModule::instantiate().resetCounters();
861 }
862
863 void Launcher::signalUSR2() throw(anna::RuntimeException) {
864
865   std::string inputFile = getSignalUSR2InputFile();
866   std::string outputFile = getSignalUSR2OutputFile();
867
868   LOGNOTICE(
869   std::string msg = "Captured signal SIGUSR2. Reading tasks at '";
870   msg += inputFile;
871   msg += "' (results will be written at '";
872   msg += outputFile;
873   msg += "')";
874   anna::Logger::notice(msg, ANNA_FILE_LOCATION);
875   );
876
877   // Operation:
878   std::string line;
879   std::string response_content;
880   std::ifstream in_file(inputFile);
881   std::ofstream out_file(outputFile);
882
883   if(!in_file.is_open()) throw RuntimeException("Unable to read tasks", ANNA_FILE_LOCATION);
884   if(!out_file.is_open()) throw RuntimeException("Unable to write tasks", ANNA_FILE_LOCATION);
885
886   while(getline(in_file, line)) {
887
888     // Ignore comments and blank lines:
889     if (line[0] == '#')  continue;
890     if (std::string::npos == line.find_first_not_of(" \t")) continue;
891
892     LOGDEBUG(
893         std::string msg = "Processing line: ";
894     msg += line;
895     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
896     );
897
898     try {
899       eventOperation(line, response_content);
900     } catch(RuntimeException &ex) {
901       ex.trace();
902     }
903
904     out_file << response_content << "\n";
905   }
906
907   in_file.close();
908   out_file << "EOF\n";
909   out_file.close();
910 }
911
912 std::string Launcher::help() const throw() {
913   std::string result = "\n";
914   result += "\n                     ------------- HELP -------------\n";
915   result += "\n";
916   result += "\nOVERVIEW";
917   result += "\n--------";
918   result += "\n";
919   result += "\nThe ADML (ANNA Diameter MultiHost Launcher) process is a multi-host node with client and server";
920   result += "\n capabilities as well as balancer (proxy) features. It could be used as diameter server (i.e. to";
921   result += "\n simulate PCRF nodes, OCS systems, etc.), as diameter client (GGSNs, DPIs, etc.), and balancer";
922   result += "\n systems to provide failover to external round-robin launchers. Also, auxiliary encoder/decoder/loader";
923   result += "\n function could be deployed to reinterpret certain external flow and send it to another process.";
924   result += "\n ";
925   result += "\nThe ANNA::diameter_comm built-in module provides a great set of characteristics as multiple connections";
926   result += "\n on both server and client side, definition for multiple-server entities (and not only two as standard";
927   result += "\n establish as minimum), separate statistics analyzer per each resource, automatic CER/CEA and DWR/DWA";
928   result += "\n generation, expiration control and many more features.";
929   result += "\n";
930   result += "\nThe ADML process can easily configure a many origin-host nodes as needed, which will have own endpoints.";
931   result += "\nYou should avoid loop configurations (client and server for that client) because automatic forwarding,";
932   result += "\n is implemented and this would get in a never ending cycle when a request is sent.";
933   result += "\n";
934   result += "\nProcess traces are dump on \"launcher.trace\" and could have any trace level (POSIX levels), usually";
935   result += "\n 'debug' or 'warning'. See ANNA documentation for more details.";
936   result += "\n";
937   result += "\nAs any other ANNA process, context dump could be retrieved sending SIGUSR1 signal:";
938   result += "\n   kill -10 <pid>";
939   result += "\n    or";
940   result += "\n   kill -s SIGUSR1 <pid>";
941   result += "\n    and then";
942   result += "\n   vi /var/tmp/anna.context.<pid>";
943   result += "\n";
944   result += "\nA complete xml report will show all the context information (counters, alarms, statistics,";
945   result += "\n handlers, diameter stacks, etc.), and a powerful log module could dump all the events";
946   result += "\n processed and flow information. Statistics could be analized at context dump and optionally";
947   result += "\n written to disk as sample files (useful for graphs and spreadsheet reports) with all the";
948   result += "\n measurements.";
949   result += "\n";
950   result += "\nAlso SIGUSR2 is handled for management purposes. We will talk later about this.";
951   result += "\n";
952   result += "\n";
953   result += "\nCOMMAND LINE";
954   result += "\n------------";
955   result += "\n";
956   result += "\nStart the launcher process without arguments in order to see all the startup configuration";
957   result += "\n posibilities, many of which could be modified on the air through the management interface";
958   result += "\n (we will talk later about this great feature). There is only one mandatory parameter which";
959   result += "\n is the services definition: --services <services xml file>. You must follow the dtd schema";
960   result += "\n to build a valid services xml file. Some basic examples are:";
961   result += "\n";
962   result += "\nClient configuration:";
963   result += "\n";
964   result += "\n<services>";
965   result += "\n  <!-- Stacks -->";
966   result += "\n  <stack id=\"0\" dictionary=\"dictionary.xml\"/>";
967   result += "\n";
968   result += "\n  <!-- Nodes -->";
969   result += "\n  <node originHost=\"ADML-client\" applicationId=\"0\" entity=\"localhost:3868\"/>";
970   result += "\n</services>";
971   result += "\n";
972   result += "\nServer configuration:";
973   result += "\n";
974   result += "\n<services>";
975   result += "\n  <!-- Stacks -->";
976   result += "\n  <stack id=\"0\" dictionary=\"dictionary.xml\"/>";
977   result += "\n";
978   result += "\n  <!-- Nodes -->";
979   result += "\n  <node originHost=\"ADML-server\" applicationId=\"0\" diameterServer=\"localhost:3868\"/>";
980   result += "\n</services>";
981   result += "\n";
982   result += "\nIf you act as a proxy or a translation agent, you need to combine both former setups, and probably";
983   result += "\n will need to program the answers to be replied through the operations interface. To balance the";
984   result += "\n traffic at your client side you shall use '--balance' and '--sessionBasedModelsClientSocketSelection'";
985   result += "\n arguments in order to define the balancing behaviour. To make hybrid setups you only must mix the nodes:";
986   result += "\n";
987   result += "\nClient and server configuration:";
988   result += "\n";
989   result += "\n<services>";
990   result += "\n  <!-- Stacks -->";
991   result += "\n  <stack id=\"16777236\" dictionary=\"dictionary_Rx.xml\"/>";
992   result += "\n  <stack id=\"16777238\" dictionary=\"dictionary_Gx.xml\"/>";
993   result += "\n  <stack id=\"0\" dictionary=\"dictionary_base.xml\"/>";
994   result += "\n";
995   result += "\n  <!-- Nodes -->";
996   result += "\n  <node originHost=\"ADML-Rx-client\" applicationId=\"16777236\" entity=\"localhost:3868\" cer=\"cer_Rx.xml\"/>";
997   result += "\n  <node originHost=\"ADML-Gx-client\" applicationId=\"16777238\" entity=\"localhost:3868\" cer=\"cer_Gx.xml\"/>";
998   result += "\n</services>";
999   result += "\n";
1000   result += "\n";
1001   result += "\nThe process builds automatically CER and DWR messages as a client, but you could specify your own";
1002   result += "\n as shown in the hybrid former example. Note that the base protocol stack must be registered because";
1003   result += "\n the configuration corresponds to a multistack process which change the stack using the application-id";
1004   result += "\n processed (0 in the case of base protocol messages: CER, CEA, DWR, DWA, DPR, DPA).";
1005   result += "\n";
1006   result += "\nDYNAMIC OPERATIONS";
1007   result += "\n------------------";
1008   result += "\n";
1009   result += "\nADML supports several operations which could be reconized via HTTP interface or SIGUSR2 caugh.";
1010   result += "\nAn operation is specified by mean a string containing the operation name and needed arguments";
1011   result += "\n separated by pipes. These are the available commands:";
1012   result += "\n";
1013   result += "\n--------------------------------------------------------------------------------------- General purpose";
1014   result += "\n";
1015   result += "\nhelp                                 This help.";
1016   result += "\n";
1017   result += "\n--------------------------------------------------------------------------------------- Node management";
1018   result += "\n";
1019   result += "\nnode[|<name>]                         Selects a context working node by mean a registered name (origin-host).";
1020   result += "\n                                      All the subsequent operations will be forced to work with";
1021   result += "\n                                      this node, which makes possible some rare scenarios like";
1022   result += "\n                                      sending unexpected messages on remote peers. This is also";
1023   result += "\n                                      useful for some operations in order to restrict the scope";
1024   result += "\n                                      of action (statistics, communication visibility, etc.).";
1025   result += "\n                                      Empty parameter will show the current configuration.";
1026   result += "\n";
1027   result += "\nnode_auto                             Returns to the default behaviour (smart node selection).";
1028   result += "\n                                      Depending on the operation, this could imply a global";
1029   result += "\n                                      action scope, affecting to all the registered hosts.";
1030   result += "\n                                      This should be the normal configuration. Take into";
1031   result += "\n                                      account that if you fix the working node, this could";
1032   result += "\n                                      affect to things like test programming: communication";
1033   result += "\n                                      resources will override those which would be inferred";
1034   result += "\n                                      from programmed messages Origin-Host avps.";
1035   result += "\n";
1036   result += "\n------------------------------------------------------------------------------------ Parsing operations";
1037   result += "\n";
1038   result += "\ncode|<source_file>|<target_file>     Encodes source file (pathfile) into target file (pathfile).";
1039   result += "\ndecode|<source_file>|<target_file>   Decodes source file (pathfile) into target file (pathfile).";
1040   result += "\nloadxml|<source_file>                Reinterpret xml source file (pathfile).";
1041   result += "\n";
1042   result += "\n------------------------------------------------------------------------------------------- Hot changes";
1043   result += "\n";
1044   result += "\nservices[|source file]               Adds and starts the services specified in the xml file provided.";
1045   result += "\n                                      (if missing, the file 'services.xml' will be used). This is used";
1046   result += "\n                                      to load new nodes once the ADML is started, regardless if command";
1047   result += "\n                                      line '--services' parameter was used or not. Those services which";
1048   result += "\n                                      are not correctly loaded will be ignored to keep the process alive.";
1049   result += "\n                                     If you need to load services as deltas, you must firstly load the";
1050   result += "\n                                      diameter base dictionary with stack id 0, because all the nodes";
1051   result += "\n                                      will use this dictionary to encode/decode base protocol messages";
1052   result += "\n                                      managed by the communication engine.";
1053   result += "\n";
1054   result += "\ndiameterServerSessions|<integer>     Updates the maximum number of accepted connections to diameter";
1055   result += "\n                                      server socket.";
1056   result += "\ncontext[|target file]                Application context could also be written by mean this operation,";
1057   result += "\n                                      and not only through SIGUSR1. If optional path file is missing,";
1058   result += "\n                                      default '/var/tmp/anna.context.<pid>' will be used.";
1059   result += "\ncollect                              Reset statistics and counters to start a new test stage of";
1060   result += "\n                                      performance measurement. Context data can be written at";
1061   result += "\n                                      '/var/tmp/anna.context.<pid>' by mean 'kill -10 <pid>'.";
1062   result += "\n                                      or sending operation 'context|[target file]'.";
1063   result += "\n                                     This operation applies over all the registered host nodes";
1064   result += "\n                                      except if one specific working node has been set.";
1065   result += "\nforceCountersRecord                  Forces dump to file the current counters of the process.";
1066   result += "\nlog-statistics-samples|<list>        Log statistics samples for the provided comma-separated concept id";
1067   result += "\n                                      list, over './sample.<concept id>.csv' files. For example: \"1,2\"";
1068   result += "\n                                      will log concepts 1 and 2. Reserved words \"all\"/\"none\" activates/";
1069   result += "\n                                      deactivates all registered statistics concept identifiers. That ids";
1070   result += "\n                                      are shown at context dump.";
1071   result += "\nchange-dir[|directory]               Changes the execution point which could be fine to ease some";
1072   result += "\n                                     file system interaction tasks. Be care about some requirements";
1073   result += "\n                                     (for example if you have a user defined counters directory as";
1074   result += "\n                                     relative path this must exists from the new execution directory).";
1075   result += "\n                                     If nothing provided, initial working directory will be restored.";
1076   result += "\nshow-oam                             Dumps current counters of the process. This is also done at";
1077   result += "\n                                      process context dump.";
1078   result += "\nshow-stats                           Dumps statistics of the process. This is also done at process";
1079   result += "\n                                      context dump.";
1080   result += "\n";
1081   result += "\n<visibility action>[|<address>:<port>][|socket id]";
1082   result += "\n";
1083   result += "\n       Actions: hide, show (update state) and hidden, shown (query state).";
1084   result += "\n       Acts over a client session for messages delivery (except CER/A, DWR/A, DPR/A).";
1085   result += "\n       If missing server (first parameter) all applications sockets will be affected.";
1086   result += "\n       If missing socket (second parameter) for specific server, all its sockets will be affected.";
1087   result += "\n";
1088   result += "\n       All application client sessions are shown on startup, but standard delivery only use primary";
1089   result += "\n        server ones except if fails. Balance configuration use all the allowed sockets. You could also";
1090   result += "\n        use command line 'sessionBasedModelsClientSocketSelection' to force traffic flow over certain";
1091   result += "\n        client sessions, but for this, hide/show feature seems easier.";
1092   result += "\n";
1093   result += "\n--------------------------------------------------------------------------------------- Flow operations";
1094   result += "\n";
1095   result += "\nsendxml2e|<source_file>    Sends xml source file (pathfile) through configured entity.";
1096   result += "\nsendxml2c|<source_file>    Sends xml source file (pathfile) to client.";
1097   result += "\nanswerxml2e[|source_file]  Answer xml source file (pathfile) for incoming request with same code from entity.";
1098   result += "\n                           The answer is stored in a FIFO queue for a specific message code, then there are";
1099   result += "\n                           as many queues as different message codes have been programmed.";
1100   result += "\nanswerxml2c[|source_file]  Answer xml source file (pathfile) for incoming request with same code from client.";
1101   result += "\n                           The answer is stored in a FIFO queue for a specific message code, then there are";
1102   result += "\n                           as many queues as different message codes have been programmed.";
1103   result += "\nanswerxml<2e/2c>           List programmed answers (to entity/client) if no parameter provided.";
1104   result += "\nanswerxml<2e/2c>|dump      Write programmed answers (to entity/client) to file 'programmed_answer.<message code>.<sequence>',";
1105   result += "\n                           where 'sequence' is the order of the answer in each FIFO code-queue of programmed answers.";
1106   result += "\nanswerxml<2e/2c>|clear     Clear programmed answers (to entity/client).";
1107   result += "\nanswerxml<2e/2c>|exhaust   Disable the corresponding queue rotation, which is the default behaviour.";
1108   result += "\nanswerxml<2e/2c>|rotate    Enable the corresponding queue rotation, useful in performance tests.";
1109   result += "\n                           Rotation consists in add again to the queue, each element retrieved for answering.";
1110   result += "\n";
1111   result += "\nSend operations are available using hexadecimal content (hex formatted files) which also allow to test";
1112   result += "\nspecial scenarios (protocol errors):";
1113   result += "\n";
1114   result += "\nsendhex2e|<source_file>    Sends hex source file (pathfile) through configured entity.";
1115   result += "\nsendhex2c|<source_file>    Sends hex source file (pathfile) to client.";
1116   result += "\n";
1117   result += "\nAnswer programming in hexadecimal is not really neccessary (you could use send primitives) and also";
1118   result += "\n is intended to be used with decoded messages in order to replace things like hop by hop, end to end,";
1119   result += "\n subscriber id, session id, etc. Anyway you could use 'decode' operation and then program the xml created.";
1120   result += "\n";
1121   result += "\nIf a request is received, answer map (built with 'answerxml<2e/2c>' operations) will be";
1122   result += "\n checked to find a corresponding programmed answer to be replied(*). If no ocurrence is found,";
1123   result += "\n or answer message was received, the message is forwarded to the other side (entity or client),";
1124   result += "\n or nothing but trace when no peer at that side is configured. Answer to client have sense when";
1125   result += "\n diameter server socket is configured, answer to entity have sense when entity does.";
1126   result += "\n";
1127   result += "\nIn the most complete situation (process with both client and server side) there are internally";
1128   result += "\n two maps with N FIFO queues, one for each different message code within programmed answers.";
1129   result += "\nOne map is for answers towards the client, and the other is to react entity requests. Then in";
1130   result += "\n each one we could program different answers corresponding to different request codes received.";
1131   result += "\n";
1132   result += "\n(*) sequence values (hop-by-hop and end-to-end), Session-Id and Subscription-Id avps, are mirrored";
1133   result += "\n    to the peer which sent the request. If user wants to test a specific answer without changing it,";
1134   result += "\n    use sendxml<2e/2c>/sendhex<2e/2c> operations better than programming.";
1135   result += "\n";
1136   result += "\nBalance ('--balance' command line parameter) could be used to forward server socket receptions through";
1137   result += "\n entity servers by mean a round-robin algorithm. Both diameter server socket and entity targets should";
1138   result += "\n have been configured, that is to say: launcher acts as client and server. If no balance is used, an";
1139   result += "\n standard delivery is performed: first primary entity server, secondary when fails, etc.";
1140   result += "\n";
1141   result += "\n--------------------------------------------------------------------------- Processing types (log tags)";
1142   result += "\n";
1143   result += "\nUsed as log file extensions (when '--splitLog' is provided on command line) and context preffixes on log";
1144   result += "\n details when unique log file is dumped:";
1145   result += "\n";
1146   result += "\n   [sent2e/send2eError]   Send to entity (success/error)";
1147   result += "\n   [sent2c/send2cError]   Send to client (success/error)";
1148   result += "\n   [fwd2e/fwd2eError]     Forward to entity a reception from client (success/error)";
1149   result += "\n   [fwd2c/fwd2cError]     Forward to client a reception from entity (success/error)";
1150   result += "\n   [recvfc]               Reception from client";
1151   result += "\n   [recvfe]               Reception from entity";
1152   result += "\n   [req2c-expired]        A request sent to client has been expired";
1153   result += "\n   [req2e-expired]        A request sent to entity has been expired";
1154   result += "\n   [recvfc-ans-unknown]   Reception from client of an unknown answer (probably former [req2c-expired]";
1155   result += "\n                           has been logged)";
1156   result += "\n   [recvfe-ans-unknown]   Reception from entity of an unknown answer (probably former [req2e-expired]";
1157   result += "\n                           has been logged)";
1158   result += "\n   [retry]                Request retransmission";
1159   result += "\n";
1160   result += "\n------------------------------------------------------------------------------------------- Burst tests";
1161   result += "\n";
1162   result += "\nIn order to simplify user experience, burst category operations are only allowed in single host node";
1163   result += "\n configuration. Indeed, you could send messages with unmatched Origin-Host, and no warning is shown.";
1164   result += "\nAll the operations are performed through the unique host: if you need to use more interfaces, you may";
1165   result += "\n launch different ADML instances. Is nonsense to allow burst in a multi-host configured ADML, because";
1166   result += "\n this feature is not able to coordinate the messages.";
1167   result += "\n";
1168   result += "\nburst|<action>[|parameter]     Used for performance testing, we first program diameter requests";
1169   result += "\n                                messages in order to launch them from client side to the configured";
1170   result += "\n                                diameter entity. We could start the burst with an initial load";
1171   result += "\n                                (non-asynchronous sending), after this, a new request will be sent";
1172   result += "\n                                per answer received or expired context. There are 10 actions: clear,";
1173   result += "\n                                load, start, push, pop, stop, repeat, send, goto and look.";
1174   result += "\n";
1175   result += "\n   burst|clear                 Clears all loaded burst messages.";
1176   result += "\n   burst|load|<source_file>    Loads the next diameter message into launcher burst.";
1177   result += "\n   burst|start|<initial load>  Starts (or restarts if already in progress) the message sending with";
1178   result += "\n                                a certain initial load.";
1179   result += "\n   burst|push|<load amount>    Sends specific non-aynchronous load.";
1180   result += "\n   burst|pop|<release amount>  Skip send burst messages in order to reduce over-the-air requests.";
1181   result += "\n                               Popping all OTA requests implies burst stop because no more answer";
1182   result += "\n                                will arrive to the process. Burst output file (--burstLog command";
1183   result += "\n                                line parameter) shows popped messages with crosses (x). Each cross";
1184   result += "\n                                represents one received answer for which no new request is sent.";
1185   result += "\n   burst|stop                  Stops the burst cycle. You can resume pushing 1 load amount.";
1186   result += "\n   burst|repeat[|[yes]|no]     Restarts the burst launch when finish. If initial load or push load";
1187   result += "\n                                amount is greater than burst list size, they will be limited when";
1188   result += "\n                                the list is processed except when repeat mode is enabled.";
1189   result += "\n   burst|send|<amount>         Sends messages from burst list. The main difference with start/push";
1190   result += "\n                                operations is that burst won't be awaken. Externally we could control";
1191   result += "\n                                sending time (no request will be sent for answers).";
1192   result += "\n   burst|goto|<order>          Updates current burst pointer position.";
1193   result += "\n   burst|look[|order]          Show programmed burst message for order provided, current when missing.";
1194   result += "\n";
1195   result += "\n-------------------------------------------------------------------------------------- Advanced testing";
1196   result += "\n";
1197   result += "\n                           Burst mode only allows single interface interaction. For multiple interface";
1198   result += "\n                            (origin-host) coordination, you could use the advanced test cases programming:";
1199   result += "\n";
1200   result += "\n";
1201   result += "\n   test|<id>|<command>[|parameters]";
1202   result += "\n";
1203   result += "\n                           Adds a new step to the test case with provided identifier. If provided identifier";
1204   result += "\n                            is not registered yet, a new test case will be created with that value and the";
1205   result += "\n                            step will be added as the first. For a specific 'id', the steps are stored in";
1206   result += "\n                            order as they are programmed. Check possible runtime exceptions when adding a";
1207   result += "\n                            new step because those which fail, will be ignored/skipped during test case";
1208   result += "\n                            programming giving an incomplete sequence invalid for the testing purpose.";
1209   result += "\n";
1210   result += "\n                           <id>: integer number, normally monotonically increased for each test case. Some external";
1211   result += "\n                                 script/procedure shall clone a test case template in order to build a collection";
1212   result += "\n                                 of independent and coherent test cases (normally same type) with different context";
1213   result += "\n                                 values (Session-Id, Subscriber-Id, etc.).";
1214   result += "\n";
1215   result += "\n                           <command>: commands to be executed for the test id provided. Each command programmed";
1216   result += "\n                                      constitutes a test case 'step', numbered from 1 to N.";
1217   result += "\n";
1218   result += "\n                              timeout|<msecs>            Sets an asynchronous timer to restrict the maximum timeout";
1219   result += "\n                                                          until last test step. Normally, this command is invoked";
1220   result += "\n                                                          in the first step, anyway it measures the time from the";
1221   result += "\n                                                          execution point whatever it is. The expiration will abort";
1222   result += "\n                                                          the test if still running. One or more timeouts could be";
1223   result += "\n                                                          programmed (not usual), but the more restrict will apply.";
1224   result += "\n                                                         It is highly recommended to program a initial timeout step,";
1225   result += "\n                                                          or the test case could be eternally in-progress.";
1226   result += "\n";
1227   result += "\n                              sendxml2e|<source_file>[|<step number>]";
1228   result += "\n                                                         Sends xml source file (pathfile) to entity (it would be a";
1229   result += "\n                                                          'forward' event if it came through local server endpoint).";
1230   result += "\n                                                         Take into account that the xml message is encoded just on";
1231   result += "\n                                                          call. The xml file is not longer needed neither interpreted";
1232   result += "\n                                                          in case of modification, after calling this command.";
1233   result += "\n                                                         The step number should be provided for answers to indicate";
1234   result += "\n                                                          the 'wait for request' corresponding step. If you miss this";
1235   result += "\n                                                          reference, the sequence information (hop-by-hop, end-to-end)";
1236   result += "\n                                                          will be sent as they are in the answer xml message (realize";
1237   result += "\n                                                          the difficulty of predicting these information). Be sure to";
1238   result += "\n                                                          refer to a 'wait for request' step. Conditions like 'regexp'";
1239   result += "\n                                                          (as we will see later) are not verified.";
1240   result += "\n                                                         In the case of requests, the step number is used to force the";
1241   result += "\n                                                          copy of Session-Id value from the referred step.";
1242   result += "\n";
1243   result += "\n                              sendxml2c|<source_file>[|<step number>]";
1244   result += "\n                                                         Sends xml source file (pathfile) to client (it would be a";
1245   result += "\n                                                          'forward' event if it came through remote server endpoint).";
1246   result += "\n                                                         Same commented for 'sendxml2e' regarding the step number.";
1247   result += "\n";
1248   result += "\n                              delay|<msecs>              Blocking step until the time lapse expires. Useful to give ";
1249   result += "\n                                                          some cadence control and time schedule for a specific case.";
1250   result += "\n                                                         A value of 0 could be used as a dummy step.";
1251   result += "\n";
1252   result += "\n                              sh-command|<script>        External execution for script/executable via shell through a dedicated";
1253   result += "\n                                                          thread, providing the command and parameters. You could use dynamic";
1254   result += "\n                                                          variables ##<tag> to have more flexibility:";
1255   result += "\n                                                             Test pool cycle id: "; result += SH_COMMAND_TAG_FOR_REPLACE__CYCLE_ID;
1256   result += "\n                                                             Test case id:       "; result += SH_COMMAND_TAG_FOR_REPLACE__TESTCASE_ID;
1257   result += "\n                                                             Test step id:       "; result += SH_COMMAND_TAG_FOR_REPLACE__TESTSTEP_ID;
1258   result += "\n";
1259   result += "\n                                                         For example, your command could be something like this:";
1260   result += "\n                                                          insert_sql_"; result += SH_COMMAND_TAG_FOR_REPLACE__TESTCASE_ID; result += ".sh -db dbname --verbose";
1261   result += "\n                                                             > /tmp/cycle-"; result += SH_COMMAND_TAG_FOR_REPLACE__CYCLE_ID;
1262   result += ".testcase-"; result += SH_COMMAND_TAG_FOR_REPLACE__TESTCASE_ID;
1263   result += ".teststep-"; result += SH_COMMAND_TAG_FOR_REPLACE__TESTSTEP_ID;
1264   result += ".out";
1265   result += "\n                                                         Try to redirect stdout and stderr to avoid ADML output contamination";
1266   result += "\n                                                          with the possible outputs from the scripts. You could also put your";
1267   result += "\n                                                          job in background although sh-command will return 0-value immediately.";
1268   result += "\n";
1269   result += "\n                              wait<fe/fc>-hex|<source_file>[|strict]";
1270   result += "\n                                                         Wait condition, from entity (waitfe-hex) or client (waitfc-hex) to";
1271   result += "\n                                                          match the hexadecimal representation for received messages against";
1272   result += "\n                                                          source file (hex format). Fix mode must be enabled to avoid unexpected";
1273   result += "\n                                                          matching behaviour. Specify 'strict' to use the hex content 'as is'.";
1274   result += "\n                                                          If not, the hex content will be understood as whole message and then,";
1275   result += "\n                                                          borders will be added (^<content>$) and sequence information bypassed";
1276   result += "\n                                                          even for diameter answers.";
1277   result += "\n";
1278   result += "\n                              wait<fe/fc>-xml|<source_file>[|strict]";
1279   result += "\n                                                         Wait condition from entity (waitfe-xml) or client (waitfc-xml) to";
1280   result += "\n                                                          match the serialized xml content for received messages against";
1281   result += "\n                                                          source file (xml representation). Fix mode must be enabled to avoid";
1282   result += "\n                                                          unexpected matching behaviour. If you need a strict matching you";
1283   result += "\n                                                          must add parameter 'strict', if not, regexp is built ignoring sequence";
1284   result += "\n                                                          information (hop-by-hop-id=\"[0-9]+\" end-to-end-id=\"[0-9]+\") and";
1285   result += "\n                                                          Origin-State-Id value.";
1286   result += "\n                                                          All LF codes will be internally removed when comparison is executed";
1287   result += "\n                                                          in order to ease xml content configuration.";
1288   result += "\n";
1289   result += "\n                              wait<fe/fc>|<condition>    Blocking step until condition is fulfilled. The message could";
1290   result += "\n                                                          received from entity (waitfe) or from client (waitfc).";
1291   result += "\n                                                         CPU cost is lower than former 'wait<fe/fc>-<xml|hex>' variants.";
1292   result += "\n";
1293   result += "\n                                          <condition>: Optional parameters which must be fulfilled to continue through the next step.";
1294   result += "\n                                                       Any received message over diameter interfaces will be evaluated against the";
1295   result += "\n                                                        corresponding test case starting from the current step until the first one";
1296   result += "\n                                                        whose condition is fulfilled. If no condition is fulfilled the event will be";
1297   result += "\n                                                        classified as 'uncovered' (normally a test case bad configuration, or perhaps";
1298   result += "\n                                                        a real unexpected message).";
1299
1300   // TODO(***)
1301   //  result += "\n                                        The way to identify the test case, is through registered Session-Id values for";
1302   //  result += "\n                                         programmed requests. But this depends on the type of node. Acting as clients,";
1303   //  result += "\n                                         requests received have Session-Id values which are already registered with";
1304   //  result += "\n                                         one test case, causing an error if not found. Acting as servers, requests are";
1305   //  result += "\n                                         received over a diameter local server from a client which are generating that";
1306   //  result += "\n                                         Session-Id values. Then we know nothing about such values. The procedure in";
1307   //  result += "\n                                         this case is find out a test case not-started containing a condition which";
1308   //  result += "\n                                         comply with the incoming message, and reactivates it.";
1309   // The other solution: register Session-Id values for answers send to client from a local diameter server.
1310
1311   result += "\n                                                       How to answer: a wait condition for a request will store the incoming message";
1312   result += "\n                                                        which fulfills that condition. This message is useful together with the peer";
1313   result += "\n                                                        connection source in a further send step configured with the corresponding";
1314   result += "\n                                                        response. You could also insert a delay between wait and send steps to be";
1315   result += "\n                                                        more realistic (processing time simulation in a specific ADML host node).";
1316   result += "\n                                                        Always, a response send step will get the needed information from the most";
1317   result += "\n                                                        recent wait step finding in reverse order (note that some race conditions";
1318   result += "\n                                                        could happen if your condition is not specific enough).";
1319
1320   result += "\n";
1321   result += "\n                                                       Condition format:";
1322   result += "\n";
1323   result += "\n                                                          [code]|[bitR]|[hopByHop]|[applicationId]|[sessionId]|[resultCode]|[msisdn]|[imsi]|[serviceContextId]";
1324   result += "\n";
1325   result += "\n                                                            code: integer number";
1326   result += "\n                                                            bitR: 1 (request), 0 (answer)";
1327   result += "\n                                                            hopByHop: integer number or request send step reference: #<step number>";
1328   result += "\n";
1329   result += "\n                                                                      Using the hash reference, you would indicate a specific wait condition";
1330   result += "\n                                                                       for answers. The step number provided must correspond to any of the";
1331   result += "\n                                                                       previous send commands (sendxml2e/sendxml2c) configured for a request.";
1332   result += "\n                                                                      This 'hop-by-hop' variant eases the wait condition for answers in the";
1333   result += "\n                                                                       safest way.";
1334   result += "\n";
1335   result += "\n                                                            applicationId: integer number";
1336   result += "\n                                                            sessionId: string";
1337   result += "\n                                                            resultCode: integer number";
1338   result += "\n                                                            msisdn: string";
1339   result += "\n                                                            imsi: string";
1340   result += "\n                                                            serviceContextId: string";
1341   result += "\n";
1342   result += "\n                                                       Take into account these rules, useful in general:";
1343   result += "\n";
1344   result += "\n                                                          - Be as much specific as possible defining conditions to avoid ambiguity sending";
1345   result += "\n                                                            messages out of context due to race conditions. Although you could program several";
1346   result += "\n                                                            times similar conditions, some risky practices will throw a warning trace (if you";
1347   result += "\n                                                            repeat the same condition within the same test case).";
1348   result += "\n                                                          - Adding a ResultCode and/or HopByHop to the condition are only valid waiting answers.";
1349   result += "\n                                                          - Requests hop-by-hop values must be different for all the test case requests.";
1350   result += "\n                                                            RFC says that a hop by hop must be unique for a specific connection, something that";
1351   result += "\n                                                            could be difficult to manage if we have multiple available connections from client";
1352   result += "\n                                                            side endpoint (entity or local server), even if we would have only one connection but";
1353   result += "\n                                                            several host interfaces. It is enough to configure different hop-by-hop values within";
1354   result += "\n                                                            each test case, because on reception, the Session-Id is used to identify that test case.";
1355   result += "\n";
1356   result += "\n";
1357   result += "\n";
1358   result += "\n";
1359   result += "\n                           Programming example:";
1360   result += "\n";
1361   result += "\n                              Basic Rx/Gx scenary: PCEF (Gx) - PCRF - AF (Rx)";
1362   result += "\n";
1363   result += "\n                              test|1|timeout|5000                  (step 1: whole time requirement is 5 seconds)";
1364   result += "\n                              test|1|sendxml2e|CCR-I.xml           (step 2: imagine this xml uses the Session-Id 'SGx')";
1365   result += "\n                              test|1|waitfe|272|0|||SGx|2001       (step 3: waits the CCA for the CCR-I with Result-Code = DIAMETER_SUCCESS)";
1366   result += "\n                              test|1|sendxml2e|AAR-flows.xml       (step 4: imagine this xml uses the Session-Id 'SRx')";
1367   result += "\n                              test|1|waitfe|265|0|||SRx|2001       (step 5: waits the AAA for the AAR-flows with Result-Code = DIAMETER_SUCCESS)";
1368   result += "\n                              test|1|waitfe|258|1|||SGx            (step 6: waits the RAR (install policies) from the PCRF server)";
1369   result += "\n                              test|1|sendxml2e|RAA-install.xml|6   (step 7: sends the response for the RAR)";
1370   result += "\n                              test|1|sendxml2e|CCR-T.xml           (step 8: termination of the Gx session, imagine this xml puts hop-by-hop 'H1')";
1371   result += "\n                              test|1|waitfe|272|0|H1||SGx|2001     (step 9: waits the CCA for the CCR-T with Result-Code = DIAMETER_SUCCESS and hop-by-hop 'H1')";
1372   result += "\n                              test|1|waitfe|258|1|||SGx            (step 10: waits the RAR (remove policies) from the PCRF server)";
1373   result += "\n                              test|1|sendxml2e|RAA-remove.xml|10   (step 11: sends the response for the RAR)";
1374   result += "\n";
1375   result += "\n                              Notes: We added an additional condition in step 9: the hop-by-hop. When we program the corresponding";
1376   result += "\n                                      source request (CCR-T), we configured the value 'H1' for the hop-by-hop. This is an 'application";
1377   result += "\n                                      value' because the real hop-by-hop transported through the client connection is managed by the";
1378   result += "\n                                      diameter stack. But when returned, the transaction pool resolve the original value. This feature";
1379   result += "\n                                      is necessary to ease the implementation of certain diameter agents (proxies for example). In our";
1380   result += "\n                                      case, we could format the hop-by-hop values within the request templates with total freedom to";
1381   result += "\n                                      improve the programmed conditions.";
1382   result += "\n";
1383   result += "\n                                     In the case of 'waiting for requests' is not such easy. Indeed, steps 6 and 10 will write a warning";
1384   result += "\n                                      because they are the same condition. We know that we are not going to have any problem because";
1385   result += "\n                                      such events are blocking-protected regarding logic-dependent messages (CCR-T), and race condition";
1386   result += "\n                                      is absolutely strange in this case.";
1387   result += "\n";
1388   result += "\n                                     You could speed up the test case moving forward steps like 3 & 5, understood as non-strict requirements";
1389   result += "\n                                      to continue testing. Anyway, remember that test cases should be as real as possible, and that there";
1390   result += "\n                                      are many ways to increase the load rate as we will see in next section (test cases execution).";
1391   result += "\n";
1392   result += "\n                                     Other simplifications: the steps 3, 5 and 9 can be replaced by";
1393   result += "\n";
1394   result += "\n                                        test|1|waitfe||0|#2";
1395   result += "\n                                        test|1|waitfe||0|#4";
1396   result += "\n                                        test|1|waitfe||0|#8";
1397   result += "\n";
1398   result += "\n                                        which means that hop-by-hop must be retrieved from steps 2, 4 and 8 respectively,";
1399   result += "\n                                        and the expected message shall be an answer. Normally you will add other conditions,";
1400   result += "\n                                        for example a DIAMETER_SUCCESS result (adding 2001 as Result-Code).";
1401   result += "\n";
1402   result += "\nTest cases execution:";
1403   result += "\n";
1404   result += "\n";
1405   result += "\n   test|ttps|<amount>            Starts/resume the provided number of test ticks per second (ttps). The ADML starts";
1406   result += "\n                                 with the event trigger system suspended, and this operation is neccessary to begin";
1407   result += "\n                                 those cases which need this time event (internal triggering). Some other test cases";
1408   result += "\n                                 could be started through external events (first test case event could be programmed";
1409   result += "\n                                 to wait specific message), but is not usual this external mode and neither usual to";
1410   result += "\n                                 mix triggering types. Normally, you will pause/stop new test launchs providing 0 as";
1411   result += "\n                                 ttps value, and also you could dynamically modify the load rate updating that value.";
1412   result += "\n                                 If a test case has N messages then 'ttps * N' will be the virtual number of messages";
1413   result += "\n                                 managed per second when no bottleneck exists.";
1414   result += "\n";
1415   result += "\n                                 Provide 0 in order to stop the timer triggering.";
1416   result += "\n";
1417   result += "\n                                 The timer manager resolution currently harcoded allows a maximum  of ";
1418   result += anna::functions::asString(1000/a_admlMinResolution); result += " events";
1419   result += "\n                                 per second. To reach greater rates ADML will join synchronously the needed number of";
1420   result += "\n                                 new time-triggered test cases per a single event, writting a warning-level trace to";
1421   result += "\n                                 advice about the risk of burst sendings and recommend launching multiple instances";
1422   result += "\n                                 to achieve such load with a lower rate per instance.";
1423   result += "\n";
1424   result += "\n   test|next[|<sync-amount>]     Forces the execution of the next test case(s) without waiting for test manager tick.";
1425   result += "\n                                 Provide an integer value for 'sync-amount' to send a burst synchronous amount of the";
1426   result += "\n                                 next programmed test cases (1 by default). This event works regardless the timer tick";
1427   result += "\n                                 function, but it is normally used with the test manager tick stopped.";
1428   result += "\n";
1429   result += "\n   test|ip-limit[|amount]        In-progress limit of test cases. No new test cases will be launched over this value";
1430   result += "\n                                 (test Manager tick work will be ignored). Zero-value is equivalent to stop the clock.";
1431   result += "\n                                 tick, -1 is used to specify 'no limit' which is the default. If missing amount, the";
1432   result += "\n                                 limit and current amount of in-progress test cases will be shown.";
1433   result += "\n";
1434   result += "\n   test|goto|<id>                Updates current test pointer position.";
1435   result += "\n";
1436   result += "\n   test|look[|id]                Show programmed test case for id provided, current 'in-process' test case when missing.";
1437   result += "\n                                 Test cases reports are not dumped on process context (too many information in general).";
1438   result += "\n                                 The report contains context information in every moment: this operation acts as a snapshot.";
1439   result += "\n";
1440   result += "\n   test|interact|amount|id       Makes interactive a specific test case id. The amount is the margin of execution steps";
1441   result += "\n                                 to be done. Normally, we will execute 'test|interact|0|<test case id>', which means that";
1442   result += "\n                                 the test case is selected to be interactive, but no step is executed. Then you have to";
1443   result += "\n                                 interact with positive amounts (usually 1), executing the provided number of steps if";
1444   result += "\n                                 they are ready and fulfill the needed conditions. The value of 0, implies no execution";
1445   result += "\n                                 steps margin, which could be useful to 'freeze' a test in the middle of its execution.";
1446   result += "\n                                 You could also provide -1 to make it non-interactive resuming it from the current step.";
1447   result += "\n";
1448   result += "\n   test|reset|<[soft]/hard>[|id] Reset the test case for id provided, all the tests when missing. It could be hard/soft:";
1449   result += "\n                                 - hard: you probably may need to stop the load rate before. This operation initializes";
1450   result += "\n                                         all test cases regardless their states.";
1451   result += "\n                                 - soft: only for finished cases (those with 'Success' or 'Failed' states). It does not";
1452   result += "\n                                         affect to test cases with 'InProgress' state.";
1453   result += "\n";
1454   result += "\n   test|repeats|<amount>         Restarts the whole programmed test list when finished the amount number of times (repeats";
1455   result += "\n                                 forever if value -1 is provided). This is disabled by default (amount = 0): testing trigger";
1456   result += "\n                                 system will enter suspended state until new ttps operation is received and a soft reset has";
1457   result += "\n                                 been done before. Test cases state & data will be reset (when achieved again), but general";
1458   result += "\n                                 statistics and counters will continue measuring until reset with 'collect' operation.";
1459   result += "\n";
1460   result += "\n   test|auto-reset|<soft|hard>   When cycling, current test cases can be soft (default) or hard reset. If no timeout has";
1461   result += "\n                                 been configured for the test case, hard reset could prevent stuck on the next cycle for";
1462   result += "\n                                 those test cases still in progress.";
1463   result += "\n";
1464   result += "\n   test|clear                    Clears all the programmed test cases and stop testing (if in progress).";
1465   result += "\n";
1466   result += "\n   test|summary                  Test manager general report (number of test cases, counts by state, global configuration,";
1467   result += "\n                                 forced in-progress limitation, reports visibility, etc.). Be careful when you have reports";
1468   result += "\n                                 enabled because the programmed test cases dumps could be heavy (anyway you could enable the";
1469   result += "\n                                 dumps separately, for any of the possible states: Initialized, InProgress, Failed, Success).";
1470   result += "\n";
1471   result += "\n   test|report|<initialized/in-progress/failed/success/[all]/none>[|[yes]|no]";
1472   result += "\n";
1473   result += "\n                                 Enables/disables report generation for a certain test case state: initialized, in-progress,";
1474   result += "\n                                 failed or success (also 'all' and 'none' reserved words could be used). This applies to report";
1475   result += "\n                                 summary (former described operation) and automatic dumps during testing where only failed or";
1476   result += "\n                                 successful states will appear: every time a test case is finished its xml representation will";
1477   result += "\n                                 be dump on a file under the execution directory (or the one configured in process command-line";
1478   result += "\n                                 'tmDir') with the name:";
1479   result += "\n";
1480   result += "\n                                    'cycle-<cycle id>.testcase-<test case id>.xml'.";
1481   result += "\n";
1482   result += "\n                                 By default, all the states are disabled to avoid IO overload. In most of cases not all the";
1483   result += "\n                                 tests are going to fail then you could enable only such failed dumps. Anyway you could set";
1484   result += "\n                                 the reports visibility to fit your needs in a given situation.";
1485   result += "\n";
1486   result += "\n   test|report-hex[|[yes]|no]    Reports could include the diameter messages in hexadecimal format. Disabled by default.";
1487   result += "\n";
1488   result += "\n";
1489   result += "\n------------------------------------------------------------------------------------- Dynamic procedure";
1490   result += "\n";
1491   result += "\ndynamic[|args]                   This launch an internal operation implemented in 'Procedure' class.";
1492   result += "\n                                 Its default implementation does nothing, but you could create a dynamic";
1493   result += "\n                                 library 'libanna_launcherDynamic.so' and replace the one in this project.";
1494   result += "\n                                 One interesting application consists in the use of the diameter API and";
1495   result += "\n                                 event operation to create a set of libraries as the testing framework.";
1496   result += "\n                                 To execute each test case, the ADML process would be executed with a";
1497   result += "\n                                 specific library path. But the main use would be the stress programming";
1498   result += "\n                                 to achieve a great amount of cloned (even mixed) tests without using";
1499   result += "\n                                 the management operation interface by mean http or signals: a single";
1500   result += "\n                                 call to 'dynamic' would be enough to start a cascade of internally";
1501   result += "\n                                 implemented operations.";
1502   result += "\n                                 This operation accepts a generic string argument (piped or not, as you";
1503   result += "\n                                 desire and depending on your procedure implementation).";
1504   result += "\n";
1505   result += "\n                                 This operation requires advanced programming and knowlegde of ANNA Diameter";
1506   result += "\n                                 stack and testing framework, to take advantage of all the possibilities.";
1507   result += "\n";
1508   result += "\n";
1509   result += "\n";
1510   result += "\nUSING OPERATIONS INTERFACE";
1511   result += "\n--------------------------";
1512   result += "\n";
1513   result += "\n------------------------------------------------------------------------- Operations via HTTP interface";
1514   result += "\n";
1515   result += "\nAll the operations described above can be used through the optional HTTP interface. You only have";
1516   result += "\n to define the http server at the command line with something like: '--httpServer localhost:9000'.";
1517   result += "\nTo send the task, we shall build the http request body with the operation string. Some examples";
1518   result += "\n using curl client could be:";
1519   result += "\n";
1520   result += "\n   curl -m 1 --data \"diameterServerSessions|4\" localhost:9000";
1521   result += "\n   curl -m 1 --data \"code|ccr.xml\" localhost:9000";
1522   result += "\n   curl -m 1 --data \"decode|ccr.hex\" localhost:9000";
1523   result += "\n   curl -m 1 --data \"sendxml2e|ccr.xml\" localhost:9000";
1524   result += "\n   etc.";
1525   result += "\n";
1526   result += "\n------------------------------------------------------------------------- Operations via SIGUSR2 signal";
1527   result += "\n";
1528   result += "\nThe alternative using SIGUSR2 signal requires the creation of the task(s) file which will be read at";
1529   result += "\n signal event:";
1530   result += "\n   echo \"<<operation>\" > "; result += getSignalUSR2InputFile();
1531   result += "\n    then";
1532   result += "\n   kill -12 <pid>";
1533   result += "\n    or";
1534   result += "\n   kill -s SIGUSR2 <pid>";
1535   result += "\n    and then see the results:";
1536   result += "\n   cat "; result += getSignalUSR2OutputFile();
1537   result += "\n";
1538   result += "\n   (this file is ended with EOF final line, useful managing huge batch files to ensure the job completion)";
1539   result += "\n";
1540   result += "\nYou could place more than one line (task) in the input file. Output reports will be appended in that";
1541   result += "\n case over the output file. Take into account that all the content of the task file will be executed";
1542   result += "\n sinchronously by the process. If you are planning traffic load, better use the asynchronous http";
1543   result += "\n interface.";
1544   result += "\n";
1545   result += "\n";
1546
1547   return result;
1548 }
1549
1550
1551 void Launcher::logStatisticsSamples(const std::string &conceptsList) throw() {
1552   anna::statistics::Engine &statEngine = anna::statistics::Engine::instantiate();
1553
1554   if(conceptsList == "all") {
1555     if(statEngine.enableSampleLog(/* -1: all concepts */))
1556       LOGDEBUG(anna::Logger::debug("Sample log activation for all statistic concepts", ANNA_FILE_LOCATION));
1557   }
1558   else if(conceptsList == "none") {
1559       if(statEngine.disableSampleLog(/* -1: all concepts */))
1560         LOGDEBUG(anna::Logger::debug("Sample log deactivation for all statistic concepts", ANNA_FILE_LOCATION));
1561   } else {
1562     anna::Tokenizer lst;
1563     lst.apply(conceptsList, ",");
1564
1565     if(lst.size() >= 1) {
1566       anna::Tokenizer::const_iterator tok_min(lst.begin());
1567       anna::Tokenizer::const_iterator tok_max(lst.end());
1568       anna::Tokenizer::const_iterator tok_iter;
1569       int conceptId;
1570
1571       for(tok_iter = tok_min; tok_iter != tok_max; tok_iter++) {
1572         conceptId = atoi(anna::Tokenizer::data(tok_iter));
1573
1574         if(statEngine.enableSampleLog(conceptId))
1575           LOGDEBUG(anna::Logger::debug(anna::functions::asString("Sample log activation for statistic concept id = %d", conceptId), ANNA_FILE_LOCATION));
1576       }
1577     }
1578   }
1579 }
1580
1581
1582 void Launcher::eventOperation(const std::string &operation, std::string &response_content) throw(anna::RuntimeException) {
1583   LOGMETHOD(anna::TraceMethod tm("Launcher", "eventOperation", ANNA_FILE_LOCATION));
1584   if (operation == "") return; // ignore
1585   LOGDEBUG(anna::Logger::debug(anna::functions::asString("Operation: %s", operation.c_str()), ANNA_FILE_LOCATION));
1586
1587   // Default response:
1588   response_content = "Operation processed with exception: ";
1589   response_content += operation;
1590   std::string opt_response_content = ""; // aditional response content
1591   anna::DataBlock db_aux(true);
1592   anna::diameter::codec::Message codecMsg; // auxiliary codec message
1593
1594   // Singletons:
1595   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1596
1597
1598   ///////////////////////////////////////////////////////////////////
1599   // Simple operations without arguments:
1600
1601   // Dynamic operation:
1602   if(operation.find("dynamic") == 0) {
1603     Procedure p(this);
1604     int op_size = operation.size();
1605     std::string args = ((operation.find("dynamic|") == 0) && (op_size > 8)) ? operation.substr(8) : "";
1606     if (args == "" && op_size != 7)
1607       throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
1608     try {
1609       p.execute(args, response_content);
1610     }
1611     catch(anna::RuntimeException &ex) {
1612       ex.trace();
1613       response_content = ex.asString();
1614       return;
1615     }
1616     return;
1617   }
1618
1619   // Help:
1620   if(operation == "help") {
1621     response_content = help();
1622     return;
1623   }
1624
1625   // Reset performance data:
1626   if(operation == "collect") {
1627     resetCounters();
1628     resetStatistics();
1629     response_content = "All process counters & statistic information have been reset";
1630     return;
1631   }
1632
1633   // Counters dump on demand:
1634   if(operation == "forceCountersRecord") {
1635     forceCountersRecord();
1636     response_content = "Current counters have been dump to disk";
1637     return;
1638   }
1639
1640   // OAM & statistics:
1641   if(operation == "show-oam") {
1642     anna::xml::Node root("root");
1643     response_content = anna::xml::Compiler().apply(oamAsXML(&root));
1644     return;
1645   }
1646   if(operation == "show-stats") {
1647     anna::xml::Node root("root");
1648     response_content = anna::xml::Compiler().apply(statsAsXML(&root));
1649     return;
1650   }
1651
1652   ///////////////////////////////////////////////////////////////////
1653   // Tokenize operation
1654   Tokenizer params;
1655   params.apply(operation, "|", "<null>" /* allow contiguous separators */);
1656   int numParams = params.size() - 1;
1657
1658   // Get the operation type and parameters:
1659   Tokenizer::const_iterator tok_iter = params.begin();
1660   std::string opType = Tokenizer::data(tok_iter);
1661   std::string param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11;
1662   if(numParams >= 1) { tok_iter++; param1 = Tokenizer::data(tok_iter); }
1663   if(numParams >= 2) { tok_iter++; param2 = Tokenizer::data(tok_iter); }
1664   if(numParams >= 3) { tok_iter++; param3 = Tokenizer::data(tok_iter); }
1665   // Tests conditions
1666   if(numParams >= 4) { tok_iter++; param4 = Tokenizer::data(tok_iter); }
1667   if(numParams >= 5) { tok_iter++; param5 = Tokenizer::data(tok_iter); }
1668   if(numParams >= 6) { tok_iter++; param6 = Tokenizer::data(tok_iter); }
1669   if(numParams >= 7) { tok_iter++; param7 = Tokenizer::data(tok_iter); }
1670   if(numParams >= 8) { tok_iter++; param8 = Tokenizer::data(tok_iter); }
1671   if(numParams >= 9) { tok_iter++; param9 = Tokenizer::data(tok_iter); }
1672   if(numParams >= 10) { tok_iter++; param10 = Tokenizer::data(tok_iter); }
1673   if(numParams >= 11) { tok_iter++; param11 = Tokenizer::data(tok_iter); }
1674   // Remove '<null>' artificial token to ease further checkings:
1675   if (param1 == "<null>") param1 = "";
1676   if (param2 == "<null>") param2 = "";
1677   if (param3 == "<null>") param3 = "";
1678   if (param4 == "<null>") param4 = "";
1679   if (param5 == "<null>") param5 = "";
1680   if (param6 == "<null>") param6 = "";
1681   if (param7 == "<null>") param7 = "";
1682   if (param8 == "<null>") param8 = "";
1683   if (param9 == "<null>") param9 = "";
1684   if (param10 == "<null>") param10 = "";
1685   if (param11 == "<null>") param11 = "";
1686
1687   // No operation has more than 2 arguments except 'test' ...
1688   if(opType != "test" && numParams > 2)
1689     throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
1690
1691
1692   // Check the number of parameters:
1693   bool wrongBody = false;
1694
1695   if((opType == "change-dir") && (numParams > 1)) wrongBody = true;
1696   if((opType == "log-statistics-samples") && (numParams != 1)) wrongBody = true;
1697   if((opType == "node") && (numParams > 1)) wrongBody = true;
1698
1699   if((opType == "node_auto") && (numParams > 0)) wrongBody = true;
1700
1701   if(((opType == "code") || (opType == "decode")) && (numParams != 2)) wrongBody = true;
1702
1703   if(((opType == "sendxml2e") || (opType == "sendhex2e")) && (numParams != 1)) wrongBody = true;
1704
1705   if((opType == "burst") && (numParams < 1)) wrongBody = true;
1706
1707   if((opType == "test") && (numParams < 1)) wrongBody = true;
1708
1709   if(((opType == "sendxml2c") || (opType == "sendhex2c") || (opType == "loadxml") || (opType == "diameterServerSessions")) && (numParams != 1)) wrongBody = true;
1710
1711   if(wrongBody) {
1712     // Launch exception
1713     std::string msg = "Wrong body content format on HTTP Request for '";
1714     msg += opType;
1715     msg += "' operation (missing parameter/s). Use 'help' management command to see more information.";
1716     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
1717   }
1718
1719   // Operations:
1720   if(opType == "context") {
1721     std::string contextFile = ((numParams == 1) ? param1 : anna::functions::asString("/var/tmp/anna.context.%05d", getPid()));
1722     writeContext(contextFile);
1723     response_content = anna::functions::asString("Context dumped on file '%s'", contextFile.c_str());
1724     return;
1725   }
1726
1727   if(opType == "log-statistics-samples") {
1728     logStatisticsSamples(param1);
1729     response_content = anna::functions::asString("Log statistics samples for '%s' concepts", param1.c_str());
1730     return;
1731   }
1732
1733   // Change execution directory:
1734   if(opType == "change-dir") {
1735     if (param1 == "") param1 = a_initialWorkingDirectory;
1736     if (chdir(param1.c_str()) == 0)
1737       response_content = "New execution directory configured: ";
1738     else
1739       response_content = "Cannot assign provided execution directory: ";
1740
1741     response_content += param1;
1742     return;
1743   }
1744
1745   if(opType == "services") {
1746     std::string servicesFile = ((numParams == 1) ? param1 : "services.xml");
1747     try {
1748       loadServices(servicesFile, true /* bind entities */);
1749     }
1750     catch(anna::RuntimeException &ex) {
1751       ex.trace();
1752       response_content = anna::functions::asString("Loaded services from file '%s' with errors", servicesFile.c_str());
1753       return;
1754     }
1755     response_content = anna::functions::asString("Loaded services from file '%s'", servicesFile.c_str());
1756     return;
1757   }
1758
1759   // Host switch:
1760   if(opType == "node") {
1761     if (param1 != "") {
1762       if (setWorkingNode(param1)) response_content = anna::functions::asString("Forced node is now '%s'", param1.c_str());
1763     }
1764     else {
1765       if (a_workingNode) {
1766         response_content = "Working node is forced to be: \n\n";
1767         response_content += a_workingNode->asXMLString();
1768       }
1769       else {
1770         response_content = "Working node is automatic";
1771       }
1772     }
1773     return;
1774   }
1775   if(opType == "node_auto") {
1776     a_workingNode = NULL;
1777     response_content = "Working node has been set to automatic";
1778     return;
1779   }
1780
1781   // Operated host from possible forced-working node:
1782   a_operatedHost = a_workingNode ? a_workingNode /* priority */: NULL /* auto */;
1783   // Use later:
1784   //    If any message is managed: updateOperatedOriginHostWithMessage(codecMessage)
1785   //    To operate, use the exception-protected methods which never will return NULL:
1786   //         getOperatedHost(), getOperatedEntity(), getOperatedServer(), getOperatedEngine()
1787
1788
1789   if(opType == "code") {
1790     codecMsg.loadXML(param1);
1791     std::string hexString = anna::functions::asHexString(codecMsg.code());
1792     // write to outfile
1793     std::ofstream outfile(param2.c_str(), std::ifstream::out);
1794     outfile.write(hexString.c_str(), hexString.size());
1795     outfile.close();
1796   } else if(opType == "decode") {
1797     // Get DataBlock from file with hex content:
1798     if(!getDataBlockFromHexFile(param1, db_aux))
1799       throw anna::RuntimeException("Error reading hex file provided", ANNA_FILE_LOCATION);
1800
1801     // Decode
1802     try { codecMsg.decode(db_aux); } catch(anna::RuntimeException &ex) { ex.trace(); }
1803
1804     std::string xmlString = codecMsg.asXMLString();
1805     // write to outfile
1806     std::ofstream outfile(param2.c_str(), std::ifstream::out);
1807     outfile.write(xmlString.c_str(), xmlString.size());
1808     outfile.close();
1809   } else if((opType == "hide") || (opType == "show") || (opType == "hidden") || (opType == "shown")) {
1810
1811     if(param1 != "") {
1812       if(param2 != "") {
1813         std::string key = param1;
1814         key += "|";
1815         key += param2;
1816
1817         if(opType == "hide") getOperatedEngine()->findClientSession(key)->hide();
1818
1819         if(opType == "show") getOperatedEngine()->findClientSession(key)->show();
1820
1821         if(opType == "hidden") opt_response_content = getOperatedEngine()->findClientSession(key)->hidden() ? "true" : "false";
1822
1823         if(opType == "shown") opt_response_content = getOperatedEngine()->findClientSession(key)->shown() ? "true" : "false";
1824       } else {
1825         std::string address;
1826         int port;
1827         anna::functions::getAddressAndPortFromSocketLiteral(param1, address, port);
1828
1829         if(opType == "hide") getOperatedEngine()->findServer(address, port)->hide();
1830
1831         if(opType == "show") getOperatedEngine()->findServer(address, port)->show();
1832
1833         if(opType == "hidden") opt_response_content = getOperatedEngine()->findServer(address, port)->hidden() ? "true" : "false";
1834
1835         if(opType == "shown") opt_response_content = getOperatedEngine()->findServer(address, port)->shown() ? "true" : "false";
1836       }
1837     } else {
1838       if(opType == "hide") getOperatedEntity()->hide();
1839
1840       if(opType == "show") getOperatedEntity()->show();
1841
1842       if(opType == "hidden") opt_response_content = getOperatedEntity()->hidden() ? "true" : "false";
1843
1844       if(opType == "shown") opt_response_content = getOperatedEntity()->shown() ? "true" : "false";
1845     }
1846   } else if((opType == "sendxml2e") || (opType == "sendhex2e")) {
1847     anna::diameter::comm::Message *msg;
1848
1849     if(opType == "sendxml2e") {
1850       codecMsg.loadXML(param1);
1851       updateOperatedOriginHostWithMessage(codecMsg);
1852       msg = getOperatedHost()->createCommMessage();
1853       msg->clearBody();
1854       try { codecMsg.valid(); } catch(anna::RuntimeException &ex) { ex.trace(); }  // at least we need to see validation errors although it will continue sending (see validation mode configured in launcher)
1855       msg->setBody(codecMsg.code());
1856     } else {
1857       // Get DataBlock from file with hex content:
1858       if(!getDataBlockFromHexFile(param1, db_aux))
1859         throw anna::RuntimeException("Error reading hex file provided", ANNA_FILE_LOCATION);
1860       msg = getOperatedHost()->createCommMessage();
1861       msg->setBody(db_aux);
1862       try { if(getOperatedHost()->logEnabled()) codecMsg.decode(db_aux); } catch(anna::RuntimeException &ex) { ex.trace(); }
1863     }
1864
1865     bool success = getOperatedEntity()->send(msg);
1866     getOperatedHost()->releaseCommMessage(msg);
1867
1868     // Detailed log:
1869     if(getOperatedHost()->logEnabled()) {
1870       anna::diameter::comm::Server *usedServer = getOperatedEntity()->getLastUsedResource();
1871       anna::diameter::comm::ClientSession *usedClientSession = usedServer ? usedServer->getLastUsedResource() : NULL;
1872       std::string detail = usedClientSession ? usedClientSession->asString() : "<null client session>"; // shouldn't happen
1873       getOperatedHost()->writeLogFile(codecMsg, (success ? "sent2e" : "send2eError"), detail);
1874     }
1875   } else if((opType == "burst")) {
1876
1877     if (!uniqueOriginHost())
1878       throw anna::RuntimeException("Burst category operations are only allowed in single host node configuration. This is only to simplify user experience.", ANNA_FILE_LOCATION);
1879
1880     // burst|clear                     clears all loaded burst messages.
1881     // burst|load|<source_file>        loads the next diameter message into launcher burst.
1882     // burst|start|<initial load>      starts the message sending with a certain initial load.
1883     // burst|push|<load amount>        sends specific non-aynchronous load.
1884     // burst|stop                      stops the burst cycle.
1885     // burst|repeat|[[yes]|no]         restarts the burst launch when finish.
1886     // burst|send|<amount>             send messages from burst list. The main difference with
1887     //                                 start/push operations is that burst won't be awaken.
1888     //                                 Externally we could control sending time (no request
1889     //                                 will be sent for answers).
1890     // burst|goto|<order>              Updates current burst pointer position.
1891     // burst|look|<order>              Show programmed burst message for order provided, current when missing.
1892
1893     if(param1 == "clear") {
1894       opt_response_content = "removed ";
1895       opt_response_content += anna::functions::asString(getOperatedHost()->clearBurst());
1896       opt_response_content += " elements";
1897     } else if(param1 == "load") {
1898       if(param2 == "") throw anna::RuntimeException("Missing xml path file for burst load operation", ANNA_FILE_LOCATION);
1899
1900       codecMsg.loadXML(param2);
1901       if(codecMsg.isAnswer()) throw anna::RuntimeException("Cannot load diameter answers for burst feature", ANNA_FILE_LOCATION);
1902       try { codecMsg.valid(); } catch(anna::RuntimeException &ex) { ex.trace(); }  // at least we need to see validation errors although it will continue loading (see validation mode configured in launcher)
1903
1904       int position = getOperatedHost()->loadBurstMessage(codecMsg.code());
1905       opt_response_content = "loaded '";
1906       opt_response_content += param2;
1907       opt_response_content += "' file into burst list position ";
1908       opt_response_content += anna::functions::asString(position);
1909     } else if(param1 == "start") {
1910       if(param2 == "") throw anna::RuntimeException("Missing initial load for burst start operation", ANNA_FILE_LOCATION);
1911
1912       int initialLoad = atoi(param2.c_str());
1913       int processed = getOperatedHost()->startBurst(initialLoad);
1914
1915       if(processed > 0) {
1916         opt_response_content = "initial load completed for ";
1917         opt_response_content += anna::functions::entriesAsString(processed, "message");
1918       }
1919     } else if(param1 == "push") {
1920       if(param2 == "") throw anna::RuntimeException("Missing load amount for burst push operation", ANNA_FILE_LOCATION);
1921
1922       int pushed = getOperatedHost()->pushBurst(atoi(param2.c_str()));
1923
1924       if(pushed > 0) {
1925         opt_response_content = "pushed ";
1926         opt_response_content += anna::functions::entriesAsString(pushed, "message");
1927       }
1928     } else if(param1 == "pop") {
1929       if(param2 == "") throw anna::RuntimeException("Missing amount for burst pop operation", ANNA_FILE_LOCATION);
1930
1931       int releaseLoad = atoi(param2.c_str());
1932       int popped = getOperatedHost()->popBurst(releaseLoad);
1933
1934       if(popped > 0) {
1935         opt_response_content = "burst popped for ";
1936         opt_response_content += anna::functions::entriesAsString(popped, "message");
1937       }
1938     } else if(param1 == "stop") {
1939       int left = getOperatedHost()->stopBurst();
1940
1941       if(left != -1) {
1942         opt_response_content += anna::functions::entriesAsString(left, "message");
1943         opt_response_content += " left to the end of the cycle";
1944       }
1945     } else if(param1 == "repeat") {
1946       if(param2 == "") param2 = "yes";
1947
1948       bool repeat = (param2 == "yes");
1949       getOperatedHost()->repeatBurst(repeat);
1950       opt_response_content += (repeat ? "repeat enabled" : "repeat disabled");
1951     } else if(param1 == "send") {
1952       if(param2 == "") throw anna::RuntimeException("Missing amount for burst send operation", ANNA_FILE_LOCATION);
1953
1954       int sent = getOperatedHost()->sendBurst(atoi(param2.c_str()));
1955
1956       if(sent > 0) {
1957         opt_response_content = "sent ";
1958         opt_response_content += anna::functions::entriesAsString(sent, "message");
1959       }
1960     } else if(param1 == "goto") {
1961       if(param2 == "") throw anna::RuntimeException("Missing order position for burst goto operation", ANNA_FILE_LOCATION);
1962
1963       opt_response_content = getOperatedHost()->gotoBurst(atoi(param2.c_str()));
1964     } else if(param1 == "look") {
1965       int order = ((param2 != "") ? atoi(param2.c_str()) : -1);
1966       opt_response_content = "\n\n";
1967       opt_response_content += getOperatedHost()->lookBurst(order);
1968     } else {
1969       throw anna::RuntimeException("Wrong body content format on HTTP Request for 'burst' operation (unexpected action parameter). See help", ANNA_FILE_LOCATION);
1970     }
1971
1972   } else if((opType == "test")) {
1973     // test|<id>|<command>[|parameters]         Add a new step to the test case ...
1974     // test|ttps|<amount>                       Starts/resume the provided number of time ticks per second (ttps). The ADML starts ...
1975     // test|next[|<sync-amount>]                Forces the execution of the next test case(s) without waiting for test manager tick ...
1976     // test|ip-limit[|amount]                   In-progress limit of test cases. No new test cases will be launched over this value ...
1977     // test|repeats|<amount>                    Restarts the whole programmed test list when finished the amount number of times ...
1978     // test|report|<initialized/in-progress/failed/success/[all]/none>[|[yes]|no]
1979     //                                          Enables/disables report generation for a certain test case state: initialized, in-progress ...
1980     // test|report-hex[|[yes]|no]               Reports could include the diameter messages in hexadecimal format. Disabled by default.
1981     // test|goto|<id>                           Updates current test pointer position.
1982     // test|look[|id]                           Show programmed test case for id provided, current when missing ...
1983     // test|interact|amount|id                  Makes interactive a specific test case id. The amount is the margin of execution steps ...
1984     // test|reset|<[soft]/hard>[|id]            Reset the test case for id provided, all the tests when missing ...
1985     // test|auto-reset|<soft|hard>              When cycling, current test cases can be soft (default) or hard reset ...
1986     // test|clear                               Clears all the programmed test cases.
1987     // test|summary                             Test manager general report (number of test cases, counts by state ...
1988
1989
1990     if(param1 == "ttps") {
1991       if (numParams > 2)
1992         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
1993
1994       bool success = ((param2 != "") ? testManager.configureTTPS(atoi(param2.c_str())) : false);
1995       if (success) {
1996         opt_response_content = "assigned new test launch rate to ";
1997         opt_response_content += anna::functions::asString(atoi(param2.c_str()));
1998         opt_response_content += " events per second";
1999       }
2000       else {
2001         opt_response_content += "unable to configure the test rate provided";
2002       }
2003     }
2004     else if (param1 == "next") {
2005       if (numParams > 2)
2006         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
2007
2008       int sync_amount = ((param2 != "") ? atoi(param2.c_str()) : 1);
2009
2010       if (sync_amount < 1)
2011         throw anna::RuntimeException("The parameter 'sync-amount' must be a positive integer value", ANNA_FILE_LOCATION);
2012
2013       bool success = testManager.execTestCases(sync_amount);
2014
2015       opt_response_content = (success ? "" : "not completely " /* completed cycle and no repeats, rare case */);
2016       opt_response_content += "processed ";
2017       opt_response_content += anna::functions::asString(sync_amount);
2018       opt_response_content += ((sync_amount > 1) ? " test cases synchronously" : " test case");
2019     }
2020     else if(param1 == "ip-limit") {
2021       if (numParams > 2)
2022         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
2023
2024       unsigned int limit;
2025       if (param2 != "") {
2026         limit = atoi(param2.c_str());
2027         testManager.setInProgressLimit(limit);
2028         opt_response_content = "new in-progress limit: ";
2029         opt_response_content += (limit != UINT_MAX) ? anna::functions::asString(limit) : "<no limit>";
2030       }
2031       else {
2032         opt_response_content = "in-progress limit amount: ";
2033         limit = testManager.getInProgressLimit();
2034         opt_response_content += (limit != UINT_MAX) ? anna::functions::asString(limit) : "<no limit>";
2035         opt_response_content += "; currently there are ";
2036         opt_response_content += anna::functions::asString(testManager.getInProgressCount());
2037         opt_response_content += " test cases running";
2038       }
2039     }
2040     else if(param1 == "repeats") {
2041       if (numParams != 2)
2042         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
2043       int repeats = atoi(param2.c_str());
2044       if (repeats < 0) repeats = -1;
2045       testManager.setPoolRepeats(repeats);
2046       std::string nolimit = (repeats != -1) ? "":" [no limit]";
2047       opt_response_content += anna::functions::asString("Pool repeats: %d%s (current cycle: %d)", repeats, nolimit.c_str(), testManager.getPoolCycle());
2048     }
2049     else if(param1 == "report") {
2050       if (numParams > 3)
2051         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
2052
2053       if(param2 == "") param2 = "all";
2054       if(param3 == "") param3 = "yes";
2055       bool enable = (param3 == "yes");
2056
2057       if(param2 == "initialized")
2058         testManager.setDumpInitializedReports(enable);
2059       else if(param2 == "in-progress")
2060         testManager.setDumpInProgressReports(enable);
2061       else if(param2 == "failed")
2062         testManager.setDumpFailedReports(enable);
2063       else if(param2 == "success")
2064         testManager.setDumpSuccessReports(enable);
2065       else if(param2 == "all") {
2066         param2 = "any";
2067         testManager.setDumpAllReports(enable);
2068       }
2069       else if(param2 == "none") {
2070         enable = !enable;
2071         param2 = "any";
2072         testManager.setDumpAllReports(enable);
2073       }
2074       else
2075         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
2076
2077       opt_response_content += (enable ? "report enabled " : "report disabled ");
2078       opt_response_content += "for tests in '";
2079       opt_response_content += param2;
2080       opt_response_content += "' state";
2081     }
2082     else if(param1 == "report-hex") {
2083       if (numParams > 2)
2084         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
2085
2086       if(param2 == "") param2 = "yes";
2087       testManager.setDumpHex((param2 == "yes"));
2088       opt_response_content += (testManager.getDumpHex() ? "report includes hexadecimal messages" : "report excludes hexadecimal messages");
2089     }
2090     else if(param1 == "goto") {
2091       if (numParams > 2)
2092         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
2093
2094       if(param2 == "") throw anna::RuntimeException("Missing id for test goto operation", ANNA_FILE_LOCATION);
2095       int id = atoi(param2.c_str());
2096       if (testManager.gotoTestCase(id)) {
2097         opt_response_content = "position updated for id provided (";
2098       }
2099       else {
2100         opt_response_content = "cannot found test id (";
2101       }
2102       opt_response_content += anna::functions::asString(id);
2103       opt_response_content += ")";
2104     }
2105     else if(param1 == "look") {
2106       if (numParams > 2)
2107         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
2108
2109       int id = ((param2 != "") ? atoi(param2.c_str()) : -1);
2110       anna::testing::TestCase *testCase = testManager.findTestCase(id);
2111
2112       if (testCase) {
2113         response_content = testCase->asXMLString();
2114         return;
2115       }
2116       else {
2117         if (id == -1) {
2118           opt_response_content = "no current test case detected (testing started ?)";
2119         }
2120         else {
2121           opt_response_content = "cannot found test id (";
2122           opt_response_content += anna::functions::asString(id);
2123           opt_response_content += ")";
2124         }
2125       }
2126     }
2127     else if (param1 == "interact") {
2128       if (numParams != 3)
2129         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
2130
2131       int amount = atoi(param2.c_str());
2132       if (amount < -1)
2133         throw anna::RuntimeException("Interactive amount must be -1 (to disable interactive mode) or a positive number.", ANNA_FILE_LOCATION);
2134
2135       int id = atoi(param3.c_str());
2136       anna::testing::TestCase *testCase = testManager.findTestCase(id);
2137       if (testCase) {
2138         if (amount == -1) {
2139           testCase->makeInteractive(false);
2140           opt_response_content = "interactive mode disabled";
2141         }
2142         else {
2143           testCase->addInteractiveAmount(amount);
2144           opt_response_content = "added interactive amount of ";
2145           opt_response_content += anna::functions::asString(amount);
2146           opt_response_content += " units";
2147           if (amount == 0) opt_response_content += " (0: freezing a non-interactive testcase, no effect on already interactive)";
2148         }
2149         opt_response_content += " for test case id ";
2150         opt_response_content += anna::functions::asString(id);
2151       }
2152       else {
2153         opt_response_content = "cannot found test id (";
2154         opt_response_content += anna::functions::asString(id);
2155         opt_response_content += ")";
2156       }
2157     }
2158     else if(param1 == "reset") {
2159       if (numParams > 3)
2160         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
2161
2162       if(param2 == "") param2 = "soft";
2163      if (param2 != "soft" && param2 != "hard")
2164         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
2165
2166       int id = ((param3 != "") ? atoi(param3.c_str()) : -1);
2167       anna::testing::TestCase *testCase = ((id != -1) ? testManager.findTestCase(id) : NULL);
2168
2169       if (testCase) {
2170         bool done = testCase->reset(param2 == "hard");
2171         opt_response_content = "test ";
2172         opt_response_content += param2;
2173         opt_response_content += " reset for id ";
2174         opt_response_content += anna::functions::asString(id);
2175         opt_response_content += done ? ": done": ": not done";
2176       }
2177       else {
2178         if (id == -1) {
2179           bool anyReset = testManager.resetPool(param2 == "hard");
2180           opt_response_content = param2; opt_response_content += " reset have been sent to all programmed tests: "; opt_response_content += anyReset ? "some/all have been reset" : "nothing was reset";
2181         }
2182         else {
2183           opt_response_content = "cannot found test id (";
2184           opt_response_content += anna::functions::asString(id);
2185           opt_response_content += ")";
2186         }
2187       }
2188     }
2189     else if(param1 == "auto-reset") {
2190       if (numParams != 2)
2191         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
2192
2193       if (param2 != "soft" && param2 != "hard")
2194         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
2195
2196       testManager.setAutoResetHard(param2 == "hard");
2197       opt_response_content += anna::functions::asString("Auto-reset configured to '%s'", param2.c_str());
2198     }
2199     else if(param1 == "clear") {
2200       if (numParams > 1)
2201         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
2202
2203       if (testManager.clearPool()) {
2204         opt_response_content = "all the programmed test cases have been dropped";
2205       }
2206       else {
2207         opt_response_content = "there are not programmed test cases to be removed";
2208       }
2209     }
2210     else if(param1 == "summary") {
2211       response_content = testManager.asXMLString();
2212       return;
2213     }
2214     else {
2215       int id = atoi(param1.c_str());
2216       if(id < 0)
2217         throw anna::RuntimeException("Invalid test case identifier: must be a non-negative number", ANNA_FILE_LOCATION);
2218
2219       // PARAM: 1     2            3      4          5           6             7           8          9       10         11
2220       // test|<id>|<command>
2221       //             timeout|    <msecs>
2222       //             sendxml2e|  <file>[|<step number>]
2223       //             sendxml2c|  <file>[|<step number>]
2224       //             delay|      [msecs]
2225       //             wait<fe/fc>|[code]|[bitR]|[hopByHop]|[applicationId]|[sessionId]|[resultCode]|[msisdn]|[imsi]|[serviceContextId]
2226       //      wait<fe/fc>-answer|<step number>
2227       //      wait<fe/fc>-xml   |<source_file>[|strict]
2228       //      wait<fe/fc>-hex   |<source_file>[|strict]
2229       if(param2 == "") throw anna::RuntimeException("Missing command for test id operation", ANNA_FILE_LOCATION);
2230
2231       // Commands:
2232       if (param2 == "timeout") {
2233         if (numParams > 3)
2234           throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
2235         if(param3 == "") throw anna::RuntimeException("Missing milliseconds for 'timeout' command in test id operation", ANNA_FILE_LOCATION);
2236         anna::Millisecond timeout = checkTimeMeasure("Test case timeout", param3);
2237         testManager.getTestCase(id)->addTimeout(timeout); // creates / reuses
2238       }
2239       else if ((param2 == "sendxml2e")||(param2 == "sendxml2c")) {
2240         if (numParams > 4)
2241           throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
2242         if(param3 == "") throw anna::RuntimeException(anna::functions::asString("Missing xml file for '%s' command in test id operation", param2.c_str()), ANNA_FILE_LOCATION);
2243         codecMsg.loadXML(param3);
2244         LOGWARNING(
2245           if (!codecMsg.isRequest() && (param4 == ""))
2246             anna::Logger::warning("Step number has not been provided. Take into account that this answer message will be sent 'as is' and sequence information could be wrong at the remote peer", ANNA_FILE_LOCATION);
2247         );
2248
2249         updateOperatedOriginHostWithMessage(codecMsg);
2250         int stepNumber = ((param4 != "") ? atoi(param4.c_str()):-1);
2251
2252         if (param2 == "sendxml2e")
2253           testManager.getTestCase(id)->addSendxml2e(codecMsg.code(), getOperatedHost(), stepNumber); // creates / reuses
2254         else
2255           testManager.getTestCase(id)->addSendxml2c(codecMsg.code(), getOperatedHost(), stepNumber); // creates / reuses
2256       }
2257       else if (param2 == "delay") {
2258         if (numParams > 3)
2259           throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
2260         if(param3 == "") throw anna::RuntimeException("Missing milliseconds for 'delay' command in test id operation", ANNA_FILE_LOCATION);
2261         anna::Millisecond delay = ((param3 == "0" /* special case */) ? (anna::Millisecond)0 : checkTimeMeasure("Test case delay step", param3));
2262         testManager.getTestCase(id)->addDelay(delay); // creates / reuses
2263       }
2264       else if ((param2 == "waitfe")||(param2 == "waitfc")) {
2265         if (numParams > 11)
2266           throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
2267         if (param3 != "" || param4 != "" || param5 != "" || param6 != "" || param7 != "" || param8 != "" || param9 != "" || param10 != "" || param11 != "") {
2268           bool fromEntity = (param2.substr(4,2) == "fe");
2269           testManager.getTestCase(id)->addWait(fromEntity, param3, param4, param5, param6, param7, param8, param9, param10, param11);
2270         }
2271         else {
2272           throw anna::RuntimeException(anna::functions::asString("Missing condition for '%s' command in test id operation", param2.c_str()), ANNA_FILE_LOCATION);
2273         }
2274       }
2275       else if ((param2 == "waitfe-hex")||(param2 == "waitfc-hex")) {
2276         if (numParams > 4)
2277           throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
2278         if(param3 == "") throw anna::RuntimeException(anna::functions::asString("Missing hex file for '%s' command in test id operation", param2.c_str()), ANNA_FILE_LOCATION);
2279
2280         // Get DataBlock from file with hex content:
2281         if(!getDataBlockFromHexFile(param3, db_aux))
2282           throw anna::RuntimeException("Error reading hex content from file provided", ANNA_FILE_LOCATION);
2283
2284         // Hexadecimal representation read from file:
2285         std::string regexp = anna::functions::asHexString(db_aux);
2286
2287         // optional 'full':
2288         if(param4 != "strict") {
2289           //// If request, we will ignore sequence data:
2290           //if (anna::diameter::codec::functions::requestBit(db_aux))
2291             regexp.replace (24, 16, "[0-9A-Fa-f]{16}");
2292
2293           regexp.insert(0, "^");
2294           regexp += "$";
2295         }
2296
2297         bool fromEntity = (param2.substr(4,2) == "fe");
2298         testManager.getTestCase(id)->addWaitRegexpHex(fromEntity, regexp);
2299       }
2300       else if ((param2 == "waitfe-xml")||(param2 == "waitfc-xml")) {
2301         if (numParams > 4)
2302           throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
2303         if(param3 == "") throw anna::RuntimeException(anna::functions::asString("Missing xml file for '%s' command in test id operation", param2.c_str()), ANNA_FILE_LOCATION);
2304
2305         // Get xml content from file:
2306         std::string regexp;
2307         if(!getContentFromFile(param3, regexp))
2308           throw anna::RuntimeException("Error reading xml content from file provided", ANNA_FILE_LOCATION);
2309
2310         // optional 'full':
2311         if(param4 != "strict") {
2312
2313           // TODO: use this from gcc4.9.0: http://stackoverflow.com/questions/8060025/is-this-c11-regex-error-me-or-the-compiler
2314 /*
2315           std::string s_from = "hop-by-hop-id=\"[0-9]+\" end-to-end-id=\"[0-9]+\"";
2316           std::string s_to = s_from;
2317           std::string s_from2 = "avp name=\"Origin-State-Id\" data=\"[0-9]+\"";
2318           std::string s_to2 = s_from2;
2319
2320           try {
2321             regexp = std::regex_replace (regexp, std::regex(s_from), s_to);
2322             regexp = std::regex_replace (regexp, std::regex(s_from2), s_to2);
2323           }
2324           catch (const std::regex_error& e) {
2325             throw anna::RuntimeException(e.what(), ANNA_FILE_LOCATION);
2326           }
2327
2328 */
2329           std::string::size_type pos, pos_1, pos_2;
2330
2331           pos = regexp.find("hop-by-hop-id=", 0u);
2332           pos = regexp.find("\"", pos);
2333           pos_1 = pos;
2334           pos = regexp.find("\"", pos+1);
2335           pos_2 = pos;
2336           regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
2337
2338           pos = regexp.find("end-to-end-id=", 0u);
2339           pos = regexp.find("\"", pos);
2340           pos_1 = pos;
2341           pos = regexp.find("\"", pos+1);
2342           pos_2 = pos;
2343           regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
2344
2345           pos = regexp.find("Origin-State-Id", 0u);
2346           pos = regexp.find("\"", pos);
2347           pos = regexp.find("\"", pos+1);
2348           pos_1 = pos;
2349           pos = regexp.find("\"", pos+1);
2350           pos_2 = pos;
2351           regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
2352
2353           //regexp.insert(0, "^");
2354           //regexp += "$";
2355         }
2356
2357         bool fromEntity = (param2.substr(4,2) == "fe");
2358         testManager.getTestCase(id)->addWaitRegexpXml(fromEntity, regexp);
2359       }
2360       else if (param2 == "sh-command") {
2361         // Allow pipes in command:
2362         //if (numParams > 4)
2363         //  throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
2364         if(param3 == "") throw anna::RuntimeException("Missing script/executable command-line for 'sh-command' in test id operation", ANNA_FILE_LOCATION);
2365         std::string token = "|sh-command|";
2366         std::string command = operation.substr(operation.find(token) + token.size());
2367         testManager.getTestCase(id)->addCommand(command); // creates / reuses
2368       }
2369       else {
2370         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
2371       }
2372     }
2373
2374   } else if((opType == "sendxml2c") || (opType == "sendhex2c")) {
2375     anna::diameter::comm::Message *msg;
2376
2377     if(opType == "sendxml2c") {
2378       codecMsg.loadXML(param1);
2379       updateOperatedOriginHostWithMessage(codecMsg);
2380       msg = getOperatedHost()->createCommMessage();
2381       msg->clearBody();
2382       try { codecMsg.valid(); } catch(anna::RuntimeException &ex) { ex.trace(); }  // at least we need to see validation errors although it will continue sending (see validation mode configured in launcher)
2383
2384       msg->setBody(codecMsg.code());
2385     } else {
2386       // Get DataBlock from file with hex content:
2387       if(!getDataBlockFromHexFile(param1, db_aux))
2388         throw anna::RuntimeException("Error reading hex file provided", ANNA_FILE_LOCATION);
2389       msg = getOperatedHost()->createCommMessage();
2390       msg->setBody(db_aux);
2391     }
2392
2393     bool success = getOperatedServer()->send(msg);
2394     getOperatedHost()->releaseCommMessage(msg);
2395
2396     // Detailed log:
2397     if(getOperatedHost()->logEnabled()) {
2398       anna::diameter::comm::ServerSession *usedServerSession = getOperatedServer()->getLastUsedResource();
2399       std::string detail = usedServerSession ? usedServerSession->asString() : "<null server session>"; // shouldn't happen
2400       getOperatedHost()->writeLogFile(codecMsg, (success ? "sent2c" : "send2cError"), detail);
2401     }
2402   } else if(opType == "loadxml") {
2403     codecMsg.loadXML(param1);
2404     response_content = codecMsg.asXMLString();
2405     return;
2406   } else if(opType == "diameterServerSessions") {
2407     int diameterServerSessions = atoi(param1.c_str());
2408     getOperatedServer()->setMaxConnections(diameterServerSessions);
2409
2410   } else if(opType == "answerxml2c") {
2411     if(param1 == "") { // programmed answers FIFO's to stdout
2412       response_content = getOperatedServer()->getReactingAnswers()->asString("ANSWERS TO CLIENT");
2413       return;
2414     } else if (param1 == "rotate") {
2415       getOperatedServer()->getReactingAnswers()->rotate(true);
2416     } else if (param1 == "exhaust") {
2417       getOperatedServer()->getReactingAnswers()->rotate(false);
2418     } else if (param1 == "clear") {
2419       getOperatedServer()->getReactingAnswers()->clear();
2420     } else if (param1 == "dump") {
2421       getOperatedServer()->getReactingAnswers()->dump("programmed_answer");
2422     } else {
2423       codecMsg.loadXML(param1);
2424       updateOperatedOriginHostWithMessage(codecMsg);
2425       anna::diameter::codec::Message *message = getOperatedHost()->getCodecEngine()->createMessage(param1); // loads xml again, lesser of two evils
2426       LOGDEBUG(anna::Logger::debug(message->asXMLString(), ANNA_FILE_LOCATION));
2427
2428       if(message->isRequest())
2429         throw anna::RuntimeException("Cannot program diameter requests. Answer type must be provided", ANNA_FILE_LOCATION);
2430
2431       int code = message->getId().first;
2432       LOGDEBUG(anna::Logger::debug("Adding a new programed 'answer to client' to the FIFO queue corresponding to its message code ...", ANNA_FILE_LOCATION));
2433       getOperatedServer()->getReactingAnswers()->addMessage(code, message);
2434     }
2435   } else if(opType == "answerxml2e") {
2436
2437     if(param1 == "") { // programmed answers FIFO's to stdout
2438       response_content = getOperatedEntity()->getReactingAnswers()->asString("ANSWERS TO ENTITY");
2439       return;
2440     } else if (param1 == "rotate") {
2441       getOperatedEntity()->getReactingAnswers()->rotate(true);
2442     } else if (param1 == "exhaust") {
2443       getOperatedEntity()->getReactingAnswers()->rotate(false);
2444     } else if (param1 == "clear") {
2445       getOperatedEntity()->getReactingAnswers()->clear();
2446     } else if (param1 == "dump") {
2447       getOperatedEntity()->getReactingAnswers()->dump("programmed_answer");
2448     } else {
2449       codecMsg.loadXML(param1);
2450       updateOperatedOriginHostWithMessage(codecMsg);
2451       anna::diameter::codec::Message *message = getOperatedHost()->getCodecEngine()->createMessage(param1); // loads xml again, lesser of two evils
2452       LOGDEBUG(anna::Logger::debug(message->asXMLString(), ANNA_FILE_LOCATION));
2453
2454       if(message->isRequest())
2455         throw anna::RuntimeException("Cannot program diameter requests. Answer type must be provided", ANNA_FILE_LOCATION);
2456
2457       int code = message->getId().first;
2458       LOGDEBUG(anna::Logger::debug("Adding a new programed 'answer to entity' to the FIFO queue corresponding to its message code ...", ANNA_FILE_LOCATION));
2459       getOperatedEntity()->getReactingAnswers()->addMessage(code, message);
2460     }
2461   } else {
2462     throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
2463   }
2464
2465   // HTTP response
2466   response_content = "Operation correctly processed: "; response_content += operation;
2467   if (opt_response_content != "") {
2468     response_content += " => ";
2469     response_content += opt_response_content;
2470   }
2471 }
2472
2473 anna::xml::Node* Launcher::asXML(anna::xml::Node* parent) const
2474 throw() {
2475   anna::xml::Node* result = parent->createChild("launcher");
2476   anna::comm::Application::asXML(result);
2477   // Timming:
2478   result->createAttribute("StartTime", a_start_time.asString());
2479   result->createAttribute("InitialWorkingDirectory", a_initialWorkingDirectory);
2480   result->createAttribute("SecondsLifeTime", anna::time::functions::lapsedMilliseconds() / 1000);
2481   // Diameter:
2482   anna::diameter::comm::OriginHostManager &ohm = anna::diameter::comm::OriginHostManager::instantiate();
2483   for (diameter::comm::origin_hosts_it it = ohm.begin(); it != ohm.end(); it++) {
2484     it->second->asXML(result);
2485   }
2486
2487   // Registered codec engines:
2488   anna::diameter::codec::EngineManager &em = anna::diameter::codec::EngineManager::instantiate();
2489   em.asXML(result);
2490
2491   // OAM & statistics:
2492   oamAsXML(result);
2493   statsAsXML(result);
2494
2495   // Testing: could be heavy if test case reports are enabled
2496   anna::testing::TestManager::instantiate().asXML(result);
2497
2498   return result;
2499 }
2500
2501 anna::xml::Node* Launcher::oamAsXML(anna::xml::Node* parent) const
2502 throw() {
2503   anna::xml::Node* result = parent->createChild("Oam");
2504
2505   // OAM:
2506   anna::diameter::comm::OamModule::instantiate().asXML(result);
2507   anna::diameter::comm::ApplicationMessageOamModule::instantiate().asXML(result);
2508   anna::diameter::codec::OamModule::instantiate().asXML(result);
2509
2510   return result;
2511 }
2512
2513 anna::xml::Node* Launcher::statsAsXML(anna::xml::Node* parent) const
2514 throw() {
2515   anna::xml::Node* result = parent->createChild("Statistics");
2516
2517   // Statistics:
2518   anna::statistics::Engine::instantiate().asXML(result);
2519
2520   return result;
2521 }