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