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