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