60a9cb0cfb7a0fec3dcc13f668d50713fc9af26c
[anna.git] / example / diameter / launcher / Launcher.cpp
1 // ANNA - Anna is Not Nothingness Anymore                                                         //
2 //                                                                                                //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
4 //                                                                                                //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite                           //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
7
8
9 // Standard
10 #include <sstream>      // std::istringstream
11 #include <iostream>     // std::cout
12 #include <math.h> // ceil
13 #include <climits>
14 #include <unistd.h> // chdir
15 //#include <regex> TODO: use this from gcc4.9.0: http://stackoverflow.com/questions/8060025/is-this-c11-regex-error-me-or-the-compiler
16 #include <stdio.h>
17
18 // Project
19 #include <anna/timex/Engine.hpp>
20 #include <anna/statistics/Engine.hpp>
21 #include <anna/diameter/codec/functions.hpp>
22 #include <anna/diameter/codec/Engine.hpp>
23 #include <anna/diameter/codec/EngineManager.hpp>
24 #include <anna/http/Transport.hpp>
25 #include <anna/diameter/stack/Engine.hpp>
26 #include <anna/diameter/helpers/base/functions.hpp>
27 #include <anna/time/functions.hpp>
28 #include <anna/diameter.comm/ApplicationMessageOamModule.hpp>
29 #include <anna/testing/defines.hpp>
30 #include <anna/xml/xml.hpp>
31 #include <anna/diameter.comm/OriginHost.hpp>
32 #include <anna/diameter.comm/OriginHostManager.hpp>
33
34 // Process
35 #include <Launcher.hpp>
36 #include <Procedure.hpp>
37 #include <EventOperation.hpp>
38 #include <MyDiameterEngine.hpp>
39 #include <anna/testing/TestManager.hpp>
40 #include <anna/testing/TestCase.hpp>
41
42
43 #define SIGUSR2_TASKS_INPUT_FILENAME "sigusr2.in"
44 #define SIGUSR2_TASKS_OUTPUT_FILENAME "sigusr2.out"
45
46
47 const char *ServicesDTD = "\
48 <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
49 <!-- Diameter services DTD -->\n\
50 \n\
51 <!ELEMENT services (stack*, node*)>\n\
52 \n\
53 <!ELEMENT stack EMPTY>\n\
54 <!ATTLIST stack id CDATA #REQUIRED dictionary CDATA #REQUIRED validationMode (BeforeEncoding | AfterDecoding | Always | Never) #IMPLIED validationDepth (Complete | FirstError) #IMPLIED fixMode (BeforeEncoding | AfterDecoding | Always | Never) #IMPLIED ignoreFlagsOnValidation (yes | no) #IMPLIED>\n\
55 <!--\n\
56    Stack record\n\
57 \n\
58     id:                      Normally the id corresponds to the Application-Id for which the dictionary provided is designed\n\
59                              (in multistack applications, it shall be mandatory respect such association to know the stack used\n\
60                              for processed messages).\n\
61     dictionary:              Path to the dictionary file.\n\
62     validationMode:          Sets the validation mode. Default is 'AfterDecoding'.\n\
63     validationDepth:         Sets the validation depth. Default is 'FirstError'.\n\
64     fixMode:                 Sets the fix mode. Default is 'BeforeEncoding'.\n\
65     ignoreFlagsOnValidation: Ignore flags during message validation. Default is 'no'.\n\
66 -->\n\
67 \n\
68 <!ELEMENT node EMPTY>\n\
69 <!ATTLIST node originHost CDATA #REQUIRED applicationId CDATA #REQUIRED originRealm CDATA #IMPLIED cer CDATA #IMPLIED dwr CDATA #IMPLIED cea CDATA #IMPLIED allowedInactivityTime CDATA #IMPLIED tcpConnectDelay CDATA #IMPLIED answersTimeout CDATA #IMPLIED ceaTimeout CDATA #IMPLIED watchdogPeriod CDATA #IMPLIED entity CDATA #IMPLIED entityServerSessions CDATA #IMPLIED diameterServer CDATA #IMPLIED diameterServerSessions CDATA #IMPLIED balance (yes | no) #IMPLIED sessionBasedModelsClientSocketSelection (SessionIdLowPart | SessionIdHighPart | SessionIdOptionalPart | RoundRobin) #IMPLIED retries CDATA #IMPLIED log CDATA #IMPLIED splitLog (yes | no) #IMPLIED detailedLog (yes | no) #IMPLIED dumpLog (yes | no) #IMPLIED burstLog (yes | no) #IMPLIED>\n\
70 <!--\n\
71    Node record\n\
72 \n\
73    originHost:                              Node identifier as diameter application host name.\n\
74    applicationId:                           The Application-Id provided must exists as a registered 'stack id'.\n\
75    originRealm:                             Origin-Realm (domain-name internally used by default).\n\
76 \n\
77 \n\
78    - Diameter clients:\n\
79 \n\
80    entity:                                  Target diameter entity (comma-separated '<address>:<port>' format).\n\
81                                             For example: 10.20.30.40:3868,10.20.30.41:3868. If missing, no entity will be enabled.\n\
82    entityServerSessions:                    Diameter entity server sessions (0: diameter entity disabled). Default value of 1.\n\
83    cer:                                     User defined CER path file to be encoded to establish diameter connections.\n\
84                                             If missing, will be harcoded.\n\
85    dwr:                                     User defined DWR path file to be encoded for diameter protocol keep alive.\n\
86                                             If missing, will be harcoded.\n\
87    tcpConnectDelay:                         Milliseconds to wait TCP connect to any server. If missing, default value of 200 will\n\
88                                             be assigned\n\
89    ceaTimeout:                              Milliseconds to wait CEA from diameter server. If missing, default value of 'answersTimeout'\n\
90                                             will be assigned.\n\
91    watchdogPeriod:                          Milliseconds for watchdog timer (Tw) for diameter keep-alive procedure. If missing, default\n\
92                                             value of 30000 will be assigned.\n\
93    balance:                                 Balance over entity servers instead of doing standard behaviour (first primary, secondary\n\
94                                             if fails, etc.). Default value 'no'.\n\
95    sessionBasedModelsClientSocketSelection: By default, round-robin will be applied for IEC model (SMS/MMS), and Session-Id Low Part\n\
96                                             will be analyzed for ECUR/SCUR model (data, voice and content). You could change ECUR/SCUR\n\
97                                             analysis behaviour providing 'SessionIdHighPart', 'SessionIdOptionalPart' (atoi applied;\n\
98                                             usually subscriber id data, i.e. MSISDN or IMSI) and 'RoundRobin' (also 'SessionIdLowPart').\n\
99 \n\
100 \n\
101    - Diameter servers:\n\
102 \n\
103    diameterServer:                          Diameter own server address in '<address>:<port>' format. For example: 10.20.30.40:3868.\n\
104                                             If missing, no local server will be enabled.\n\
105    diameterServerSessions:                  Diameter own server available connections (0: diameter server disabled). Default value of 1.\n\
106                                             Negative value implies no limit accepting incoming connections.\n\
107    cea:                                     User defined CEA path file to be encoded to answer client CERs.\n\
108                                             If missing, will be harcoded.\n\
109    allowedInactivityTime:                   Milliseconds for the maximum allowed inactivity time on server sessions born over the\n\
110                                             local server before being reset. If missing, default value of 90000 will be assigned.\n\
111 \n\
112 \n\
113    - General:\n\
114 \n\
115    answersTimeout:                          Milliseconds to wait pending application answers from diameter peers. If missing,\n\
116                                             default value of 10000 will be assigned.\n\
117    retries:                                 Expired responses will cause a number of request retransmissions. Disabled by default (0 retries).\n\
118    log:                                     Process log file (operations result, traffic log, etc.). By default '<originHost>.launcher.log'.\n\
119                                             Empty string or \"null\" name, to disable. Warning: there is no rotation for log files\n\
120                                             (use logrotate or whatever you consider).\n\
121    splitLog:                                Splits log file (appends to log filename, extensions with the type of event.\n\
122                                             (Check 'HELP.md' for more information). No log files for code/decode and load operations are created.\n\
123                                             Default value 'no'.\n\
124    detailedLog:                             Insert detailed information at log files (timestamps, communication resources, etc.). Useful\n\
125                                             to analyze the messages flow along the sockets (specially on 'balance' mode). Default 'no'.\n\
126    dumpLog:                                 Write to disk every incoming/outcoming message named as:\n\
127                                                '<unix ms timestamp>.<originHost>.<hop by hop>.<end to end>.<message code>.<request|answer>.<type of event>.xml'\n\
128                                             Default value 'no'.\n\
129    burstLog:                                Burst operations log file. By default '<originHost>.launcher.burst'. Empty string or \"null\" name, to disable.\n\
130                                             Warning: there is no rotation for log files (use logrotate or whatever). Output: dot (.) for each\n\
131                                             burst message sent/pushed, cross (x) for popped ones, and order number when multiple of 1% of burst\n\
132                                             list size, plus OTA requests when changed.\n\
133 \n\
134 -->\n\
135 \n\
136 ";
137
138
139 Launcher::Launcher() : anna::comm::Application("launcher", "DiameterLauncher", "1.1"), a_communicator(NULL) {
140   a_timeEngine = NULL;
141   a_counterRecorder = NULL;
142   a_admlMinResolution = 2 * anna::timex::Engine::minResolution; // 2*10 = 20 ms; 1000/20 = 50 ticks per second;
143   //a_admlMinResolution = (anna::Millisecond)100;
144   a_counterRecorderClock = NULL;
145
146   a_workingNode = NULL;
147
148   a_httpServerSocket = NULL;
149 }
150
151
152 std::string Launcher::getSignalUSR2InputFile() const throw() {
153   return (a_initialWorkingDirectory + "/" + SIGUSR2_TASKS_INPUT_FILENAME);
154 }
155
156 std::string Launcher::getSignalUSR2OutputFile() const throw() {
157   return (a_initialWorkingDirectory + "/" + SIGUSR2_TASKS_OUTPUT_FILENAME);
158 }
159
160
161 void Launcher::servicesFromXML(const anna::xml::Node* servicesNode, bool eventOperation) throw(anna::RuntimeException) {
162
163   CommandLine& cl(anna::CommandLine::instantiate());
164   bool allLogsDisabled = cl.exists("disableLogs");
165
166     //<!ATTLIST stack id CDATA #REQUIRED dictionary CDATA #REQUIRED>
167   const anna::xml::Attribute *id, *dictionary;
168
169   // <!ATTLIST node originHost CDATA #REQUIRED applicationId CDATA #REQUIRED cer CDATA #IMPLIED dwr CDATA #IMPLIED allowedInactivityTime CDATA #IMPLIED tcpConnectDelay CDATA #IMPLIED answersTimeout CDATA #IMPLIED ceaTimeout CDATA #IMPLIED watchdogPeriod CDATA #IMPLIED entity CDATA #IMPLIED entityServerSessions CDATA #IMPLIED diameterServer CDATA #IMPLIED diameterServerSessions CDATA #IMPLIED balance (yes | no) #IMPLIED sessionBasedModelsClientSocketSelection (SessionIdLowPart | SessionIdHighPart | SessionIdOptionalPart | RoundRobin) #IMPLIED retries CDATA #IMPLIED log CDATA #IMPLIED splitLog (yes | no) #IMPLIED detailedLog (yes | no) #IMPLIED dumpLog (yes | no) #IMPLIED burstLog (yes | no) #IMPLIED>
170   const anna::xml::Attribute *originHost, *appId, *originRealm, *cer, *dwr, *cea, *allowedInactivityTime, *tcpConnectDelay,
171   *answersTimeout, *ceaTimeout, *watchdogPeriod, *entity, *entityServerSessions,
172   *diameterServer, *diameterServerSessions, *balance, *sessionBasedModelsClientSocketSelection,
173   *retries, *log, *splitLog, *detailedLog, *dumpLog, *burstLog;
174   // Never clear services content from here (append new data from xml). At the moment no node removing is implemented in this process
175
176   // Stacks
177   anna::diameter::stack::Engine &stackEngine = anna::diameter::stack::Engine::instantiate();
178   anna::diameter::stack::Dictionary *d;
179   const anna::diameter::stack::Dictionary *bpd = NULL; // base protocol dictionary
180
181   // Codec engine manager:
182   anna::diameter::codec::EngineManager &em = anna::diameter::codec::EngineManager::instantiate();
183   anna::diameter::codec::Engine *ce;
184
185   ///////////////////////////////////////////
186   // APPLICATION MESSAGE OAM MODULE SCOPES //
187   ///////////////////////////////////////////
188   // We will register a scope per stack id registered. The counters will be dynamically registered at count method.
189   anna::diameter::comm::ApplicationMessageOamModule & appMsgOamModule = anna::diameter::comm::ApplicationMessageOamModule::instantiate();
190   appMsgOamModule.enableCounters(); // this special module is disabled by default (the only)
191   static int scope_id = 3;
192   unsigned int id_value;
193   std::string codecEngineName;
194
195   for(anna::xml::Node::const_child_iterator it = servicesNode->child_begin(); it != servicesNode->child_end(); it++) {
196     std::string nodeName = (*it)->getName();
197
198     if(nodeName == "stack") {
199       // Input data:
200       id = (*it)->getAttribute("id");
201       dictionary = (*it)->getAttribute("dictionary");
202       id_value = id->getIntegerValue();
203
204       if (stackEngine.getDictionary(id_value)) { // Ignore (but don't fail) dictionary load with same stack id already registered
205         LOGWARNING(anna::Logger::warning(anna::functions::asString("Ignore dictionary load for stack id already registered: %llu", id_value), ANNA_FILE_LOCATION));
206         continue;
207       }
208
209       try {
210         d = stackEngine.createDictionary(id_value, dictionary->getValue());
211         LOGDEBUG(anna::Logger::debug(anna::functions::asString("Created dictionary (%p) for stack id %llu", d, id_value), ANNA_FILE_LOCATION));
212
213         // OAM module for counters:
214         appMsgOamModule.createStackCounterScope(scope_id, id_value /* application-id */);
215         scope_id++;
216
217       } catch(anna::RuntimeException &ex) {
218         //_exit(ex.asString());
219         throw ex;
220       }
221
222       bpd = d; // base protocol dictionary in case of monostack. If multistack, will be calculated later
223
224       // Create codec engine and register it in the codec engine manager:
225       codecEngineName = anna::functions::asString("CodecEngineForStackId_%llu", id_value);
226       ce = new anna::diameter::codec::Engine(codecEngineName.c_str(), d);
227       em.registerCodecEngine(id_value, ce);
228
229       // Codec engine configuration:
230       const anna::xml::Attribute *vm_attr = (*it)->getAttribute("validationMode", false /* no exception */);
231       const anna::xml::Attribute *vd_attr = (*it)->getAttribute("validationDepth", false /* no exception */);
232       const anna::xml::Attribute *fm_attr = (*it)->getAttribute("fixMode", false /* no exception */);
233       const anna::xml::Attribute *if_attr = (*it)->getAttribute("ignoreFlagsOnValidation", false /* no exception */);
234
235       std::string vm_value = vm_attr ? vm_attr->getValue() : "AfterDecoding";
236       std::string vd_value = vd_attr ? vd_attr->getValue() : "FirstError";
237       std::string fm_value = fm_attr ? fm_attr->getValue() : "BeforeEncoding";
238
239       anna::diameter::codec::Engine::ValidationMode::_v vm;
240       if (vm_value == "BeforeEncoding") vm = anna::diameter::codec::Engine::ValidationMode::BeforeEncoding;
241       else if (vm_value == "AfterDecoding") vm = anna::diameter::codec::Engine::ValidationMode::AfterDecoding;
242       else if (vm_value == "Always") vm = anna::diameter::codec::Engine::ValidationMode::Always;
243       else if (vm_value == "Never") vm = anna::diameter::codec::Engine::ValidationMode::Never;
244       ce->setValidationMode(vm);
245
246       anna::diameter::codec::Engine::ValidationDepth::_v vd;
247       if (vd_value == "Complete") vd = anna::diameter::codec::Engine::ValidationDepth::Complete;
248       else if (vd_value == "FirstError") vd = anna::diameter::codec::Engine::ValidationDepth::FirstError;
249       ce->setValidationDepth(vd);
250
251       anna::diameter::codec::Engine::FixMode::_v fm;
252       if (fm_value == "BeforeEncoding") fm = anna::diameter::codec::Engine::FixMode::BeforeEncoding;
253       else if (fm_value == "AfterDecoding") fm = anna::diameter::codec::Engine::FixMode::AfterDecoding;
254       else if (fm_value == "Always") fm = anna::diameter::codec::Engine::FixMode::Always;
255       else if (fm_value == "Never") fm = anna::diameter::codec::Engine::FixMode::Never;
256       ce->setFixMode(fm);
257
258       bool if_value = (if_attr ? (if_attr->getValue() == "yes") : false);
259       ce->ignoreFlagsOnValidation(if_value);
260     }
261   }
262
263   // Show loaded stacks:
264   std::cout << "Stacks currently loaded:" << std::endl;
265   std::cout << anna::functions::tab(stackEngine.asString(false /* light */)) << std::endl;
266
267   // Basic checking for multistack:
268   bool multistack = (stackEngine.stack_size() > 1);
269   if (multistack) {
270     bpd = stackEngine.getDictionary(0);
271     if(!bpd)
272       throw anna::RuntimeException("In multistack applications is mandatory register a stack id = 0 using a dictionary which contains the needed elements to build base protocol messages (CER/A, DWR/A, DPR/A, STR/A, etc.)", ANNA_FILE_LOCATION);
273   }
274
275   // REALMS:
276   for(anna::xml::Node::const_child_iterator it = servicesNode->child_begin(); it != servicesNode->child_end(); it++) {
277     std::string nodeName = (*it)->getName();
278
279     if(nodeName == "node") {
280       // Input data:
281       originHost = (*it)->getAttribute("originHost");
282       appId = (*it)->getAttribute("applicationId");
283       originRealm = (*it)->getAttribute("originRealm", false /* no exception */);
284       cer = (*it)->getAttribute("cer", false /* no exception */);
285       dwr = (*it)->getAttribute("dwr", false /* no exception */);
286       cea = (*it)->getAttribute("cea", false /* no exception */);
287       allowedInactivityTime = (*it)->getAttribute("allowedInactivityTime", false /* no exception */);
288       tcpConnectDelay = (*it)->getAttribute("tcpConnectDelay", false /* no exception */);
289       answersTimeout = (*it)->getAttribute("answersTimeout", false /* no exception */);
290       ceaTimeout = (*it)->getAttribute("ceaTimeout", false /* no exception */);
291       watchdogPeriod = (*it)->getAttribute("watchdogPeriod", false /* no exception */);
292       entity = (*it)->getAttribute("entity", false /* no exception */);
293       entityServerSessions = (*it)->getAttribute("entityServerSessions", false /* no exception */);
294       diameterServer = (*it)->getAttribute("diameterServer", false /* no exception */);
295       diameterServerSessions = (*it)->getAttribute("diameterServerSessions", false /* no exception */);
296       balance = (*it)->getAttribute("balance", false /* no exception */); // (yes | no)
297       sessionBasedModelsClientSocketSelection = (*it)->getAttribute("sessionBasedModelsClientSocketSelection", false /* no exception */); // (SessionIdHighPart | SessionIdOptionalPart | RoundRobin)
298       retries = (*it)->getAttribute("retries", false /* no exception */);
299       log = (*it)->getAttribute("log", false /* no exception */);
300       splitLog = (*it)->getAttribute("splitLog", false /* no exception */); // (yes | no)
301       detailedLog = (*it)->getAttribute("detailedLog", false /* no exception */); // (yes | no)
302       dumpLog = (*it)->getAttribute("dumpLog", false /* no exception */); // (yes | no)
303       burstLog = (*it)->getAttribute("burstLog", false /* no exception */); // (yes | no)
304
305       // Basic checkings:
306       anna::diameter::comm::OriginHostManager &ohm = anna::diameter::comm::OriginHostManager::instantiate();
307       anna::diameter::comm::OriginHost *oh = ohm.getOriginHost(originHost->getValue());
308       if (oh) {
309         std::string msg = "Already registered such Origin-Host: "; msg += originHost->getValue();
310         throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
311       }
312
313       unsigned int applicationId = appId->getIntegerValue();
314       if (!stackEngine.getDictionary(applicationId)) {
315         std::string msg = "Cannot found a registered stack id with the value of applicationId provided: "; msg += appId->getValue();
316         throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
317       }
318
319       // Engine time measures checking & assignment:
320       anna::Millisecond allowedInactivityTimeMs(90000);
321       anna::Millisecond tcpConnectDelayMs(200);
322       anna::Millisecond answersTimeoutMs(10000);
323       anna::Millisecond ceaTimeoutMs(10000);
324       anna::Millisecond watchdogPeriodMs(30000);
325
326       if (allowedInactivityTime) allowedInactivityTimeMs = checkTimeMeasure("allowedInactivityTime", allowedInactivityTime->getValue());
327       if (tcpConnectDelay)       tcpConnectDelayMs =       checkTimeMeasure("tcpConnectDelay",       tcpConnectDelay->getValue());
328       if (answersTimeout)        answersTimeoutMs =        checkTimeMeasure("answersTimeout",        answersTimeout->getValue());
329       if (ceaTimeout)            ceaTimeoutMs =            checkTimeMeasure("ceaTimeout",            ceaTimeout->getValue());
330       if (watchdogPeriod)        watchdogPeriodMs =        checkTimeMeasure("watchdogPeriod",        watchdogPeriod->getValue());
331
332       // Checking command line parameters
333       std::string sessionBasedModelsType;
334       anna::diameter::comm::Entity::SessionBasedModelsType::_v sessionBasedModelsTypeEnum = anna::diameter::comm::Entity::SessionBasedModelsType::SessionIdLowPart;
335       if(sessionBasedModelsClientSocketSelection) {
336         sessionBasedModelsType = sessionBasedModelsClientSocketSelection->getValue();
337         if (sessionBasedModelsType == "RoundRobin") {
338           sessionBasedModelsTypeEnum = anna::diameter::comm::Entity::SessionBasedModelsType::RoundRobin;
339         }
340         else if (sessionBasedModelsType == "SessionIdOptionalPart") {
341           sessionBasedModelsTypeEnum = anna::diameter::comm::Entity::SessionBasedModelsType::SessionIdOptionalPart;
342         }
343         else if (sessionBasedModelsType == "SessionIdHighPart") {
344           sessionBasedModelsTypeEnum = anna::diameter::comm::Entity::SessionBasedModelsType::SessionIdHighPart;
345         }
346         else if (sessionBasedModelsType == "SessionIdLowPart") {
347           sessionBasedModelsTypeEnum = anna::diameter::comm::Entity::SessionBasedModelsType::SessionIdLowPart;
348         }
349         else {
350           throw anna::RuntimeException("Parameter 'sessionBasedModelsClientSocketSelection' only accepts 'SessionIdLowPart'/'SessionIdHighPart'/'SessionIdOptionalPart'/'RoundRobin' as parameter values", ANNA_FILE_LOCATION);
351         }
352       }
353
354       int retransmissions = retries ? retries->getIntegerValue() : 0;
355       if(retransmissions < 0) {
356         throw anna::RuntimeException("Parameter 'retries' must be non-negative", ANNA_FILE_LOCATION);
357       }
358
359       /////////////////////////////////////////////////////////////////////////////////////////////
360       // Diameter communication engine:
361       std::string commEngineName = originHost->getValue() + "_DiameterCommEngine";
362       MyDiameterEngine *commEngine = new MyDiameterEngine(commEngineName.c_str(), bpd);
363       commEngine->setAutoBind(false);  // allow to create client-sessions without binding them, in order to set timeouts.
364       commEngine->setMaxConnectionDelay(tcpConnectDelayMs);
365       commEngine->setWatchdogPeriod(watchdogPeriodMs);
366       commEngine->setOriginHostName(originHost->getValue());
367       if (originRealm) commEngine->setOriginRealmName(originRealm->getValue());
368
369       // Origin host node:
370       a_workingNode = new anna::diameter::comm::OriginHost((anna::diameter::comm::Engine*)commEngine, applicationId);
371       a_workingNode->setRequestRetransmissions(retransmissions);
372       /////////////////////////////////////////////////////////////////////////////////////////////
373
374
375       // Diameter entity:
376       if(entity) {
377         int sessions = entityServerSessions ? entityServerSessions->getIntegerValue() : 1;
378
379         if(sessions > 0) {
380           // Number of sessions:
381           commEngine->setNumberOfClientSessionsPerServer(sessions);
382
383           // Client CER and DWR
384           std::string cerPathfile = cer ? cer->getValue() : "";
385           std::string dwrPathfile = dwr ? dwr->getValue() : "";
386           commEngine->setClientCERandDWR(cerPathfile, dwrPathfile);
387
388           // Register one entity for this engine:
389           a_workingNode->createEntity(entity->getValue(), ceaTimeoutMs, answersTimeoutMs);
390           a_workingNode->getEntity()->setSessionBasedModelsType(sessionBasedModelsTypeEnum);
391           a_workingNode->getEntity()->setBalance(balance ? (balance->getValue() == "yes") : false); // for sendings
392           if (eventOperation) a_workingNode->getEntity()->bind();
393         }
394       }
395
396       // Diameter Server:
397       if(diameterServer) {
398         // Server CEA
399         std::string ceaPathfile = cea ? cea->getValue() : "";
400
401         int sessions = diameterServerSessions ? diameterServerSessions->getIntegerValue() : 1;
402         a_workingNode->createDiameterServer(diameterServer->getValue(), sessions, allowedInactivityTimeMs, answersTimeoutMs, ceaPathfile);
403       }
404
405       // Logs:
406       if (!allLogsDisabled) {
407         std::string host = commEngine->getOriginHostName();
408         std::string s_log = host + ".launcher.log"; if (log) s_log = log->getValue();
409         bool b_splitLog = (splitLog ? (splitLog->getValue() == "yes") : false);
410         bool b_detailedLog = (detailedLog ? (detailedLog->getValue() == "yes") : false);
411         bool b_dumpLog = (dumpLog ? (dumpLog->getValue() == "yes") : false);
412         std::string s_burstLog = host + ".launcher.burst"; if (burstLog) s_burstLog = burstLog->getValue();
413         a_workingNode->setLogs(s_log, b_splitLog, b_detailedLog, b_dumpLog, s_burstLog);
414       }
415
416
417       // Lazy initialization for comm engine:
418       if (eventOperation) commEngine->lazyInitialize();
419
420       // Node and Codec Engine registration ///////////////////////////////////////////////////////
421       ohm.registerOriginHost(originHost->getValue(), a_workingNode);
422       /////////////////////////////////////////////////////////////////////////////////////////////
423     }
424   }
425
426   if (!uniqueOriginHost())
427     a_workingNode = NULL; // by default, mode auto
428
429   // Diameter comm engines which are loaded after application start (via management operation 'services') are not really started,
430   //  but this don't care because application startComponents() -> initialize() -> do_initialize() -> do nothing.
431   // And when stopped, running state is not checked and it will be stopped anyway.
432 }
433
434
435 void Launcher::loadServices(const std::string & xmlPathFile, bool eventOperation) throw(anna::RuntimeException) {
436
437   if (xmlPathFile == "null" || xmlPathFile == "") {
438     LOGWARNING(anna::Logger::warning("Ignoring services configuration on start: empty or 'null' string provided as xml file. Use management interface (operation 'services') in order to add services", ANNA_FILE_LOCATION));
439     return;
440   }
441
442   LOGDEBUG(
443       std::string trace = "Loading ADML services file '";
444   trace += xmlPathFile;
445   trace += "'";
446   anna::Logger::debug(trace, ANNA_FILE_LOCATION);
447   );
448   anna::xml::DocumentFile xmlDocument; // has private copy constructor defined but not implemented to avoid inhenrit/copy (is very heavy)
449   anna::xml::DTDMemory xmlDTD;
450   const anna::xml::Node *rootNode;
451   xmlDocument.initialize(xmlPathFile.c_str()); // fail here is i/o error
452   xmlDTD.initialize(ServicesDTD);
453   try {
454     rootNode = xmlDocument.parse(xmlDTD); // Parsing: fail here if xml violates dtd
455   }
456   catch (anna::RuntimeException &ex) {
457     LOGWARNING(
458         std::string msg = "Services DTD schema:\n\n";
459     msg += ServicesDTD;
460     anna::Logger::warning(msg, ANNA_FILE_LOCATION);
461     );
462     throw ex;
463   }
464
465   LOGDEBUG(
466       std::string trace = "Loaded XML file (";
467   trace += xmlPathFile;
468   trace += "):\n";
469   trace += anna::xml::Compiler().apply(rootNode);
470   anna::Logger::debug(trace, ANNA_FILE_LOCATION);
471   );
472   servicesFromXML(rootNode, eventOperation);
473 }
474
475
476 anna::Millisecond Launcher::checkTimeMeasure(const std::string &parameter, const std::string &value) throw(anna::RuntimeException) {
477
478   if(anna::functions::isLike("^[0-9]+$", value)) {  // para incluir numeros decimales: ^[0-9]+(.[0-9]+)?$
479     int msecs;
480     std::istringstream ( value ) >> msecs;
481
482     if(msecs > a_timeEngine->getMaxTimeout()) { // 600000 ms
483       std::string msg = "Configuration value for '";
484       msg += parameter;
485       msg += "' ("; msg += value; msg += " msecs) is greater than allowed max timeout for timming engine: ";
486       msg += anna::functions::asString(a_timeEngine->getMaxTimeout());
487       throw RuntimeException(msg, ANNA_FILE_LOCATION);
488     }
489
490     if(msecs > 300000) {
491       std::string msg = "Configuration value for '";
492       msg += parameter;
493       msg += "' ("; msg += value; msg += " msecs) is perhaps very big (over 5 minutes).";
494       LOGWARNING(anna::Logger::warning(msg, ANNA_FILE_LOCATION));
495     }
496
497     if(msecs <= a_timeEngine->getResolution()) {
498       std::string msg = "Configuration value for '";
499       msg += parameter;
500       msg += "' ("; msg += value; msg += " msecs) as any other time measure, must be greater than timming engine resolution: ";
501       msg += anna::functions::asString(a_timeEngine->getResolution());
502       throw RuntimeException(msg, ANNA_FILE_LOCATION);
503     }
504
505     return (anna::Millisecond)msecs; // ok
506   }
507
508   // Non-integer exception:
509   std::string msg = "Configuration error for '";
510   msg += parameter;
511   msg += "' = '";
512   msg += value;
513   msg += "': must be a non-negative integer number";
514   throw RuntimeException(msg, ANNA_FILE_LOCATION);
515 }
516
517 bool Launcher::setWorkingNode(const std::string &name) throw() {
518   bool result = false;
519
520   anna::diameter::comm::OriginHostManager &ohm = anna::diameter::comm::OriginHostManager::instantiate();
521   anna::diameter::comm::OriginHost *oh = ohm.getOriginHost(name);
522
523   if (oh) {
524     a_workingNode = const_cast<anna::diameter::comm::OriginHost*>(oh);
525     result = true;
526   }
527
528   return result;
529 }
530
531 anna::diameter::comm::OriginHost *Launcher::getOriginHost(const std::string &name) const throw(anna::RuntimeException) {
532   anna::diameter::comm::OriginHostManager &ohm = anna::diameter::comm::OriginHostManager::instantiate();
533   anna::diameter::comm::OriginHost *result = ohm.getOriginHost(name);
534
535   if (!result)
536   throw anna::RuntimeException(anna::functions::asString("There is no origin host registered as '%s' (set Origin-Host avp correctly or force a specific host with 'node' operation)", name.c_str()), ANNA_FILE_LOCATION);
537
538   return result;
539 }
540
541 anna::diameter::comm::OriginHost *Launcher::getOriginHost(const anna::diameter::codec::Message &message) const throw(anna::RuntimeException) {
542   std::string originHost = message.getAvp(anna::diameter::helpers::base::AVPID__Origin_Host)->getDiameterIdentity()->getValue();
543   return (getOriginHost(originHost));
544 }
545
546 bool Launcher::uniqueOriginHost() const throw() {
547   anna::diameter::comm::OriginHostManager &ohm = anna::diameter::comm::OriginHostManager::instantiate();
548   return (ohm.size() == 1);
549 }
550
551
552 void Launcher::updateOperatedOriginHostWithMessage(const anna::diameter::codec::Message &message) throw(anna::RuntimeException) {
553   if (!a_operatedHost) // priority for working node by mean 'node' operation
554     a_operatedHost = getOriginHost(message);
555 }
556
557 anna::diameter::comm::OriginHost *Launcher::getWorkingNode() const throw(anna::RuntimeException) {
558   if(!a_workingNode)
559     throw anna::RuntimeException("Working node not identified (try to load services)", ANNA_FILE_LOCATION);
560
561   return a_workingNode;
562 }
563
564 anna::diameter::comm::OriginHost *Launcher::getOperatedHost() const throw(anna::RuntimeException) {
565   if(!a_operatedHost)
566     throw anna::RuntimeException("Node not identified (try to force a specific Origin-Host with 'node' operation)", ANNA_FILE_LOCATION);
567
568   return a_operatedHost;
569 }
570
571 MyDiameterEntity *Launcher::getOperatedEntity() const throw(anna::RuntimeException) {
572   MyDiameterEntity *result = (MyDiameterEntity *)(getOperatedHost()->getEntity());
573   if (!result)
574     throw anna::RuntimeException("No entity configured for the operated node", ANNA_FILE_LOCATION);
575   return result;
576 }
577
578 MyLocalServer *Launcher::getOperatedServer() const throw(anna::RuntimeException) {
579   MyLocalServer *result = (MyLocalServer *)(getOperatedHost()->getDiameterServer());
580   if (!result)
581     throw anna::RuntimeException("No local server configured for the operated node", ANNA_FILE_LOCATION);
582   return result;
583 }
584
585 MyDiameterEngine *Launcher::getOperatedEngine() const throw(anna::RuntimeException) {
586   return (MyDiameterEngine *)getOperatedHost()->getCommEngine(); // never will be NULL
587 }
588
589 void Launcher::initialize()
590 throw(anna::RuntimeException) {
591   anna::comm::Application::initialize();
592   CommandLine& cl(anna::CommandLine::instantiate());
593   anna::comm::Communicator::WorkMode::_v workMode(anna::comm::Communicator::WorkMode::Single);
594   a_communicator = new MyCommunicator(workMode);
595   a_timeEngine = new anna::timex::Engine((anna::Millisecond)600000, a_admlMinResolution);
596   anna::testing::TestManager::instantiate().setTimerController(a_timeEngine);
597
598   // Counters record procedure:
599   const char *varname = "cntRecordPeriod";
600   anna::Millisecond cntRecordPeriod;
601   try {
602     cntRecordPeriod = (cl.exists(varname)) ? checkTimeMeasure(varname, cl.getValue(varname)) : (anna::Millisecond)300000;
603   }
604   catch(anna::RuntimeException &ex) {
605     if (cntRecordPeriod != 0) throw ex;
606   }
607
608   if(cntRecordPeriod != 0) {
609     a_counterRecorderClock = new MyCounterRecorderClock("Counters record procedure clock", cntRecordPeriod); // clock
610     std::string cntDir = ".";
611     if(cl.exists("cntDir")) cntDir = cl.getValue("cntDir");
612     a_counterRecorder = new MyCounterRecorder(cntDir + anna::functions::asString("/Counters.Pid%d", (int)getPid()));
613   }
614
615   // Testing framework:
616   std::string tmDir = ".";
617   if(cl.exists("tmDir")) tmDir = cl.getValue("tmDir");
618   anna::testing::TestManager::instantiate().setReportsDirectory(tmDir);
619
620   // Tracing:
621   if(cl.exists("trace"))
622     anna::Logger::setLevel(anna::Logger::asLevel(cl.getValue("trace")));
623
624   // Load launcher services:
625   loadServices(cl.getValue("services")); // before run (have components to be created)
626 }
627
628 void Launcher::run()
629 throw(anna::RuntimeException) {
630   LOGMETHOD(anna::TraceMethod tm("Launcher", "run", ANNA_FILE_LOCATION));
631   CommandLine& cl(anna::CommandLine::instantiate());
632   anna::diameter::stack::Engine::instantiate();
633
634   // Start time:
635   a_start_time.setNow();
636
637   // Initial working directory:
638   char cwd[1024];
639   if (getcwd(cwd, sizeof(cwd)) == NULL)
640     throw anna::RuntimeException("Cannot retrieve initial working directory !!", ANNA_FILE_LOCATION);
641   a_initialWorkingDirectory = cwd;
642
643   // Statistics:
644   anna::statistics::Engine::instantiate().enable();
645
646   LOGINFORMATION(
647   // Test messages dtd:
648   std::string msg = "\n                     ------------- TESTMESSAGES DTD -------------\n";
649   msg += anna::diameter::codec::MessageDTD;
650   anna::Logger::information(msg, ANNA_FILE_LOCATION);
651   );
652
653   // HTTP Server:
654   if(cl.exists("httpServer")) {
655     anna::comm::Network& network = anna::comm::Network::instantiate();
656     std::string address;
657     int port;
658     anna::functions::getAddressAndPortFromSocketLiteral(cl.getValue("httpServer"), address, port);
659     //const anna::comm::Device* device = network.find(Device::asAddress(address)); // here provide IP
660     const anna::comm::Device* device = *((network.resolve(address)->device_begin())); // trick to solve
661     a_httpServerSocket = new anna::comm::ServerSocket(anna::comm::INetAddress(device, port), cl.exists("httpServerShared") /* shared bind */, &anna::http::Transport::getFactory());
662   }
663
664   ///////////////////////////////
665   // Diameter library COUNTERS //
666   ///////////////////////////////
667   anna::diameter::comm::OamModule & oamDiameterComm = anna::diameter::comm::OamModule::instantiate();
668   oamDiameterComm.initializeCounterScope(1);  // 1000 - 1999
669   oamDiameterComm.enableCounters();
670   oamDiameterComm.enableAlarms();
671   anna::diameter::codec::OamModule & oamDiameterCodec = anna::diameter::codec::OamModule::instantiate();
672   oamDiameterCodec.initializeCounterScope(2);  // 2000 - 2999
673   oamDiameterCodec.enableCounters();
674   oamDiameterCodec.enableAlarms();
675   /////////////////
676   // COMM MODULE //
677   /////////////////
678   /* Main events */
679   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestReceived, "" /* get defaults for enum type*/, 0 /*1000*/);
680   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerReceived,                 "", 1 /*1001*/);
681   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestReceivedOnClientSession, "", 2 /*1002*/);
682   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerReceivedOnClientSession,  "", 3 /*1003*/);
683   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestReceivedOnServerSession, "", 4 /* etc. */);
684   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerReceivedOnServerSession,  "", 5);
685   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentOK,                  "", 6);
686   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentNOK,                 "", 7);
687   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerSentOK,                   "", 8);
688   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerSentNOK,                  "", 9);
689   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentOnClientSessionOK,   "", 10);
690   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentOnClientSessionNOK,  "", 11);
691   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerSentOnClientSessionOK,    "", 12);
692   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerSentOnClientSessionNOK,   "", 13);
693   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentOnServerSessionOK,   "", 14);
694   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentOnServerSessionNOK,  "", 15);
695   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerSentOnServerSessionOK,    "", 16);
696   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerSentOnServerSessionNOK,   "", 17);
697   /* Diameter Base (capabilities exchange & keep alive) */
698   // as client
699   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::CERSentOK,   "", 18);
700   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::CERSentNOK,  "", 19);
701   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::CEAReceived, "", 20);
702   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DWRSentOK,   "", 21);
703   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DWRSentNOK,  "", 22);
704   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DWAReceived, "", 23);
705   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DPRSentOK,   "", 24);
706   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DPRSentNOK,  "", 25);
707   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DPAReceived, "", 26);
708   // as server
709   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::CERReceived, "", 27);
710   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::CEASentOK,   "", 28);
711   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::CEASentNOK,  "", 29);
712   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DWRReceived, "", 30);
713   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DWASentOK,   "", 31);
714   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DWASentNOK,  "", 32);
715   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DPRReceived, "", 33);
716   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DPASentOK,   "", 34);
717   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DPASentNOK,  "", 35);
718   /* server socket operations (enable/disable listening port for any local server) */
719   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::ServerSocketsOpened, "", 36);
720   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::ServerSocketsClosed, "", 37);
721   /* Connectivity */
722   // clients
723   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::UnableToDeliverOverEntity,                  "", 38);
724   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::LostAvailabilityOverClientSession,          "", 39);
725   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RecoveredAvailabilityOverClientSession,     "", 40);
726   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::LostAvailabilityOverServer,                 "", 41);
727   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RecoveredAvailabilityOverServer,            "", 42);
728   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::LostAvailabilityOverEntity,                 "", 43);
729   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RecoveredAvailabilityOverEntity,            "", 44);
730   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::LostAvailabilityOverEngineForEntities,      "", 45);
731   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RecoveredAvailabilityOverEngineForEntities, "", 46);
732   // servers
733   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::UnableToDeliverToClient,                                    "", 47);
734   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::LostConnectionForServerSession,                             "", 48);
735   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::UnbindConnectionForServerSessionDueToInactivityTimeAnomaly, "", 49);
736   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::CreatedConnectionForServerSession,                          "", 50);
737   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::LostAvailabilityOverLocalServer,                            "", 51);
738   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RecoveredAvailabilityOverLocalServer,                       "", 52);
739   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::LostAvailabilityOverEngineForLocalServers,                  "", 53);
740   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RecoveredAvailabilityOverEngineForLocalServers,             "", 54);
741   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentExpired,  "", 55);
742   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentOnClientSessionExpired,  "", 56);
743   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentOnServerSessionExpired,  "", 57);
744   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestRetransmitted,  "", 58);
745   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestRetransmittedOnClientSession,  "", 59);
746   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestRetransmittedOnServerSession,  "", 60);
747   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerReceivedUnknown,  "", 61);
748   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerReceivedOnClientSessionUnknown,  "", 62);
749   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerReceivedOnServerSessionUnknown,  "", 63);
750   //////////////////
751   // CODEC MODULE //
752   //////////////////
753   /* Avp decoding */
754   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::AvpDecode__NotEnoughBytesToCoverAvpHeaderLength,                          "", 0 /*2000*/);
755   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::AvpDecode__IncoherenceBetweenActivatedVBitAndZeroedVendorIDValueReceived, "", 1 /*2001*/);
756   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::AvpDecode__IncorrectLength,                                               "", 2 /*2002*/);
757   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::AvpDecode__DataPartInconsistence,                                         "", 3 /*2003*/);
758   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::AvpDecode__UnknownAvpWithMandatoryBit,                                    "", 4 /*2004*/);
759   /* Message decoding */
760   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::MessageDecode__NotEnoughBytesToCoverMessageHeaderLength, "", 5 /*2005*/);
761   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::MessageDecode__NotEnoughBytesToCoverMessageLength,       "", 6 /*2006*/);
762   /* Avp validation */
763   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::AvpValidation__EnumeratedAvpWithValueDoesNotComplyRestriction, "", 10 /*2010*/);
764   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::AvpValidation__AvpFlagsDoesNotFulfillTheDefinedFlagRules,      "", 11 /*2011*/);
765   /* Message validation */
766   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::MessageValidation__UnknownOperationUnableToValidate, "", 12 /*2012*/);
767   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::MessageValidation__OperationHaveIncoherentFlags,     "", 13 /*2013*/);
768   /* Level validation */
769   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::LevelValidation__MissingFixedRule,                                       "", 14 /*2014*/);
770   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::LevelValidation__FailedRuleForCardinality,                               "", 15 /*2015*/);
771   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::LevelValidation__FailedRuleForCardinalityLessThanNeeded,                 "", 16 /*2016*/);
772   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::LevelValidation__FailedRuleForCardinalityMoreThanNeeded,                 "", 17 /*2017*/);
773   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::LevelValidation__FailedGenericAvpRuleForCardinalityFoundDisregardedItem, "", 18 /*2018*/);
774   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::LevelValidation__FoundDisregardedItemsAndGenericAVPWasNotSpecified,      "", 19 /*2019*/);
775
776
777   /////////////////////////////////
778   // Counter recorder associated //
779   /////////////////////////////////
780   if(a_counterRecorderClock) {
781     oamDiameterComm.setCounterRecorder(a_counterRecorder);
782     oamDiameterCodec.setCounterRecorder(a_counterRecorder);
783     anna::diameter::comm::ApplicationMessageOamModule::instantiate().setCounterRecorder(a_counterRecorder);
784     a_timeEngine->activate(a_counterRecorderClock); // start clock
785   }
786
787   /////////////////////////////
788   // Log statistics concepts //
789   /////////////////////////////
790   if(cl.exists("logStatisticSamples"))
791     logStatisticsSamples(cl.getValue("logStatisticSamples"));
792
793   // Start client connections //////////////////////////////////////////////////////////////////////////////////
794   MyDiameterEntity *entity;
795   anna::diameter::comm::OriginHostManager &ohm = anna::diameter::comm::OriginHostManager::instantiate();
796   for (diameter::comm::origin_hosts_it it = ohm.begin(); it != ohm.end(); it++) {
797     entity = (MyDiameterEntity *)(it->second->getEntity());
798     if (entity) entity->bind();
799   }
800
801   // Go into communicator poll
802   // Reconnection period (tcp reconnect retry time):
803   const char *varname = "reconnectionPeriod";
804   anna::Millisecond reconnectionPeriod = (cl.exists(varname)) ? checkTimeMeasure(varname, cl.getValue(varname)) : (anna::Millisecond)10000;
805
806   a_communicator->setRecoveryTime(reconnectionPeriod);
807   if(cl.exists("httpServer")) a_communicator->attach(a_httpServerSocket);  // HTTP
808   a_communicator->accept();
809 }
810
811 bool Launcher::getDataBlockFromHexFile(const std::string &pathfile, anna::DataBlock &db) const throw(anna::RuntimeException) {
812   // Get hex string
813   static char buffer[8192];
814   std::ifstream infile(pathfile.c_str(), std::ifstream::in);
815
816   if(infile.is_open()) {
817     infile >> buffer;
818     std::string hexString(buffer, strlen(buffer));
819     // Allow colon separator in hex string: we have to remove them before processing with 'fromHexString':
820     hexString.erase(std::remove(hexString.begin(), hexString.end(), ':'), hexString.end());
821     LOGDEBUG(
822         std::string msg = "Hex string (remove colons if exists): ";
823     msg += hexString;
824     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
825     );
826
827     anna::functions::fromHexString(hexString, db); // could launch exception
828     // Close file
829     infile.close();
830     return true;
831   }
832
833   return false;
834 }
835
836 void Launcher::resetStatistics() throw() {
837   if (a_workingNode) {
838     a_workingNode->getCommEngine()->resetStatistics();
839   }
840   else {
841     anna::diameter::comm::OriginHostManager &ohm = anna::diameter::comm::OriginHostManager::instantiate();
842     for (diameter::comm::origin_hosts_it it = ohm.begin(); it != ohm.end(); it++) {
843       it->second->getCommEngine()->resetStatistics();
844     }
845   }
846 }
847
848 void Launcher::resetCounters() throw() {
849   anna::diameter::comm::OamModule::instantiate().resetCounters();
850   anna::diameter::comm::ApplicationMessageOamModule::instantiate().resetCounters();
851   anna::diameter::codec::OamModule::instantiate().resetCounters();
852 }
853
854 void Launcher::signalTerminate() throw(anna::RuntimeException) {
855   LOGMETHOD(anna::TraceMethod tm("Launcher", "signalTerminate", ANNA_FILE_LOCATION));
856
857   forceCountersRecord();
858
859   a_communicator->terminate ();
860   comm::Application::signalTerminate ();
861 }
862
863 void Launcher::signalUSR2() throw(anna::RuntimeException) {
864
865   std::string inputFile = getSignalUSR2InputFile();
866   std::string outputFile = getSignalUSR2OutputFile();
867
868   LOGNOTICE(
869   std::string msg = "Captured signal SIGUSR2. Reading tasks at '";
870   msg += inputFile;
871   msg += "' (results will be written at '";
872   msg += outputFile;
873   msg += "')";
874   anna::Logger::notice(msg, ANNA_FILE_LOCATION);
875   );
876
877   // Operation:
878   std::string line;
879   std::string response_content;
880   std::ifstream in_file(inputFile);
881   std::ofstream out_file(outputFile);
882
883   if(!in_file.is_open()) throw RuntimeException("Unable to read tasks", ANNA_FILE_LOCATION);
884   if(!out_file.is_open()) throw RuntimeException("Unable to write tasks", ANNA_FILE_LOCATION);
885
886   while(getline(in_file, line)) {
887
888     // Ignore comments and blank lines:
889     if (line[0] == '#')  continue;
890     if (std::string::npos == line.find_first_not_of(" \t")) continue;
891
892     LOGDEBUG(
893         std::string msg = "Processing line: ";
894     msg += line;
895     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
896     );
897
898     try {
899       eventOperation(line, response_content);
900     } catch(RuntimeException &ex) {
901       ex.trace();
902     }
903
904     out_file << response_content << "\n";
905   }
906
907   in_file.close();
908   out_file << "EOF\n";
909   out_file.close();
910 }
911
912
913 void Launcher::logStatisticsSamples(const std::string &conceptsList) throw() {
914   anna::statistics::Engine &statEngine = anna::statistics::Engine::instantiate();
915
916   if(conceptsList == "all") {
917     if(statEngine.enableSampleLog(/* -1: all concepts */))
918       LOGDEBUG(anna::Logger::debug("Sample log activation for all statistic concepts", ANNA_FILE_LOCATION));
919   }
920   else if(conceptsList == "none") {
921       if(statEngine.disableSampleLog(/* -1: all concepts */))
922         LOGDEBUG(anna::Logger::debug("Sample log deactivation for all statistic concepts", ANNA_FILE_LOCATION));
923   } else {
924     anna::Tokenizer lst;
925     lst.apply(conceptsList, ",");
926
927     if(lst.size() >= 1) {
928       anna::Tokenizer::const_iterator tok_min(lst.begin());
929       anna::Tokenizer::const_iterator tok_max(lst.end());
930       anna::Tokenizer::const_iterator tok_iter;
931       int conceptId;
932
933       for(tok_iter = tok_min; tok_iter != tok_max; tok_iter++) {
934         conceptId = atoi(anna::Tokenizer::data(tok_iter));
935
936         if(statEngine.enableSampleLog(conceptId))
937           LOGDEBUG(anna::Logger::debug(anna::functions::asString("Sample log activation for statistic concept id = %d", conceptId), ANNA_FILE_LOCATION));
938       }
939     }
940   }
941 }
942
943
944 bool Launcher::eventOperation(const std::string &operation, std::string &response_content) throw(anna::RuntimeException) {
945
946   bool result = true;
947
948   LOGMETHOD(anna::TraceMethod tm("Launcher", "eventOperation", ANNA_FILE_LOCATION));
949   if (operation == "") return result; // ignore
950   LOGDEBUG(anna::Logger::debug(anna::functions::asString("Operation: %s", operation.c_str()), ANNA_FILE_LOCATION));
951
952   // Default response:
953   response_content = "Operation processed with exception: ";
954   response_content += operation;
955   std::string opt_response_content = ""; // aditional response content
956   anna::DataBlock db_aux(true);
957   anna::diameter::codec::Message codecMsg; // auxiliary codec message
958
959   // Singletons:
960   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
961
962
963   ///////////////////////////////////////////////////////////////////
964   // Simple operations without arguments:
965
966   // Dynamic operation:
967   if(operation.find("dynamic") == 0) {
968     Procedure p(this);
969     int op_size = operation.size();
970     std::string args = ((operation.find("dynamic|") == 0) && (op_size > 8)) ? operation.substr(8) : "";
971     if (args == "" && op_size != 7)
972       throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
973     try {
974       p.execute(args, response_content);
975     }
976     catch(anna::RuntimeException &ex) {
977       ex.trace();
978       response_content = ex.asString();
979       return false;
980     }
981     return true; // OK
982   }
983
984   // Reset performance data:
985   if(operation == "collect") {
986     resetCounters();
987     resetStatistics();
988     response_content = "All process counters & statistic information have been reset";
989     return true; // OK
990   }
991
992   // Counters dump on demand:
993   if(operation == "forceCountersRecord") {
994     forceCountersRecord();
995     response_content = "Current counters have been dump to disk";
996     return true; // OK
997   }
998
999   // OAM & statistics:
1000   if(operation == "show-oam") {
1001     anna::xml::Node root("root");
1002     response_content = anna::xml::Compiler().apply(oamAsXML(&root));
1003     return true; // OK
1004   }
1005   if(operation == "show-stats") {
1006     anna::xml::Node root("root");
1007     response_content = anna::xml::Compiler().apply(statsAsXML(&root));
1008     return true; // OK
1009   }
1010
1011   ///////////////////////////////////////////////////////////////////
1012   // Tokenize operation
1013   Tokenizer params;
1014   params.apply(operation, "|", "<null>" /* allow contiguous separators */);
1015   int numParams = params.size() - 1;
1016
1017   // Get the operation type and parameters:
1018   Tokenizer::const_iterator tok_iter = params.begin();
1019   std::string opType = Tokenizer::data(tok_iter);
1020   std::string param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11;
1021   if(numParams >= 1) { tok_iter++; param1 = Tokenizer::data(tok_iter); }
1022   if(numParams >= 2) { tok_iter++; param2 = Tokenizer::data(tok_iter); }
1023   if(numParams >= 3) { tok_iter++; param3 = Tokenizer::data(tok_iter); }
1024   // Tests conditions
1025   if(numParams >= 4) { tok_iter++; param4 = Tokenizer::data(tok_iter); }
1026   if(numParams >= 5) { tok_iter++; param5 = Tokenizer::data(tok_iter); }
1027   if(numParams >= 6) { tok_iter++; param6 = Tokenizer::data(tok_iter); }
1028   if(numParams >= 7) { tok_iter++; param7 = Tokenizer::data(tok_iter); }
1029   if(numParams >= 8) { tok_iter++; param8 = Tokenizer::data(tok_iter); }
1030   if(numParams >= 9) { tok_iter++; param9 = Tokenizer::data(tok_iter); }
1031   if(numParams >= 10) { tok_iter++; param10 = Tokenizer::data(tok_iter); }
1032   if(numParams >= 11) { tok_iter++; param11 = Tokenizer::data(tok_iter); }
1033   // Remove '<null>' artificial token to ease further checkings:
1034   if (param1 == "<null>") param1 = "";
1035   if (param2 == "<null>") param2 = "";
1036   if (param3 == "<null>") param3 = "";
1037   if (param4 == "<null>") param4 = "";
1038   if (param5 == "<null>") param5 = "";
1039   if (param6 == "<null>") param6 = "";
1040   if (param7 == "<null>") param7 = "";
1041   if (param8 == "<null>") param8 = "";
1042   if (param9 == "<null>") param9 = "";
1043   if (param10 == "<null>") param10 = "";
1044   if (param11 == "<null>") param11 = "";
1045
1046   // No operation has more than 2 arguments except 'test' ...
1047   if(opType != "test" && numParams > 2)
1048     throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1049
1050
1051   // Check the number of parameters:
1052   bool wrongBody = false;
1053
1054   if((opType == "change-dir") && (numParams > 1)) wrongBody = true;
1055   if((opType == "log-statistics-samples") && (numParams != 1)) wrongBody = true;
1056   if((opType == "node") && (numParams > 1)) wrongBody = true;
1057
1058   if((opType == "node_auto") && (numParams > 0)) wrongBody = true;
1059
1060   if(((opType == "code") || (opType == "decode")) && (numParams != 2)) wrongBody = true;
1061
1062   if(((opType == "sendxml2e") || (opType == "sendhex2e")) && (numParams != 1)) wrongBody = true;
1063
1064   if((opType == "burst") && (numParams < 1)) wrongBody = true;
1065
1066   if((opType == "test") && (numParams < 1)) wrongBody = true;
1067
1068   if(((opType == "sendxml2c") || (opType == "sendhex2c") || (opType == "loadxml") || (opType == "diameterServerSessions")) && (numParams != 1)) wrongBody = true;
1069
1070   if(wrongBody) {
1071     // Launch exception
1072     std::string msg = "Wrong body content format on HTTP Request for '";
1073     msg += opType;
1074     msg += "' operation (missing parameter/s). Check 'HELP.md' for more information.";
1075     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
1076   }
1077
1078   // Operations:
1079   if(opType == "context") {
1080     std::string contextFile = ((numParams == 1) ? param1 : anna::functions::asString("/var/tmp/anna.context.%05d", getPid()));
1081     writeContext(contextFile);
1082     response_content = anna::functions::asString("Context dumped on file '%s'", contextFile.c_str());
1083     return true; // OK
1084   }
1085
1086   if(opType == "log-statistics-samples") {
1087     logStatisticsSamples(param1);
1088     response_content = anna::functions::asString("Log statistics samples for '%s' concepts", param1.c_str());
1089     return true; // OK
1090   }
1091
1092   // Change execution directory:
1093   if(opType == "change-dir") {
1094     if (param1 == "") param1 = a_initialWorkingDirectory;
1095     result = (chdir(param1.c_str()) == 0);
1096
1097     if (result)
1098       response_content = "New execution directory configured: ";
1099     else
1100       response_content = "Cannot assign provided execution directory: ";
1101
1102     response_content += param1;
1103     return result;
1104   }
1105
1106   if(opType == "services") {
1107     std::string servicesFile = ((numParams == 1) ? param1 : "services.xml");
1108     try {
1109       loadServices(servicesFile, true /* bind entities */);
1110     }
1111     catch(anna::RuntimeException &ex) {
1112       ex.trace();
1113       response_content = anna::functions::asString("Loaded services from file '%s' with errors", servicesFile.c_str());
1114       return false;
1115     }
1116     response_content = anna::functions::asString("Loaded services from file '%s'", servicesFile.c_str());
1117     return true; // OK
1118   }
1119
1120   // Host switch:
1121   if(opType == "node") {
1122     if (param1 != "") {
1123       if (setWorkingNode(param1)) response_content = anna::functions::asString("Forced node is now '%s'", param1.c_str());
1124     }
1125     else {
1126       if (a_workingNode) {
1127         response_content = "Working node is forced to be: \n\n";
1128         response_content += a_workingNode->asXMLString();
1129       }
1130       else {
1131         response_content = "Working node is automatic";
1132       }
1133     }
1134     return true; // OK
1135   }
1136   if(opType == "node_auto") {
1137     a_workingNode = NULL;
1138     response_content = "Working node has been set to automatic";
1139     return true; // OK
1140   }
1141
1142   // Operated host from possible forced-working node:
1143   a_operatedHost = a_workingNode ? a_workingNode /* priority */: NULL /* auto */;
1144   // Use later:
1145   //    If any message is managed: updateOperatedOriginHostWithMessage(codecMessage)
1146   //    To operate, use the exception-protected methods which never will return NULL:
1147   //         getOperatedHost(), getOperatedEntity(), getOperatedServer(), getOperatedEngine()
1148
1149
1150   if(opType == "code") {
1151     codecMsg.loadXMLFile(param1);
1152     std::string hexString = anna::functions::asHexString(codecMsg.code());
1153     // write to outfile
1154     std::ofstream outfile(param2.c_str(), std::ifstream::out);
1155     outfile.write(hexString.c_str(), hexString.size());
1156     outfile.close();
1157   } else if(opType == "decode") {
1158     // Get DataBlock from file with hex content:
1159     if(!getDataBlockFromHexFile(param1, db_aux))
1160       throw anna::RuntimeException("Error reading hex file provided", ANNA_FILE_LOCATION);
1161
1162     // Decode
1163     try { codecMsg.decode(db_aux); } catch(anna::RuntimeException &ex) { ex.trace(); }
1164
1165     std::string xmlString = codecMsg.asXMLString();
1166     // write to outfile
1167     std::ofstream outfile(param2.c_str(), std::ifstream::out);
1168     outfile.write(xmlString.c_str(), xmlString.size());
1169     outfile.close();
1170   } else if((opType == "hide") || (opType == "show") || (opType == "hidden") || (opType == "shown")) {
1171
1172     if(param1 != "") {
1173       if(param2 != "") {
1174         std::string key = param1;
1175         key += "|";
1176         key += param2;
1177
1178         if(opType == "hide") getOperatedEngine()->findClientSession(key)->hide();
1179
1180         if(opType == "show") getOperatedEngine()->findClientSession(key)->show();
1181
1182         if(opType == "hidden") opt_response_content = getOperatedEngine()->findClientSession(key)->hidden() ? "true" : "false";
1183
1184         if(opType == "shown") opt_response_content = getOperatedEngine()->findClientSession(key)->shown() ? "true" : "false";
1185       } else {
1186         std::string address;
1187         int port;
1188         anna::functions::getAddressAndPortFromSocketLiteral(param1, address, port);
1189
1190         if(opType == "hide") getOperatedEngine()->findServer(address, port)->hide();
1191
1192         if(opType == "show") getOperatedEngine()->findServer(address, port)->show();
1193
1194         if(opType == "hidden") opt_response_content = getOperatedEngine()->findServer(address, port)->hidden() ? "true" : "false";
1195
1196         if(opType == "shown") opt_response_content = getOperatedEngine()->findServer(address, port)->shown() ? "true" : "false";
1197       }
1198     } else {
1199       if(opType == "hide") getOperatedEntity()->hide();
1200
1201       if(opType == "show") getOperatedEntity()->show();
1202
1203       if(opType == "hidden") opt_response_content = getOperatedEntity()->hidden() ? "true" : "false";
1204
1205       if(opType == "shown") opt_response_content = getOperatedEntity()->shown() ? "true" : "false";
1206     }
1207   } else if((opType == "sendxml2e") || (opType == "sendhex2e")) {
1208     anna::diameter::comm::Message *msg;
1209
1210     if(opType == "sendxml2e") {
1211       codecMsg.loadXMLFile(param1);
1212       updateOperatedOriginHostWithMessage(codecMsg);
1213       msg = getOperatedHost()->createCommMessage();
1214       msg->clearBody();
1215       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)
1216       msg->setBody(codecMsg.code());
1217     } else {
1218       // Get DataBlock from file with hex content:
1219       if(!getDataBlockFromHexFile(param1, db_aux))
1220         throw anna::RuntimeException("Error reading hex file provided", ANNA_FILE_LOCATION);
1221       msg = getOperatedHost()->createCommMessage();
1222       msg->setBody(db_aux);
1223       try { if(getOperatedHost()->logEnabled()) codecMsg.decode(db_aux); } catch(anna::RuntimeException &ex) { ex.trace(); }
1224     }
1225
1226     bool success = getOperatedEntity()->send(msg);
1227     getOperatedHost()->releaseCommMessage(msg);
1228
1229     // Detailed log:
1230     if(getOperatedHost()->logEnabled()) {
1231       anna::diameter::comm::Server *usedServer = getOperatedEntity()->getLastUsedResource();
1232       anna::diameter::comm::ClientSession *usedClientSession = usedServer ? usedServer->getLastUsedResource() : NULL;
1233       std::string detail = usedClientSession ? usedClientSession->asString() : "<null client session>"; // shouldn't happen
1234       getOperatedHost()->writeLogFile(codecMsg, (success ? "sent2e" : "send2eError"), detail);
1235     }
1236   } else if((opType == "burst")) {
1237
1238     if (!uniqueOriginHost())
1239       throw anna::RuntimeException("Burst category operations are only allowed in single host node configuration. This is only to simplify user experience.", ANNA_FILE_LOCATION);
1240
1241     // burst|clear                     clears all loaded burst messages.
1242     // burst|load|<source_file>        loads the next diameter message into launcher burst.
1243     // burst|start|<initial load>      starts the message sending with a certain initial load.
1244     // burst|push|<load amount>        sends specific non-aynchronous load.
1245     // burst|stop                      stops the burst cycle.
1246     // burst|repeat|[[yes]|no]         restarts the burst launch when finish.
1247     // burst|send|<amount>             send messages from burst list. The main difference with
1248     //                                 start/push operations is that burst won't be awaken.
1249     //                                 Externally we could control sending time (no request
1250     //                                 will be sent for answers).
1251     // burst|goto|<order>              Updates current burst pointer position.
1252     // burst|look|<order>              Show programmed burst message for order provided, current when missing.
1253
1254     if(param1 == "clear") {
1255       opt_response_content = "removed ";
1256       opt_response_content += anna::functions::asString(getOperatedHost()->clearBurst());
1257       opt_response_content += " elements";
1258     } else if(param1 == "load") {
1259       if(param2 == "") throw anna::RuntimeException("Missing xml path file for burst load operation", ANNA_FILE_LOCATION);
1260
1261       codecMsg.loadXMLFile(param2);
1262       if(codecMsg.isAnswer()) throw anna::RuntimeException("Cannot load diameter answers for burst feature", ANNA_FILE_LOCATION);
1263       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)
1264
1265       int position = getOperatedHost()->loadBurstMessage(codecMsg.code());
1266       opt_response_content = "loaded '";
1267       opt_response_content += param2;
1268       opt_response_content += "' file into burst list position ";
1269       opt_response_content += anna::functions::asString(position);
1270     } else if(param1 == "start") {
1271       if(param2 == "") throw anna::RuntimeException("Missing initial load for burst start operation", ANNA_FILE_LOCATION);
1272
1273       int initialLoad = atoi(param2.c_str());
1274       int processed = getOperatedHost()->startBurst(initialLoad);
1275
1276       if(processed > 0) {
1277         opt_response_content = "initial load completed for ";
1278         opt_response_content += anna::functions::entriesAsString(processed, "message");
1279       }
1280     } else if(param1 == "push") {
1281       if(param2 == "") throw anna::RuntimeException("Missing load amount for burst push operation", ANNA_FILE_LOCATION);
1282
1283       int pushed = getOperatedHost()->pushBurst(atoi(param2.c_str()));
1284
1285       if(pushed > 0) {
1286         opt_response_content = "pushed ";
1287         opt_response_content += anna::functions::entriesAsString(pushed, "message");
1288       }
1289     } else if(param1 == "pop") {
1290       if(param2 == "") throw anna::RuntimeException("Missing amount for burst pop operation", ANNA_FILE_LOCATION);
1291
1292       int releaseLoad = atoi(param2.c_str());
1293       int popped = getOperatedHost()->popBurst(releaseLoad);
1294
1295       if(popped > 0) {
1296         opt_response_content = "burst popped for ";
1297         opt_response_content += anna::functions::entriesAsString(popped, "message");
1298       }
1299     } else if(param1 == "stop") {
1300       int left = getOperatedHost()->stopBurst();
1301
1302       if(left != -1) {
1303         opt_response_content += anna::functions::entriesAsString(left, "message");
1304         opt_response_content += " left to the end of the cycle";
1305       }
1306     } else if(param1 == "repeat") {
1307       if(param2 == "") param2 = "yes";
1308
1309       bool repeat = (param2 == "yes");
1310       getOperatedHost()->repeatBurst(repeat);
1311       opt_response_content += (repeat ? "repeat enabled" : "repeat disabled");
1312     } else if(param1 == "send") {
1313       if(param2 == "") throw anna::RuntimeException("Missing amount for burst send operation", ANNA_FILE_LOCATION);
1314
1315       int sent = getOperatedHost()->sendBurst(atoi(param2.c_str()));
1316
1317       if(sent > 0) {
1318         opt_response_content = "sent ";
1319         opt_response_content += anna::functions::entriesAsString(sent, "message");
1320       }
1321     } else if(param1 == "goto") {
1322       if(param2 == "") throw anna::RuntimeException("Missing order position for burst goto operation", ANNA_FILE_LOCATION);
1323
1324       opt_response_content = getOperatedHost()->gotoBurst(atoi(param2.c_str()));
1325     } else if(param1 == "look") {
1326       int order = ((param2 != "") ? atoi(param2.c_str()) : -1);
1327       opt_response_content = "\n\n";
1328       opt_response_content += getOperatedHost()->lookBurst(order);
1329     } else {
1330       throw anna::RuntimeException("Wrong body content format on HTTP Request for 'burst' operation (unexpected action parameter). Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1331     }
1332
1333   } else if((opType == "test")) {
1334     // test|<id>|<command>[|parameters]         Add a new step to the test case ...
1335     // test|ttps|<amount>                       Starts/resume the provided number of time ticks per second (ttps). The ADML starts ...
1336     // test|next[|<sync-amount>]                Forces the execution of the next test case(s) without waiting for test manager tick ...
1337     // test|ip-limit[|amount]                   In-progress limit of test cases. No new test cases will be launched over this value ...
1338     // test|repeats|<amount>                    Restarts the whole programmed test list when finished the amount number of times ...
1339     // test|report|<initialized/in-progress/failed/success/[all]/none>[|[yes]|no]
1340     //                                          Enables/disables report generation for a certain test case state: initialized, in-progress ...
1341     // test|report-hex[|[yes]|no]               Reports could include the diameter messages in hexadecimal format. Disabled by default.
1342     // test|goto|<id>                           Updates current test pointer position.
1343     // test|look[|id]                           Show programmed test case for id provided, current when missing ...
1344     // test|state[|id]                          Show test case state for id provided, current when missing ...
1345     // test|interact|amount|id                  Makes interactive a specific test case id. The amount is the margin of execution steps ...
1346     // test|reset|<[soft]/hard>[|id]            Reset the test case for id provided, all the tests when missing ...
1347     // test|auto-reset|<soft|hard>              When cycling, current test cases can be soft (default) or hard reset ...
1348     // test|clear                               Clears all the programmed test cases.
1349     // test|summary                             Test manager general report (number of test cases, counts by state ...
1350
1351
1352     if(param1 == "ttps") {
1353       if (numParams > 2)
1354         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1355
1356       bool success = ((param2 != "") ? testManager.configureTTPS(atoi(param2.c_str())) : false);
1357       if (success) {
1358         opt_response_content = "assigned new test launch rate to ";
1359         opt_response_content += anna::functions::asString(atoi(param2.c_str()));
1360         opt_response_content += " events per second";
1361       }
1362       else {
1363         opt_response_content += "unable to configure the test rate provided";
1364       }
1365     }
1366     else if (param1 == "next") {
1367       if (numParams > 2)
1368         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1369
1370       int sync_amount = ((param2 != "") ? atoi(param2.c_str()) : 1);
1371
1372       if (sync_amount < 1)
1373         throw anna::RuntimeException("The parameter 'sync-amount' must be a positive integer value", ANNA_FILE_LOCATION);
1374
1375       bool success = testManager.execTestCases(sync_amount);
1376
1377       opt_response_content = (success ? "" : "not completely " /* completed cycle and no repeats, rare case */);
1378       opt_response_content += "processed ";
1379       opt_response_content += anna::functions::asString(sync_amount);
1380       opt_response_content += ((sync_amount > 1) ? " test cases synchronously" : " test case");
1381     }
1382     else if(param1 == "ip-limit") {
1383       if (numParams > 2)
1384         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1385
1386       unsigned int limit;
1387       if (param2 != "") {
1388         limit = atoi(param2.c_str());
1389         testManager.setInProgressLimit(limit);
1390         opt_response_content = "new in-progress limit: ";
1391         opt_response_content += (limit != UINT_MAX) ? anna::functions::asString(limit) : "<no limit>";
1392       }
1393       else {
1394         opt_response_content = "in-progress limit amount: ";
1395         limit = testManager.getInProgressLimit();
1396         opt_response_content += (limit != UINT_MAX) ? anna::functions::asString(limit) : "<no limit>";
1397         opt_response_content += "; currently there are ";
1398         opt_response_content += anna::functions::asString(testManager.getInProgressCount());
1399         opt_response_content += " test cases running";
1400       }
1401     }
1402     else if(param1 == "repeats") {
1403       if (numParams != 2)
1404         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1405       int repeats = atoi(param2.c_str());
1406       if (repeats < 0) repeats = -1;
1407       testManager.setPoolRepeats(repeats);
1408       std::string nolimit = (repeats != -1) ? "":" [no limit]";
1409       opt_response_content += anna::functions::asString("Pool repeats: %d%s (current cycle: %d)", repeats, nolimit.c_str(), testManager.getPoolCycle());
1410     }
1411     else if(param1 == "report") {
1412       if (numParams > 3)
1413         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1414
1415       if(param2 == "") param2 = "all";
1416       if(param3 == "") param3 = "yes";
1417       bool enable = (param3 == "yes");
1418
1419       if(param2 == "initialized")
1420         testManager.setDumpInitializedReports(enable);
1421       else if(param2 == "in-progress")
1422         testManager.setDumpInProgressReports(enable);
1423       else if(param2 == "failed")
1424         testManager.setDumpFailedReports(enable);
1425       else if(param2 == "success")
1426         testManager.setDumpSuccessReports(enable);
1427       else if(param2 == "all") {
1428         param2 = "any";
1429         testManager.setDumpAllReports(enable);
1430       }
1431       else if(param2 == "none") {
1432         enable = !enable;
1433         param2 = "any";
1434         testManager.setDumpAllReports(enable);
1435       }
1436       else
1437         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1438
1439       opt_response_content += (enable ? "report enabled " : "report disabled ");
1440       opt_response_content += "for tests in '";
1441       opt_response_content += param2;
1442       opt_response_content += "' state";
1443     }
1444     else if(param1 == "report-hex") {
1445       if (numParams > 2)
1446         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1447
1448       if(param2 == "") param2 = "yes";
1449       testManager.setDumpHex((param2 == "yes"));
1450       opt_response_content += (testManager.getDumpHex() ? "report includes hexadecimal messages" : "report excludes hexadecimal messages");
1451     }
1452     else if(param1 == "dump-stdout") {
1453       if (numParams > 2)
1454         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1455
1456       if(param2 == "") param2 = "yes";
1457       testManager.setDumpStdout((param2 == "yes"));
1458       opt_response_content += (testManager.getDumpHex() ? "test manager dumps progress into stdout" : "test manager does not dump progress into stdout");
1459     }
1460     else if(param1 == "goto") {
1461       if (numParams > 2)
1462         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1463
1464       if(param2 == "") throw anna::RuntimeException("Missing id for test goto operation", ANNA_FILE_LOCATION);
1465       int id = atoi(param2.c_str());
1466       if (testManager.gotoTestCase(id)) {
1467         opt_response_content = "position updated for id provided (";
1468       }
1469       else {
1470         opt_response_content = "cannot found test id (";
1471       }
1472       opt_response_content += anna::functions::asString(id);
1473       opt_response_content += ")";
1474     }
1475     else if(param1 == "run") {
1476       if (numParams > 2)
1477         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1478
1479       if(param2 == "") throw anna::RuntimeException("Missing id for test run operation", ANNA_FILE_LOCATION);
1480       int id = atoi(param2.c_str());
1481       if (testManager.runTestCase(id)) {
1482         opt_response_content = "test executed for id provided (";
1483       }
1484       else {
1485         opt_response_content = "cannot found test id (";
1486       }
1487       opt_response_content += anna::functions::asString(id);
1488       opt_response_content += ")";
1489     }
1490     else if(param1 == "look") {
1491       if (numParams > 2)
1492         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1493
1494       int id = ((param2 != "") ? atoi(param2.c_str()) : -1);
1495       anna::testing::TestCase *testCase = testManager.findTestCase(id);
1496
1497       if (testCase) {
1498         response_content = testCase->asXMLString();
1499         return true; // OK
1500       }
1501       else {
1502         result = false;
1503         if (id == -1) {
1504           opt_response_content = "no current test case detected (testing started ?)";
1505         }
1506         else {
1507           opt_response_content = "cannot found test id (";
1508           opt_response_content += anna::functions::asString(id);
1509           opt_response_content += ")";
1510         }
1511       }
1512     }
1513     else if(param1 == "state") {
1514       if (numParams > 2)
1515         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1516
1517       int id = ((param2 != "") ? atoi(param2.c_str()) : -1);
1518       anna::testing::TestCase *testCase = testManager.findTestCase(id);
1519
1520       if (testCase) {
1521         response_content = anna::testing::TestCase::asText(testCase->getState());
1522         return testCase->isSuccess();
1523       }
1524       else {
1525         result = false;
1526         if (id == -1) {
1527           opt_response_content = "no current test case detected (testing started ?)";
1528         }
1529         else {
1530           opt_response_content = "cannot found test id (";
1531           opt_response_content += anna::functions::asString(id);
1532           opt_response_content += ")";
1533         }
1534       }
1535     }
1536     else if (param1 == "interact") {
1537       if (numParams != 2)
1538         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1539
1540       int amount = atoi(param2.c_str());
1541       if (amount < -1)
1542         throw anna::RuntimeException("Interactive amount must be -1 (to disable interactive mode) or a positive number.", ANNA_FILE_LOCATION);
1543
1544       int id = ((param3 != "") ? atoi(param3.c_str()) : -1);
1545       anna::testing::TestCase *testCase = testManager.findTestCase(id);
1546       if (testCase) {
1547         if (amount == -1) {
1548           testCase->makeInteractive(false);
1549           opt_response_content = "interactive mode disabled";
1550         }
1551         else {
1552           testCase->addInteractiveAmount(amount);
1553           opt_response_content = "added interactive amount of ";
1554           opt_response_content += anna::functions::asString(amount);
1555           opt_response_content += " units";
1556           if (amount == 0) opt_response_content += " (0: freezing a non-interactive testcase, no effect on already interactive)";
1557         }
1558         opt_response_content += " for test case id ";
1559         opt_response_content += anna::functions::asString(id);
1560       }
1561       else {
1562         result = false;
1563         opt_response_content = "cannot found test id (";
1564         opt_response_content += anna::functions::asString(id);
1565         opt_response_content += ")";
1566       }
1567     }
1568     else if(param1 == "reset") {
1569       if (numParams > 3)
1570         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1571
1572       if(param2 == "") param2 = "soft";
1573      if (param2 != "soft" && param2 != "hard")
1574         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1575
1576       int id = ((param3 != "") ? atoi(param3.c_str()) : -1);
1577       anna::testing::TestCase *testCase = ((id != -1) ? testManager.findTestCase(id) : NULL);
1578
1579       if (testCase) {
1580         bool done = testCase->reset(param2 == "hard");
1581         opt_response_content = "test ";
1582         opt_response_content += param2;
1583         opt_response_content += " reset for id ";
1584         opt_response_content += anna::functions::asString(id);
1585         opt_response_content += done ? ": done": ": not done";
1586       }
1587       else {
1588         if (id == -1) {
1589           bool anyReset = testManager.resetPool(param2 == "hard");
1590           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";
1591         }
1592         else {
1593           result = false;
1594           opt_response_content = "cannot found test id (";
1595           opt_response_content += anna::functions::asString(id);
1596           opt_response_content += ")";
1597         }
1598       }
1599     }
1600     else if(param1 == "auto-reset") {
1601       if (numParams != 2)
1602         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1603
1604       if (param2 != "soft" && param2 != "hard")
1605         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1606
1607       testManager.setAutoResetHard(param2 == "hard");
1608       opt_response_content += anna::functions::asString("Auto-reset configured to '%s'", param2.c_str());
1609     }
1610     else if(param1 == "initialized") {
1611       if (numParams > 1)
1612         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1613
1614       opt_response_content = anna::functions::asString("%lu", testManager.getInitializedCount());
1615     }
1616     else if(param1 == "finished") {
1617       if (numParams > 1)
1618         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1619
1620       opt_response_content = anna::functions::asString("%lu", testManager.getFinishedCount());
1621     }
1622     else if(param1 == "clear") {
1623       if (numParams > 1)
1624         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1625
1626       if (testManager.clearPool()) {
1627         opt_response_content = "all the programmed test cases have been dropped";
1628       }
1629       else {
1630         opt_response_content = "there are not programmed test cases to be removed";
1631       }
1632     }
1633     else if(param1 == "junit") {
1634       response_content = testManager.junitAsXMLString();
1635       return true; // OK
1636     }
1637     else if(param1 == "summary-counts") {
1638       response_content = testManager.summaryCounts();
1639       return true; // OK
1640     }
1641     else if(param1 == "summary-states") {
1642       response_content = testManager.summaryStates();
1643       return true; // OK
1644     }
1645     else if(param1 == "summary") {
1646       response_content = testManager.asXMLString();
1647       return true; // OK
1648     }
1649     else {
1650       int id = atoi(param1.c_str());
1651       if(id < 0)
1652         throw anna::RuntimeException("Invalid test case identifier: must be a non-negative number", ANNA_FILE_LOCATION);
1653
1654       // PARAM: 1     2            3      4          5           6             7           8          9       10         11
1655       // test|<id>|<command>
1656       //             description|<description>
1657       //             ip-limit[|<iplimit>]
1658       //             timeout|    <msecs>
1659       //             sendxml2e|  <file>[|<step number>]
1660       //             sendxml2c|  <file>[|<step number>]
1661       //             delay|      [msecs]
1662       //             wait<fe/fc>|[code]|[bitR]|[hopByHop]|[applicationId]|[sessionId]|[resultCode]|[msisdn]|[imsi]|[serviceContextId]
1663       //      wait<fe/fc>-answer|<step number>
1664       //      wait<fe/fc>-xml   |<source_file>[|strict]
1665       //      wait<fe/fc>-hex   |<source_file>[|strict]
1666       if(param2 == "") throw anna::RuntimeException("Missing command for test id operation", ANNA_FILE_LOCATION);
1667
1668       // Commands:
1669       if (param2 == "description") {
1670         if (numParams > 3)
1671           throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1672         if(param3 == "") throw anna::RuntimeException("Missing description for test case", ANNA_FILE_LOCATION);
1673         testManager.getTestCase(id)->setDescription(param3); // creates / reuses
1674       }
1675       else if (param2 == "ip-limit") {
1676         if (numParams > 3)
1677           throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1678         unsigned int limit = (param3 == "") ? 1 : atoi(param3.c_str());
1679         testManager.getTestCase(id)->addIpLimit(limit); // creates / reuses
1680       }
1681       else if (param2 == "timeout") {
1682         if (numParams > 3)
1683           throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1684         if(param3 == "") throw anna::RuntimeException("Missing milliseconds for 'timeout' command in test id operation", ANNA_FILE_LOCATION);
1685         anna::Millisecond timeout = checkTimeMeasure("Test case timeout", param3);
1686         testManager.getTestCase(id)->addTimeout(timeout); // creates / reuses
1687       }
1688       else if ((param2 == "sendxml2e")||(param2 == "sendxml2c")) {
1689         if (numParams > 4)
1690           throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1691         if(param3 == "") throw anna::RuntimeException(anna::functions::asString("Missing xml file for '%s' command in test id operation", param2.c_str()), ANNA_FILE_LOCATION);
1692         codecMsg.loadXMLFile(param3);
1693         LOGWARNING(
1694           if (!codecMsg.isRequest() && (param4 == ""))
1695             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);
1696         );
1697
1698         updateOperatedOriginHostWithMessage(codecMsg);
1699         int stepNumber = ((param4 != "") ? atoi(param4.c_str()):-1);
1700
1701         if (param2 == "sendxml2e")
1702           testManager.getTestCase(id)->addSendDiameterXml2e(codecMsg.code(), getOperatedHost(), stepNumber); // creates / reuses
1703         else
1704           testManager.getTestCase(id)->addSendDiameterXml2c(codecMsg.code(), getOperatedHost(), stepNumber); // creates / reuses
1705       }
1706       else if (param2 == "delay") {
1707         if (numParams > 3)
1708           throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1709         if(param3 == "") throw anna::RuntimeException("Missing milliseconds for 'delay' command in test id operation", ANNA_FILE_LOCATION);
1710         anna::Millisecond delay = ((param3 == "0" /* special case */) ? (anna::Millisecond)0 : checkTimeMeasure("Test case delay step", param3));
1711         testManager.getTestCase(id)->addDelay(delay); // creates / reuses
1712       }
1713
1714 // TODO(***)
1715 //                                        The way to identify the test case, is through registered Session-Id values for
1716 //                                         programmed requests. But this depends on the type of node. Acting as clients,
1717 //                                         requests received have Session-Id values which are already registered with
1718 //                                         one test case, causing an error if not found. Acting as servers, requests are
1719 //                                         received over a diameter local server from a client which are generating that
1720 //                                         Session-Id values. Then we know nothing about such values. The procedure in
1721 //                                         this case is find out a test case not-started containing a condition which
1722 //                                         comply with the incoming message, and reactivates it.
1723 // The other solution: register Session-Id values for answers send to client from a local diameter server.
1724       else if ((param2 == "waitfe")||(param2 == "waitfc")) {
1725         if (numParams > 11)
1726           throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1727         if (param3 != "" || param4 != "" || param5 != "" || param6 != "" || param7 != "" || param8 != "" || param9 != "" || param10 != "" || param11 != "") {
1728           bool fromEntity = (param2.substr(4,2) == "fe");
1729           testManager.getTestCase(id)->addWaitDiameter(fromEntity, param3, param4, param5, param6, param7, param8, param9, param10, param11);
1730         }
1731         else {
1732           throw anna::RuntimeException(anna::functions::asString("Missing condition for '%s' command in test id operation", param2.c_str()), ANNA_FILE_LOCATION);
1733         }
1734       }
1735       else if ((param2 == "waitfe-hex")||(param2 == "waitfc-hex")) {
1736         if (numParams > 4)
1737           throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1738         if(param3 == "") throw anna::RuntimeException(anna::functions::asString("Missing hex file for '%s' command in test id operation", param2.c_str()), ANNA_FILE_LOCATION);
1739
1740         // Get DataBlock from file with hex content:
1741         if(!getDataBlockFromHexFile(param3, db_aux))
1742           throw anna::RuntimeException("Error reading hex content from file provided", ANNA_FILE_LOCATION);
1743
1744         // Hexadecimal representation read from file:
1745         std::string regexp = anna::functions::asHexString(db_aux);
1746
1747         // optional 'full':
1748         if(param4 != "strict") {
1749           //// If request, we will ignore sequence data:
1750           //if (anna::diameter::codec::functions::requestBit(db_aux))
1751             regexp.replace (24, 16, "[0-9A-Fa-f]{16}");
1752
1753           regexp.insert(0, "^");
1754           regexp += "$";
1755         }
1756
1757         bool fromEntity = (param2.substr(4,2) == "fe");
1758         testManager.getTestCase(id)->addWaitDiameterRegexpHex(fromEntity, regexp);
1759       }
1760       else if ((param2 == "waitfe-xml")||(param2 == "waitfc-xml")) {
1761         if (numParams > 4)
1762           throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1763         if(param3 == "") throw anna::RuntimeException(anna::functions::asString("Missing xml file for '%s' command in test id operation", param2.c_str()), ANNA_FILE_LOCATION);
1764
1765         codecMsg.loadXMLFile(param3);
1766         std::string regexp = codecMsg.asXMLString(true /* normalization */);
1767
1768         // Now we must insert regular expressions in hop-by-hop, end-to-end and Origin-State-Id:
1769
1770         // optional 'full':
1771         if(param4 != "strict") {
1772           std::string::size_type pos, pos_1, pos_2;
1773
1774           pos = regexp.find("end-to-end-id=", 0u);
1775           pos = regexp.find("\"", pos);
1776           pos_1 = pos;
1777           pos = regexp.find("\"", pos+1);
1778           pos_2 = pos;
1779           regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
1780
1781           pos = regexp.find("hop-by-hop-id=", 0u);
1782           pos = regexp.find("\"", pos);
1783           pos_1 = pos;
1784           pos = regexp.find("\"", pos+1);
1785           pos_2 = pos;
1786           regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
1787
1788           // For this representation: <avp name="Origin-State-Id" data="1428633668"/>
1789           //pos = regexp.find("Origin-State-Id", 0u);
1790           //pos = regexp.find("\"", pos);
1791           //pos = regexp.find("\"", pos+1);
1792           //pos_1 = pos;
1793           //pos = regexp.find("\"", pos+1);
1794           //pos_2 = pos;
1795           //regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
1796           // But we have this one: <avp data="1428633668" name="Origin-State-Id"/>
1797           pos = regexp.find("Origin-State-Id", 0u);
1798           pos = regexp.rfind("\"", pos);
1799           pos = regexp.rfind("\"", pos-1);
1800           pos = regexp.rfind("\"", pos-1);
1801           pos_1 = pos;
1802           pos = regexp.find("\"", pos+1);
1803           pos_2 = pos;
1804           regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
1805
1806           //regexp.insert(0, "^");
1807           //regexp += "$";
1808         }
1809
1810         bool fromEntity = (param2.substr(4,2) == "fe");
1811         testManager.getTestCase(id)->addWaitDiameterRegexpXml(fromEntity, regexp);
1812       }
1813       else if (param2 == "sh-command") {
1814         // Allow pipes in command:
1815         //if (numParams > 4)
1816         //  throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1817         if(param3 == "") throw anna::RuntimeException("Missing script/executable command-line for 'sh-command' in test id operation", ANNA_FILE_LOCATION);
1818         std::string token = "|sh-command|";
1819         std::string command = operation.substr(operation.find(token) + token.size());
1820         testManager.getTestCase(id)->addCommand(command); // creates / reuses
1821       }
1822       else {
1823         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1824       }
1825     }
1826
1827   } else if((opType == "sendxml2c") || (opType == "sendhex2c")) {
1828     anna::diameter::comm::Message *msg;
1829
1830     if(opType == "sendxml2c") {
1831       codecMsg.loadXMLFile(param1);
1832       updateOperatedOriginHostWithMessage(codecMsg);
1833       msg = getOperatedHost()->createCommMessage();
1834       msg->clearBody();
1835       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)
1836
1837       msg->setBody(codecMsg.code());
1838     } else {
1839       // Get DataBlock from file with hex content:
1840       if(!getDataBlockFromHexFile(param1, db_aux))
1841         throw anna::RuntimeException("Error reading hex file provided", ANNA_FILE_LOCATION);
1842       msg = getOperatedHost()->createCommMessage();
1843       msg->setBody(db_aux);
1844     }
1845
1846     bool success = getOperatedServer()->send(msg);
1847     getOperatedHost()->releaseCommMessage(msg);
1848
1849     // Detailed log:
1850     if(getOperatedHost()->logEnabled()) {
1851       anna::diameter::comm::ServerSession *usedServerSession = getOperatedServer()->getLastUsedResource();
1852       std::string detail = usedServerSession ? usedServerSession->asString() : "<null server session>"; // shouldn't happen
1853       getOperatedHost()->writeLogFile(codecMsg, (success ? "sent2c" : "send2cError"), detail);
1854     }
1855   } else if(opType == "loadxml") {
1856     codecMsg.loadXMLFile(param1);
1857     response_content = codecMsg.asXMLString();
1858     return true; // OK
1859   } else if(opType == "diameterServerSessions") {
1860     int diameterServerSessions = atoi(param1.c_str());
1861     getOperatedServer()->setMaxConnections(diameterServerSessions);
1862
1863   } else if(opType == "answerxml2c") {
1864     if(param1 == "") { // programmed answers FIFO's to stdout
1865       response_content = getOperatedServer()->getReactingAnswers()->asString("ANSWERS TO CLIENT");
1866       return true; // OK
1867     } else if (param1 == "rotate") {
1868       getOperatedServer()->getReactingAnswers()->rotate(true);
1869     } else if (param1 == "exhaust") {
1870       getOperatedServer()->getReactingAnswers()->rotate(false);
1871     } else if (param1 == "clear") {
1872       getOperatedServer()->getReactingAnswers()->clear();
1873     } else if (param1 == "dump") {
1874       getOperatedServer()->getReactingAnswers()->dump("programmed_answer");
1875     } else {
1876       codecMsg.loadXMLFile(param1);
1877       updateOperatedOriginHostWithMessage(codecMsg);
1878       anna::diameter::codec::Message *message = getOperatedHost()->getCodecEngine()->createMessage(param1); // loads xml again, lesser of two evils
1879       LOGDEBUG(anna::Logger::debug(message->asXMLString(), ANNA_FILE_LOCATION));
1880
1881       if(message->isRequest())
1882         throw anna::RuntimeException("Cannot program diameter requests. Answer type must be provided", ANNA_FILE_LOCATION);
1883
1884       int code = message->getId().first;
1885       LOGDEBUG(anna::Logger::debug("Adding a new programed 'answer to client' to the FIFO queue corresponding to its message code ...", ANNA_FILE_LOCATION));
1886       getOperatedServer()->getReactingAnswers()->addMessage(code, message);
1887     }
1888   } else if(opType == "answerxml2e") {
1889
1890     if(param1 == "") { // programmed answers FIFO's to stdout
1891       response_content = getOperatedEntity()->getReactingAnswers()->asString("ANSWERS TO ENTITY");
1892       return true; // OK
1893     } else if (param1 == "rotate") {
1894       getOperatedEntity()->getReactingAnswers()->rotate(true);
1895     } else if (param1 == "exhaust") {
1896       getOperatedEntity()->getReactingAnswers()->rotate(false);
1897     } else if (param1 == "clear") {
1898       getOperatedEntity()->getReactingAnswers()->clear();
1899     } else if (param1 == "dump") {
1900       getOperatedEntity()->getReactingAnswers()->dump("programmed_answer");
1901     } else {
1902       codecMsg.loadXMLFile(param1);
1903       updateOperatedOriginHostWithMessage(codecMsg);
1904       anna::diameter::codec::Message *message = getOperatedHost()->getCodecEngine()->createMessage(param1); // loads xml again, lesser of two evils
1905       LOGDEBUG(anna::Logger::debug(message->asXMLString(), ANNA_FILE_LOCATION));
1906
1907       if(message->isRequest())
1908         throw anna::RuntimeException("Cannot program diameter requests. Answer type must be provided", ANNA_FILE_LOCATION);
1909
1910       int code = message->getId().first;
1911       LOGDEBUG(anna::Logger::debug("Adding a new programed 'answer to entity' to the FIFO queue corresponding to its message code ...", ANNA_FILE_LOCATION));
1912       getOperatedEntity()->getReactingAnswers()->addMessage(code, message);
1913     }
1914   } else {
1915     throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1916   }
1917
1918   // HTTP response
1919   response_content = "Operation correctly processed: "; response_content += operation;
1920   if (opt_response_content != "") {
1921     response_content += " => ";
1922     response_content += opt_response_content;
1923   }
1924
1925   return result;
1926 }
1927
1928 anna::xml::Node* Launcher::asXML(anna::xml::Node* parent) const
1929 throw() {
1930   anna::xml::Node* result = parent->createChild("launcher");
1931   anna::comm::Application::asXML(result);
1932   // Timming:
1933   result->createAttribute("StartTime", a_start_time.asString());
1934   result->createAttribute("InitialWorkingDirectory", a_initialWorkingDirectory);
1935   result->createAttribute("SecondsLifeTime", anna::time::functions::lapsedMilliseconds() / 1000);
1936   // Diameter:
1937   anna::diameter::comm::OriginHostManager &ohm = anna::diameter::comm::OriginHostManager::instantiate();
1938   for (diameter::comm::origin_hosts_it it = ohm.begin(); it != ohm.end(); it++) {
1939     it->second->asXML(result);
1940   }
1941
1942   // Registered codec engines:
1943   anna::diameter::codec::EngineManager &em = anna::diameter::codec::EngineManager::instantiate();
1944   em.asXML(result);
1945
1946   // OAM & statistics:
1947   oamAsXML(result);
1948   statsAsXML(result);
1949
1950   // Testing: could be heavy if test case reports are enabled
1951   anna::testing::TestManager::instantiate().asXML(result);
1952
1953   return result;
1954 }
1955
1956 anna::xml::Node* Launcher::oamAsXML(anna::xml::Node* parent) const
1957 throw() {
1958   anna::xml::Node* result = parent->createChild("Oam");
1959
1960   // OAM:
1961   anna::diameter::comm::OamModule::instantiate().asXML(result);
1962   anna::diameter::comm::ApplicationMessageOamModule::instantiate().asXML(result);
1963   anna::diameter::codec::OamModule::instantiate().asXML(result);
1964
1965   return result;
1966 }
1967
1968 anna::xml::Node* Launcher::statsAsXML(anna::xml::Node* parent) const
1969 throw() {
1970   anna::xml::Node* result = parent->createChild("Statistics");
1971
1972   // Statistics:
1973   anna::statistics::Engine::instantiate().asXML(result);
1974
1975   return result;
1976 }