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