bb28699ec9bf76837e9048055dd8c867830ecc3c
[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 bool Launcher::getContentFromFile(const std::string &pathfile, std::string &content) const throw(anna::RuntimeException) {
837
838   std::ifstream inFile(pathfile.c_str(), std::ifstream::in);
839   if(!inFile.good()) {
840     throw RuntimeException(anna::functions::asString("Unable to open file '%s'", pathfile.c_str()), ANNA_FILE_LOCATION);
841   }
842
843   std::stringstream strStream;
844   strStream << inFile.rdbuf(); //read the file
845   content = strStream.str(); // holds the content of the file
846   inFile.close();
847
848   return true;
849 }
850
851 void Launcher::resetStatistics() throw() {
852   if (a_workingNode) {
853     a_workingNode->getCommEngine()->resetStatistics();
854   }
855   else {
856     anna::diameter::comm::OriginHostManager &ohm = anna::diameter::comm::OriginHostManager::instantiate();
857     for (diameter::comm::origin_hosts_it it = ohm.begin(); it != ohm.end(); it++) {
858       it->second->getCommEngine()->resetStatistics();
859     }
860   }
861 }
862
863 void Launcher::resetCounters() throw() {
864   anna::diameter::comm::OamModule::instantiate().resetCounters();
865   anna::diameter::comm::ApplicationMessageOamModule::instantiate().resetCounters();
866   anna::diameter::codec::OamModule::instantiate().resetCounters();
867 }
868
869 void Launcher::signalTerminate() throw(anna::RuntimeException) {
870   LOGMETHOD(anna::TraceMethod tm("Launcher", "signalTerminate", ANNA_FILE_LOCATION));
871
872   forceCountersRecord();
873
874   a_communicator->terminate ();
875   comm::Application::signalTerminate ();
876 }
877
878 void Launcher::signalUSR2() throw(anna::RuntimeException) {
879
880   std::string inputFile = getSignalUSR2InputFile();
881   std::string outputFile = getSignalUSR2OutputFile();
882
883   LOGNOTICE(
884   std::string msg = "Captured signal SIGUSR2. Reading tasks at '";
885   msg += inputFile;
886   msg += "' (results will be written at '";
887   msg += outputFile;
888   msg += "')";
889   anna::Logger::notice(msg, ANNA_FILE_LOCATION);
890   );
891
892   // Operation:
893   std::string line;
894   std::string response_content;
895   std::ifstream in_file(inputFile);
896   std::ofstream out_file(outputFile);
897
898   if(!in_file.is_open()) throw RuntimeException("Unable to read tasks", ANNA_FILE_LOCATION);
899   if(!out_file.is_open()) throw RuntimeException("Unable to write tasks", ANNA_FILE_LOCATION);
900
901   while(getline(in_file, line)) {
902
903     // Ignore comments and blank lines:
904     if (line[0] == '#')  continue;
905     if (std::string::npos == line.find_first_not_of(" \t")) continue;
906
907     LOGDEBUG(
908         std::string msg = "Processing line: ";
909     msg += line;
910     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
911     );
912
913     try {
914       eventOperation(line, response_content);
915     } catch(RuntimeException &ex) {
916       ex.trace();
917     }
918
919     out_file << response_content << "\n";
920   }
921
922   in_file.close();
923   out_file << "EOF\n";
924   out_file.close();
925 }
926
927
928 void Launcher::logStatisticsSamples(const std::string &conceptsList) throw() {
929   anna::statistics::Engine &statEngine = anna::statistics::Engine::instantiate();
930
931   if(conceptsList == "all") {
932     if(statEngine.enableSampleLog(/* -1: all concepts */))
933       LOGDEBUG(anna::Logger::debug("Sample log activation for all statistic concepts", ANNA_FILE_LOCATION));
934   }
935   else if(conceptsList == "none") {
936       if(statEngine.disableSampleLog(/* -1: all concepts */))
937         LOGDEBUG(anna::Logger::debug("Sample log deactivation for all statistic concepts", ANNA_FILE_LOCATION));
938   } else {
939     anna::Tokenizer lst;
940     lst.apply(conceptsList, ",");
941
942     if(lst.size() >= 1) {
943       anna::Tokenizer::const_iterator tok_min(lst.begin());
944       anna::Tokenizer::const_iterator tok_max(lst.end());
945       anna::Tokenizer::const_iterator tok_iter;
946       int conceptId;
947
948       for(tok_iter = tok_min; tok_iter != tok_max; tok_iter++) {
949         conceptId = atoi(anna::Tokenizer::data(tok_iter));
950
951         if(statEngine.enableSampleLog(conceptId))
952           LOGDEBUG(anna::Logger::debug(anna::functions::asString("Sample log activation for statistic concept id = %d", conceptId), ANNA_FILE_LOCATION));
953       }
954     }
955   }
956 }
957
958
959 bool Launcher::eventOperation(const std::string &operation, std::string &response_content) throw(anna::RuntimeException) {
960
961   bool result = true;
962
963   LOGMETHOD(anna::TraceMethod tm("Launcher", "eventOperation", ANNA_FILE_LOCATION));
964   if (operation == "") return result; // ignore
965   LOGDEBUG(anna::Logger::debug(anna::functions::asString("Operation: %s", operation.c_str()), ANNA_FILE_LOCATION));
966
967   // Default response:
968   response_content = "Operation processed with exception: ";
969   response_content += operation;
970   std::string opt_response_content = ""; // aditional response content
971   anna::DataBlock db_aux(true);
972   anna::diameter::codec::Message codecMsg; // auxiliary codec message
973
974   // Singletons:
975   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
976
977
978   ///////////////////////////////////////////////////////////////////
979   // Simple operations without arguments:
980
981   // Dynamic operation:
982   if(operation.find("dynamic") == 0) {
983     Procedure p(this);
984     int op_size = operation.size();
985     std::string args = ((operation.find("dynamic|") == 0) && (op_size > 8)) ? operation.substr(8) : "";
986     if (args == "" && op_size != 7)
987       throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
988     try {
989       p.execute(args, response_content);
990     }
991     catch(anna::RuntimeException &ex) {
992       ex.trace();
993       response_content = ex.asString();
994       return false;
995     }
996     return true; // OK
997   }
998
999   // Reset performance data:
1000   if(operation == "collect") {
1001     resetCounters();
1002     resetStatistics();
1003     response_content = "All process counters & statistic information have been reset";
1004     return true; // OK
1005   }
1006
1007   // Counters dump on demand:
1008   if(operation == "forceCountersRecord") {
1009     forceCountersRecord();
1010     response_content = "Current counters have been dump to disk";
1011     return true; // OK
1012   }
1013
1014   // OAM & statistics:
1015   if(operation == "show-oam") {
1016     anna::xml::Node root("root");
1017     response_content = anna::xml::Compiler().apply(oamAsXML(&root));
1018     return true; // OK
1019   }
1020   if(operation == "show-stats") {
1021     anna::xml::Node root("root");
1022     response_content = anna::xml::Compiler().apply(statsAsXML(&root));
1023     return true; // OK
1024   }
1025
1026   ///////////////////////////////////////////////////////////////////
1027   // Tokenize operation
1028   Tokenizer params;
1029   params.apply(operation, "|", "<null>" /* allow contiguous separators */);
1030   int numParams = params.size() - 1;
1031
1032   // Get the operation type and parameters:
1033   Tokenizer::const_iterator tok_iter = params.begin();
1034   std::string opType = Tokenizer::data(tok_iter);
1035   std::string param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11;
1036   if(numParams >= 1) { tok_iter++; param1 = Tokenizer::data(tok_iter); }
1037   if(numParams >= 2) { tok_iter++; param2 = Tokenizer::data(tok_iter); }
1038   if(numParams >= 3) { tok_iter++; param3 = Tokenizer::data(tok_iter); }
1039   // Tests conditions
1040   if(numParams >= 4) { tok_iter++; param4 = Tokenizer::data(tok_iter); }
1041   if(numParams >= 5) { tok_iter++; param5 = Tokenizer::data(tok_iter); }
1042   if(numParams >= 6) { tok_iter++; param6 = Tokenizer::data(tok_iter); }
1043   if(numParams >= 7) { tok_iter++; param7 = Tokenizer::data(tok_iter); }
1044   if(numParams >= 8) { tok_iter++; param8 = Tokenizer::data(tok_iter); }
1045   if(numParams >= 9) { tok_iter++; param9 = Tokenizer::data(tok_iter); }
1046   if(numParams >= 10) { tok_iter++; param10 = Tokenizer::data(tok_iter); }
1047   if(numParams >= 11) { tok_iter++; param11 = Tokenizer::data(tok_iter); }
1048   // Remove '<null>' artificial token to ease further checkings:
1049   if (param1 == "<null>") param1 = "";
1050   if (param2 == "<null>") param2 = "";
1051   if (param3 == "<null>") param3 = "";
1052   if (param4 == "<null>") param4 = "";
1053   if (param5 == "<null>") param5 = "";
1054   if (param6 == "<null>") param6 = "";
1055   if (param7 == "<null>") param7 = "";
1056   if (param8 == "<null>") param8 = "";
1057   if (param9 == "<null>") param9 = "";
1058   if (param10 == "<null>") param10 = "";
1059   if (param11 == "<null>") param11 = "";
1060
1061   // No operation has more than 2 arguments except 'test' ...
1062   if(opType != "test" && numParams > 2)
1063     throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1064
1065
1066   // Check the number of parameters:
1067   bool wrongBody = false;
1068
1069   if((opType == "change-dir") && (numParams > 1)) wrongBody = true;
1070   if((opType == "log-statistics-samples") && (numParams != 1)) wrongBody = true;
1071   if((opType == "node") && (numParams > 1)) wrongBody = true;
1072
1073   if((opType == "node_auto") && (numParams > 0)) wrongBody = true;
1074
1075   if(((opType == "code") || (opType == "decode")) && (numParams != 2)) wrongBody = true;
1076
1077   if(((opType == "sendxml2e") || (opType == "sendhex2e")) && (numParams != 1)) wrongBody = true;
1078
1079   if((opType == "burst") && (numParams < 1)) wrongBody = true;
1080
1081   if((opType == "test") && (numParams < 1)) wrongBody = true;
1082
1083   if(((opType == "sendxml2c") || (opType == "sendhex2c") || (opType == "loadxml") || (opType == "diameterServerSessions")) && (numParams != 1)) wrongBody = true;
1084
1085   if(wrongBody) {
1086     // Launch exception
1087     std::string msg = "Wrong body content format on HTTP Request for '";
1088     msg += opType;
1089     msg += "' operation (missing parameter/s). Check 'HELP.md' for more information.";
1090     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
1091   }
1092
1093   // Operations:
1094   if(opType == "context") {
1095     std::string contextFile = ((numParams == 1) ? param1 : anna::functions::asString("/var/tmp/anna.context.%05d", getPid()));
1096     writeContext(contextFile);
1097     response_content = anna::functions::asString("Context dumped on file '%s'", contextFile.c_str());
1098     return true; // OK
1099   }
1100
1101   if(opType == "log-statistics-samples") {
1102     logStatisticsSamples(param1);
1103     response_content = anna::functions::asString("Log statistics samples for '%s' concepts", param1.c_str());
1104     return true; // OK
1105   }
1106
1107   // Change execution directory:
1108   if(opType == "change-dir") {
1109     if (param1 == "") param1 = a_initialWorkingDirectory;
1110     result = (chdir(param1.c_str()) == 0);
1111
1112     if (result)
1113       response_content = "New execution directory configured: ";
1114     else
1115       response_content = "Cannot assign provided execution directory: ";
1116
1117     response_content += param1;
1118     return result;
1119   }
1120
1121   if(opType == "services") {
1122     std::string servicesFile = ((numParams == 1) ? param1 : "services.xml");
1123     try {
1124       loadServices(servicesFile, true /* bind entities */);
1125     }
1126     catch(anna::RuntimeException &ex) {
1127       ex.trace();
1128       response_content = anna::functions::asString("Loaded services from file '%s' with errors", servicesFile.c_str());
1129       return false;
1130     }
1131     response_content = anna::functions::asString("Loaded services from file '%s'", servicesFile.c_str());
1132     return true; // OK
1133   }
1134
1135   // Host switch:
1136   if(opType == "node") {
1137     if (param1 != "") {
1138       if (setWorkingNode(param1)) response_content = anna::functions::asString("Forced node is now '%s'", param1.c_str());
1139     }
1140     else {
1141       if (a_workingNode) {
1142         response_content = "Working node is forced to be: \n\n";
1143         response_content += a_workingNode->asXMLString();
1144       }
1145       else {
1146         response_content = "Working node is automatic";
1147       }
1148     }
1149     return true; // OK
1150   }
1151   if(opType == "node_auto") {
1152     a_workingNode = NULL;
1153     response_content = "Working node has been set to automatic";
1154     return true; // OK
1155   }
1156
1157   // Operated host from possible forced-working node:
1158   a_operatedHost = a_workingNode ? a_workingNode /* priority */: NULL /* auto */;
1159   // Use later:
1160   //    If any message is managed: updateOperatedOriginHostWithMessage(codecMessage)
1161   //    To operate, use the exception-protected methods which never will return NULL:
1162   //         getOperatedHost(), getOperatedEntity(), getOperatedServer(), getOperatedEngine()
1163
1164
1165   if(opType == "code") {
1166     codecMsg.loadXMLFile(param1);
1167     std::string hexString = anna::functions::asHexString(codecMsg.code());
1168     // write to outfile
1169     std::ofstream outfile(param2.c_str(), std::ifstream::out);
1170     outfile.write(hexString.c_str(), hexString.size());
1171     outfile.close();
1172   } else if(opType == "decode") {
1173     // Get DataBlock from file with hex content:
1174     if(!getDataBlockFromHexFile(param1, db_aux))
1175       throw anna::RuntimeException("Error reading hex file provided", ANNA_FILE_LOCATION);
1176
1177     // Decode
1178     try { codecMsg.decode(db_aux); } catch(anna::RuntimeException &ex) { ex.trace(); }
1179
1180     std::string xmlString = codecMsg.asXMLString();
1181     // write to outfile
1182     std::ofstream outfile(param2.c_str(), std::ifstream::out);
1183     outfile.write(xmlString.c_str(), xmlString.size());
1184     outfile.close();
1185   } else if((opType == "hide") || (opType == "show") || (opType == "hidden") || (opType == "shown")) {
1186
1187     if(param1 != "") {
1188       if(param2 != "") {
1189         std::string key = param1;
1190         key += "|";
1191         key += param2;
1192
1193         if(opType == "hide") getOperatedEngine()->findClientSession(key)->hide();
1194
1195         if(opType == "show") getOperatedEngine()->findClientSession(key)->show();
1196
1197         if(opType == "hidden") opt_response_content = getOperatedEngine()->findClientSession(key)->hidden() ? "true" : "false";
1198
1199         if(opType == "shown") opt_response_content = getOperatedEngine()->findClientSession(key)->shown() ? "true" : "false";
1200       } else {
1201         std::string address;
1202         int port;
1203         anna::functions::getAddressAndPortFromSocketLiteral(param1, address, port);
1204
1205         if(opType == "hide") getOperatedEngine()->findServer(address, port)->hide();
1206
1207         if(opType == "show") getOperatedEngine()->findServer(address, port)->show();
1208
1209         if(opType == "hidden") opt_response_content = getOperatedEngine()->findServer(address, port)->hidden() ? "true" : "false";
1210
1211         if(opType == "shown") opt_response_content = getOperatedEngine()->findServer(address, port)->shown() ? "true" : "false";
1212       }
1213     } else {
1214       if(opType == "hide") getOperatedEntity()->hide();
1215
1216       if(opType == "show") getOperatedEntity()->show();
1217
1218       if(opType == "hidden") opt_response_content = getOperatedEntity()->hidden() ? "true" : "false";
1219
1220       if(opType == "shown") opt_response_content = getOperatedEntity()->shown() ? "true" : "false";
1221     }
1222   } else if((opType == "sendxml2e") || (opType == "sendhex2e")) {
1223     anna::diameter::comm::Message *msg;
1224
1225     if(opType == "sendxml2e") {
1226       codecMsg.loadXMLFile(param1);
1227       updateOperatedOriginHostWithMessage(codecMsg);
1228       msg = getOperatedHost()->createCommMessage();
1229       msg->clearBody();
1230       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)
1231       msg->setBody(codecMsg.code());
1232     } else {
1233       // Get DataBlock from file with hex content:
1234       if(!getDataBlockFromHexFile(param1, db_aux))
1235         throw anna::RuntimeException("Error reading hex file provided", ANNA_FILE_LOCATION);
1236       msg = getOperatedHost()->createCommMessage();
1237       msg->setBody(db_aux);
1238       try { if(getOperatedHost()->logEnabled()) codecMsg.decode(db_aux); } catch(anna::RuntimeException &ex) { ex.trace(); }
1239     }
1240
1241     bool success = getOperatedEntity()->send(msg);
1242     getOperatedHost()->releaseCommMessage(msg);
1243
1244     // Detailed log:
1245     if(getOperatedHost()->logEnabled()) {
1246       anna::diameter::comm::Server *usedServer = getOperatedEntity()->getLastUsedResource();
1247       anna::diameter::comm::ClientSession *usedClientSession = usedServer ? usedServer->getLastUsedResource() : NULL;
1248       std::string detail = usedClientSession ? usedClientSession->asString() : "<null client session>"; // shouldn't happen
1249       getOperatedHost()->writeLogFile(codecMsg, (success ? "sent2e" : "send2eError"), detail);
1250     }
1251   } else if((opType == "burst")) {
1252
1253     if (!uniqueOriginHost())
1254       throw anna::RuntimeException("Burst category operations are only allowed in single host node configuration. This is only to simplify user experience.", ANNA_FILE_LOCATION);
1255
1256     // burst|clear                     clears all loaded burst messages.
1257     // burst|load|<source_file>        loads the next diameter message into launcher burst.
1258     // burst|start|<initial load>      starts the message sending with a certain initial load.
1259     // burst|push|<load amount>        sends specific non-aynchronous load.
1260     // burst|stop                      stops the burst cycle.
1261     // burst|repeat|[[yes]|no]         restarts the burst launch when finish.
1262     // burst|send|<amount>             send messages from burst list. The main difference with
1263     //                                 start/push operations is that burst won't be awaken.
1264     //                                 Externally we could control sending time (no request
1265     //                                 will be sent for answers).
1266     // burst|goto|<order>              Updates current burst pointer position.
1267     // burst|look|<order>              Show programmed burst message for order provided, current when missing.
1268
1269     if(param1 == "clear") {
1270       opt_response_content = "removed ";
1271       opt_response_content += anna::functions::asString(getOperatedHost()->clearBurst());
1272       opt_response_content += " elements";
1273     } else if(param1 == "load") {
1274       if(param2 == "") throw anna::RuntimeException("Missing xml path file for burst load operation", ANNA_FILE_LOCATION);
1275
1276       codecMsg.loadXMLFile(param2);
1277       if(codecMsg.isAnswer()) throw anna::RuntimeException("Cannot load diameter answers for burst feature", ANNA_FILE_LOCATION);
1278       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)
1279
1280       int position = getOperatedHost()->loadBurstMessage(codecMsg.code());
1281       opt_response_content = "loaded '";
1282       opt_response_content += param2;
1283       opt_response_content += "' file into burst list position ";
1284       opt_response_content += anna::functions::asString(position);
1285     } else if(param1 == "start") {
1286       if(param2 == "") throw anna::RuntimeException("Missing initial load for burst start operation", ANNA_FILE_LOCATION);
1287
1288       int initialLoad = atoi(param2.c_str());
1289       int processed = getOperatedHost()->startBurst(initialLoad);
1290
1291       if(processed > 0) {
1292         opt_response_content = "initial load completed for ";
1293         opt_response_content += anna::functions::entriesAsString(processed, "message");
1294       }
1295     } else if(param1 == "push") {
1296       if(param2 == "") throw anna::RuntimeException("Missing load amount for burst push operation", ANNA_FILE_LOCATION);
1297
1298       int pushed = getOperatedHost()->pushBurst(atoi(param2.c_str()));
1299
1300       if(pushed > 0) {
1301         opt_response_content = "pushed ";
1302         opt_response_content += anna::functions::entriesAsString(pushed, "message");
1303       }
1304     } else if(param1 == "pop") {
1305       if(param2 == "") throw anna::RuntimeException("Missing amount for burst pop operation", ANNA_FILE_LOCATION);
1306
1307       int releaseLoad = atoi(param2.c_str());
1308       int popped = getOperatedHost()->popBurst(releaseLoad);
1309
1310       if(popped > 0) {
1311         opt_response_content = "burst popped for ";
1312         opt_response_content += anna::functions::entriesAsString(popped, "message");
1313       }
1314     } else if(param1 == "stop") {
1315       int left = getOperatedHost()->stopBurst();
1316
1317       if(left != -1) {
1318         opt_response_content += anna::functions::entriesAsString(left, "message");
1319         opt_response_content += " left to the end of the cycle";
1320       }
1321     } else if(param1 == "repeat") {
1322       if(param2 == "") param2 = "yes";
1323
1324       bool repeat = (param2 == "yes");
1325       getOperatedHost()->repeatBurst(repeat);
1326       opt_response_content += (repeat ? "repeat enabled" : "repeat disabled");
1327     } else if(param1 == "send") {
1328       if(param2 == "") throw anna::RuntimeException("Missing amount for burst send operation", ANNA_FILE_LOCATION);
1329
1330       int sent = getOperatedHost()->sendBurst(atoi(param2.c_str()));
1331
1332       if(sent > 0) {
1333         opt_response_content = "sent ";
1334         opt_response_content += anna::functions::entriesAsString(sent, "message");
1335       }
1336     } else if(param1 == "goto") {
1337       if(param2 == "") throw anna::RuntimeException("Missing order position for burst goto operation", ANNA_FILE_LOCATION);
1338
1339       opt_response_content = getOperatedHost()->gotoBurst(atoi(param2.c_str()));
1340     } else if(param1 == "look") {
1341       int order = ((param2 != "") ? atoi(param2.c_str()) : -1);
1342       opt_response_content = "\n\n";
1343       opt_response_content += getOperatedHost()->lookBurst(order);
1344     } else {
1345       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);
1346     }
1347
1348   } else if((opType == "test")) {
1349     // test|<id>|<command>[|parameters]         Add a new step to the test case ...
1350     // test|ttps|<amount>                       Starts/resume the provided number of time ticks per second (ttps). The ADML starts ...
1351     // test|next[|<sync-amount>]                Forces the execution of the next test case(s) without waiting for test manager tick ...
1352     // test|ip-limit[|amount]                   In-progress limit of test cases. No new test cases will be launched over this value ...
1353     // test|repeats|<amount>                    Restarts the whole programmed test list when finished the amount number of times ...
1354     // test|report|<initialized/in-progress/failed/success/[all]/none>[|[yes]|no]
1355     //                                          Enables/disables report generation for a certain test case state: initialized, in-progress ...
1356     // test|report-hex[|[yes]|no]               Reports could include the diameter messages in hexadecimal format. Disabled by default.
1357     // test|goto|<id>                           Updates current test pointer position.
1358     // test|look[|id]                           Show programmed test case for id provided, current when missing ...
1359     // test|state[|id]                          Show test case state for id provided, current when missing ...
1360     // test|interact|amount|id                  Makes interactive a specific test case id. The amount is the margin of execution steps ...
1361     // test|reset|<[soft]/hard>[|id]            Reset the test case for id provided, all the tests when missing ...
1362     // test|auto-reset|<soft|hard>              When cycling, current test cases can be soft (default) or hard reset ...
1363     // test|clear                               Clears all the programmed test cases.
1364     // test|summary                             Test manager general report (number of test cases, counts by state ...
1365
1366
1367     if(param1 == "ttps") {
1368       if (numParams > 2)
1369         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1370
1371       bool success = ((param2 != "") ? testManager.configureTTPS(atoi(param2.c_str())) : false);
1372       if (success) {
1373         opt_response_content = "assigned new test launch rate to ";
1374         opt_response_content += anna::functions::asString(atoi(param2.c_str()));
1375         opt_response_content += " events per second";
1376       }
1377       else {
1378         opt_response_content += "unable to configure the test rate provided";
1379       }
1380     }
1381     else if (param1 == "next") {
1382       if (numParams > 2)
1383         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1384
1385       int sync_amount = ((param2 != "") ? atoi(param2.c_str()) : 1);
1386
1387       if (sync_amount < 1)
1388         throw anna::RuntimeException("The parameter 'sync-amount' must be a positive integer value", ANNA_FILE_LOCATION);
1389
1390       bool success = testManager.execTestCases(sync_amount);
1391
1392       opt_response_content = (success ? "" : "not completely " /* completed cycle and no repeats, rare case */);
1393       opt_response_content += "processed ";
1394       opt_response_content += anna::functions::asString(sync_amount);
1395       opt_response_content += ((sync_amount > 1) ? " test cases synchronously" : " test case");
1396     }
1397     else if(param1 == "ip-limit") {
1398       if (numParams > 2)
1399         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1400
1401       unsigned int limit;
1402       if (param2 != "") {
1403         limit = atoi(param2.c_str());
1404         testManager.setInProgressLimit(limit);
1405         opt_response_content = "new in-progress limit: ";
1406         opt_response_content += (limit != UINT_MAX) ? anna::functions::asString(limit) : "<no limit>";
1407       }
1408       else {
1409         opt_response_content = "in-progress limit amount: ";
1410         limit = testManager.getInProgressLimit();
1411         opt_response_content += (limit != UINT_MAX) ? anna::functions::asString(limit) : "<no limit>";
1412         opt_response_content += "; currently there are ";
1413         opt_response_content += anna::functions::asString(testManager.getInProgressCount());
1414         opt_response_content += " test cases running";
1415       }
1416     }
1417     else if(param1 == "repeats") {
1418       if (numParams != 2)
1419         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1420       int repeats = atoi(param2.c_str());
1421       if (repeats < 0) repeats = -1;
1422       testManager.setPoolRepeats(repeats);
1423       std::string nolimit = (repeats != -1) ? "":" [no limit]";
1424       opt_response_content += anna::functions::asString("Pool repeats: %d%s (current cycle: %d)", repeats, nolimit.c_str(), testManager.getPoolCycle());
1425     }
1426     else if(param1 == "report") {
1427       if (numParams > 3)
1428         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1429
1430       if(param2 == "") param2 = "all";
1431       if(param3 == "") param3 = "yes";
1432       bool enable = (param3 == "yes");
1433
1434       if(param2 == "initialized")
1435         testManager.setDumpInitializedReports(enable);
1436       else if(param2 == "in-progress")
1437         testManager.setDumpInProgressReports(enable);
1438       else if(param2 == "failed")
1439         testManager.setDumpFailedReports(enable);
1440       else if(param2 == "success")
1441         testManager.setDumpSuccessReports(enable);
1442       else if(param2 == "all") {
1443         param2 = "any";
1444         testManager.setDumpAllReports(enable);
1445       }
1446       else if(param2 == "none") {
1447         enable = !enable;
1448         param2 = "any";
1449         testManager.setDumpAllReports(enable);
1450       }
1451       else
1452         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1453
1454       opt_response_content += (enable ? "report enabled " : "report disabled ");
1455       opt_response_content += "for tests in '";
1456       opt_response_content += param2;
1457       opt_response_content += "' state";
1458     }
1459     else if(param1 == "report-hex") {
1460       if (numParams > 2)
1461         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1462
1463       if(param2 == "") param2 = "yes";
1464       testManager.setDumpHex((param2 == "yes"));
1465       opt_response_content += (testManager.getDumpHex() ? "report includes hexadecimal messages" : "report excludes hexadecimal messages");
1466     }
1467     else if(param1 == "dump-stdout") {
1468       if (numParams > 2)
1469         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1470
1471       if(param2 == "") param2 = "yes";
1472       testManager.setDumpStdout((param2 == "yes"));
1473       opt_response_content += (testManager.getDumpHex() ? "test manager dumps progress into stdout" : "test manager does not dump progress into stdout");
1474     }
1475     else if(param1 == "goto") {
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 goto operation", ANNA_FILE_LOCATION);
1480       int id = atoi(param2.c_str());
1481       if (testManager.gotoTestCase(id)) {
1482         opt_response_content = "position updated 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 == "run") {
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       if(param2 == "") throw anna::RuntimeException("Missing id for test run operation", ANNA_FILE_LOCATION);
1495       int id = atoi(param2.c_str());
1496       if (testManager.runTestCase(id)) {
1497         opt_response_content = "test executed for id provided (";
1498       }
1499       else {
1500         opt_response_content = "cannot found test id (";
1501       }
1502       opt_response_content += anna::functions::asString(id);
1503       opt_response_content += ")";
1504     }
1505     else if(param1 == "look") {
1506       if (numParams > 2)
1507         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1508
1509       int id = ((param2 != "") ? atoi(param2.c_str()) : -1);
1510       anna::testing::TestCase *testCase = testManager.findTestCase(id);
1511
1512       if (testCase) {
1513         response_content = testCase->asXMLString();
1514         return true; // OK
1515       }
1516       else {
1517         result = false;
1518         if (id == -1) {
1519           opt_response_content = "no current test case detected (testing started ?)";
1520         }
1521         else {
1522           opt_response_content = "cannot found test id (";
1523           opt_response_content += anna::functions::asString(id);
1524           opt_response_content += ")";
1525         }
1526       }
1527     }
1528     else if(param1 == "state") {
1529       if (numParams > 2)
1530         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1531
1532       int id = ((param2 != "") ? atoi(param2.c_str()) : -1);
1533       anna::testing::TestCase *testCase = testManager.findTestCase(id);
1534
1535       if (testCase) {
1536         response_content = anna::testing::TestCase::asText(testCase->getState());
1537         return testCase->isSuccess();
1538       }
1539       else {
1540         result = false;
1541         if (id == -1) {
1542           opt_response_content = "no current test case detected (testing started ?)";
1543         }
1544         else {
1545           opt_response_content = "cannot found test id (";
1546           opt_response_content += anna::functions::asString(id);
1547           opt_response_content += ")";
1548         }
1549       }
1550     }
1551     else if (param1 == "interact") {
1552       if (numParams != 2)
1553         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1554
1555       int amount = atoi(param2.c_str());
1556       if (amount < -1)
1557         throw anna::RuntimeException("Interactive amount must be -1 (to disable interactive mode) or a positive number.", ANNA_FILE_LOCATION);
1558
1559       int id = ((param3 != "") ? atoi(param3.c_str()) : -1);
1560       anna::testing::TestCase *testCase = testManager.findTestCase(id);
1561       if (testCase) {
1562         if (amount == -1) {
1563           testCase->makeInteractive(false);
1564           opt_response_content = "interactive mode disabled";
1565         }
1566         else {
1567           testCase->addInteractiveAmount(amount);
1568           opt_response_content = "added interactive amount of ";
1569           opt_response_content += anna::functions::asString(amount);
1570           opt_response_content += " units";
1571           if (amount == 0) opt_response_content += " (0: freezing a non-interactive testcase, no effect on already interactive)";
1572         }
1573         opt_response_content += " for test case id ";
1574         opt_response_content += anna::functions::asString(id);
1575       }
1576       else {
1577         result = false;
1578         opt_response_content = "cannot found test id (";
1579         opt_response_content += anna::functions::asString(id);
1580         opt_response_content += ")";
1581       }
1582     }
1583     else if(param1 == "reset") {
1584       if (numParams > 3)
1585         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1586
1587       if(param2 == "") param2 = "soft";
1588      if (param2 != "soft" && param2 != "hard")
1589         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1590
1591       int id = ((param3 != "") ? atoi(param3.c_str()) : -1);
1592       anna::testing::TestCase *testCase = ((id != -1) ? testManager.findTestCase(id) : NULL);
1593
1594       if (testCase) {
1595         bool done = testCase->reset(param2 == "hard");
1596         opt_response_content = "test ";
1597         opt_response_content += param2;
1598         opt_response_content += " reset for id ";
1599         opt_response_content += anna::functions::asString(id);
1600         opt_response_content += done ? ": done": ": not done";
1601       }
1602       else {
1603         if (id == -1) {
1604           bool anyReset = testManager.resetPool(param2 == "hard");
1605           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";
1606         }
1607         else {
1608           result = false;
1609           opt_response_content = "cannot found test id (";
1610           opt_response_content += anna::functions::asString(id);
1611           opt_response_content += ")";
1612         }
1613       }
1614     }
1615     else if(param1 == "auto-reset") {
1616       if (numParams != 2)
1617         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1618
1619       if (param2 != "soft" && param2 != "hard")
1620         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1621
1622       testManager.setAutoResetHard(param2 == "hard");
1623       opt_response_content += anna::functions::asString("Auto-reset configured to '%s'", param2.c_str());
1624     }
1625     else if(param1 == "initialized") {
1626       if (numParams > 1)
1627         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1628
1629       opt_response_content = anna::functions::asString("%lu", testManager.getInitializedCount());
1630     }
1631     else if(param1 == "finished") {
1632       if (numParams > 1)
1633         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1634
1635       opt_response_content = anna::functions::asString("%lu", testManager.getFinishedCount());
1636     }
1637     else if(param1 == "clear") {
1638       if (numParams > 1)
1639         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1640
1641       if (testManager.clearPool()) {
1642         opt_response_content = "all the programmed test cases have been dropped";
1643       }
1644       else {
1645         opt_response_content = "there are not programmed test cases to be removed";
1646       }
1647     }
1648     else if(param1 == "junit") {
1649       response_content = testManager.junitAsXMLString();
1650       return true; // OK
1651     }
1652     else if(param1 == "summary-counts") {
1653       response_content = testManager.summaryCounts();
1654       return true; // OK
1655     }
1656     else if(param1 == "summary-states") {
1657       response_content = testManager.summaryStates();
1658       return true; // OK
1659     }
1660     else if(param1 == "summary") {
1661       response_content = testManager.asXMLString();
1662       return true; // OK
1663     }
1664     else {
1665       int id = atoi(param1.c_str());
1666       if(id < 0)
1667         throw anna::RuntimeException("Invalid test case identifier: must be a non-negative number", ANNA_FILE_LOCATION);
1668
1669       // PARAM: 1     2            3      4          5           6             7           8          9       10         11
1670       // test|<id>|<command>
1671       //             description|<description>
1672       //             ip-limit[|<iplimit>]
1673       //             timeout|    <msecs>
1674       //             sendxml2e|  <file>[|<step number>]
1675       //             sendxml2c|  <file>[|<step number>]
1676       //             delay|      [msecs]
1677       //             wait<fe/fc>|[code]|[bitR]|[hopByHop]|[applicationId]|[sessionId]|[resultCode]|[msisdn]|[imsi]|[serviceContextId]
1678       //      wait<fe/fc>-answer|<step number>
1679       //      wait<fe/fc>-xml   |<source_file>[|strict]
1680       //      wait<fe/fc>-hex   |<source_file>[|strict]
1681       if(param2 == "") throw anna::RuntimeException("Missing command for test id operation", ANNA_FILE_LOCATION);
1682
1683       // Commands:
1684       if (param2 == "description") {
1685         if (numParams > 3)
1686           throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1687         if(param3 == "") throw anna::RuntimeException("Missing description for test case", ANNA_FILE_LOCATION);
1688         testManager.getTestCase(id)->setDescription(param3); // creates / reuses
1689       }
1690       else if (param2 == "ip-limit") {
1691         if (numParams > 3)
1692           throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1693         unsigned int limit = (param3 == "") ? 1 : atoi(param3.c_str());
1694         testManager.getTestCase(id)->addIpLimit(limit); // creates / reuses
1695       }
1696       else if (param2 == "timeout") {
1697         if (numParams > 3)
1698           throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1699         if(param3 == "") throw anna::RuntimeException("Missing milliseconds for 'timeout' command in test id operation", ANNA_FILE_LOCATION);
1700         anna::Millisecond timeout = checkTimeMeasure("Test case timeout", param3);
1701         testManager.getTestCase(id)->addTimeout(timeout); // creates / reuses
1702       }
1703       else if ((param2 == "sendxml2e")||(param2 == "sendxml2c")) {
1704         if (numParams > 4)
1705           throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1706         if(param3 == "") throw anna::RuntimeException(anna::functions::asString("Missing xml file for '%s' command in test id operation", param2.c_str()), ANNA_FILE_LOCATION);
1707         codecMsg.loadXMLFile(param3);
1708         LOGWARNING(
1709           if (!codecMsg.isRequest() && (param4 == ""))
1710             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);
1711         );
1712
1713         updateOperatedOriginHostWithMessage(codecMsg);
1714         int stepNumber = ((param4 != "") ? atoi(param4.c_str()):-1);
1715
1716         if (param2 == "sendxml2e")
1717           testManager.getTestCase(id)->addSendDiameterXml2e(codecMsg.code(), getOperatedHost(), stepNumber); // creates / reuses
1718         else
1719           testManager.getTestCase(id)->addSendDiameterXml2c(codecMsg.code(), getOperatedHost(), stepNumber); // creates / reuses
1720       }
1721       else if (param2 == "delay") {
1722         if (numParams > 3)
1723           throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1724         if(param3 == "") throw anna::RuntimeException("Missing milliseconds for 'delay' command in test id operation", ANNA_FILE_LOCATION);
1725         anna::Millisecond delay = ((param3 == "0" /* special case */) ? (anna::Millisecond)0 : checkTimeMeasure("Test case delay step", param3));
1726         testManager.getTestCase(id)->addDelay(delay); // creates / reuses
1727       }
1728
1729 // TODO(***)
1730 //                                        The way to identify the test case, is through registered Session-Id values for
1731 //                                         programmed requests. But this depends on the type of node. Acting as clients,
1732 //                                         requests received have Session-Id values which are already registered with
1733 //                                         one test case, causing an error if not found. Acting as servers, requests are
1734 //                                         received over a diameter local server from a client which are generating that
1735 //                                         Session-Id values. Then we know nothing about such values. The procedure in
1736 //                                         this case is find out a test case not-started containing a condition which
1737 //                                         comply with the incoming message, and reactivates it.
1738 // The other solution: register Session-Id values for answers send to client from a local diameter server.
1739       else if ((param2 == "waitfe")||(param2 == "waitfc")) {
1740         if (numParams > 11)
1741           throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1742         if (param3 != "" || param4 != "" || param5 != "" || param6 != "" || param7 != "" || param8 != "" || param9 != "" || param10 != "" || param11 != "") {
1743           bool fromEntity = (param2.substr(4,2) == "fe");
1744           testManager.getTestCase(id)->addWaitDiameter(fromEntity, param3, param4, param5, param6, param7, param8, param9, param10, param11);
1745         }
1746         else {
1747           throw anna::RuntimeException(anna::functions::asString("Missing condition for '%s' command in test id operation", param2.c_str()), ANNA_FILE_LOCATION);
1748         }
1749       }
1750       else if ((param2 == "waitfe-hex")||(param2 == "waitfc-hex")) {
1751         if (numParams > 4)
1752           throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1753         if(param3 == "") throw anna::RuntimeException(anna::functions::asString("Missing hex file for '%s' command in test id operation", param2.c_str()), ANNA_FILE_LOCATION);
1754
1755         // Get DataBlock from file with hex content:
1756         if(!getDataBlockFromHexFile(param3, db_aux))
1757           throw anna::RuntimeException("Error reading hex content from file provided", ANNA_FILE_LOCATION);
1758
1759         // Hexadecimal representation read from file:
1760         std::string regexp = anna::functions::asHexString(db_aux);
1761
1762         // optional 'full':
1763         if(param4 != "strict") {
1764           //// If request, we will ignore sequence data:
1765           //if (anna::diameter::codec::functions::requestBit(db_aux))
1766             regexp.replace (24, 16, "[0-9A-Fa-f]{16}");
1767
1768           regexp.insert(0, "^");
1769           regexp += "$";
1770         }
1771
1772         bool fromEntity = (param2.substr(4,2) == "fe");
1773         testManager.getTestCase(id)->addWaitDiameterRegexpHex(fromEntity, regexp);
1774       }
1775       else if ((param2 == "waitfe-xml")||(param2 == "waitfc-xml")) {
1776         if (numParams > 4)
1777           throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1778         if(param3 == "") throw anna::RuntimeException(anna::functions::asString("Missing xml file for '%s' command in test id operation", param2.c_str()), ANNA_FILE_LOCATION);
1779
1780         // Get xml content from file:
1781         std::string regexp;
1782         if(!getContentFromFile(param3, regexp))
1783           throw anna::RuntimeException("Error reading xml content from file provided", ANNA_FILE_LOCATION);
1784
1785         // optional 'full':
1786         if(param4 != "strict") {
1787
1788           // TODO: use this from gcc4.9.0: http://stackoverflow.com/questions/8060025/is-this-c11-regex-error-me-or-the-compiler
1789 /*
1790           std::string s_from = "hop-by-hop-id=\"[0-9]+\" end-to-end-id=\"[0-9]+\"";
1791           std::string s_to = s_from;
1792           std::string s_from2 = "avp name=\"Origin-State-Id\" data=\"[0-9]+\"";
1793           std::string s_to2 = s_from2;
1794
1795           try {
1796             regexp = std::regex_replace (regexp, std::regex(s_from), s_to);
1797             regexp = std::regex_replace (regexp, std::regex(s_from2), s_to2);
1798           }
1799           catch (const std::regex_error& e) {
1800             throw anna::RuntimeException(e.what(), ANNA_FILE_LOCATION);
1801           }
1802
1803 */
1804           std::string::size_type pos, pos_1, pos_2;
1805
1806           pos = regexp.find("hop-by-hop-id=", 0u);
1807           pos = regexp.find("\"", pos);
1808           pos_1 = pos;
1809           pos = regexp.find("\"", pos+1);
1810           pos_2 = pos;
1811           regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
1812
1813           pos = regexp.find("end-to-end-id=", 0u);
1814           pos = regexp.find("\"", pos);
1815           pos_1 = pos;
1816           pos = regexp.find("\"", pos+1);
1817           pos_2 = pos;
1818           regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
1819
1820           pos = regexp.find("Origin-State-Id", 0u);
1821           pos = regexp.find("\"", pos);
1822           pos = regexp.find("\"", pos+1);
1823           pos_1 = pos;
1824           pos = regexp.find("\"", pos+1);
1825           pos_2 = pos;
1826           regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
1827
1828           //regexp.insert(0, "^");
1829           //regexp += "$";
1830         }
1831
1832         bool fromEntity = (param2.substr(4,2) == "fe");
1833         testManager.getTestCase(id)->addWaitDiameterRegexpXml(fromEntity, regexp);
1834       }
1835       else if (param2 == "sh-command") {
1836         // Allow pipes in command:
1837         //if (numParams > 4)
1838         //  throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1839         if(param3 == "") throw anna::RuntimeException("Missing script/executable command-line for 'sh-command' in test id operation", ANNA_FILE_LOCATION);
1840         std::string token = "|sh-command|";
1841         std::string command = operation.substr(operation.find(token) + token.size());
1842         testManager.getTestCase(id)->addCommand(command); // creates / reuses
1843       }
1844       else {
1845         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1846       }
1847     }
1848
1849   } else if((opType == "sendxml2c") || (opType == "sendhex2c")) {
1850     anna::diameter::comm::Message *msg;
1851
1852     if(opType == "sendxml2c") {
1853       codecMsg.loadXMLFile(param1);
1854       updateOperatedOriginHostWithMessage(codecMsg);
1855       msg = getOperatedHost()->createCommMessage();
1856       msg->clearBody();
1857       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)
1858
1859       msg->setBody(codecMsg.code());
1860     } else {
1861       // Get DataBlock from file with hex content:
1862       if(!getDataBlockFromHexFile(param1, db_aux))
1863         throw anna::RuntimeException("Error reading hex file provided", ANNA_FILE_LOCATION);
1864       msg = getOperatedHost()->createCommMessage();
1865       msg->setBody(db_aux);
1866     }
1867
1868     bool success = getOperatedServer()->send(msg);
1869     getOperatedHost()->releaseCommMessage(msg);
1870
1871     // Detailed log:
1872     if(getOperatedHost()->logEnabled()) {
1873       anna::diameter::comm::ServerSession *usedServerSession = getOperatedServer()->getLastUsedResource();
1874       std::string detail = usedServerSession ? usedServerSession->asString() : "<null server session>"; // shouldn't happen
1875       getOperatedHost()->writeLogFile(codecMsg, (success ? "sent2c" : "send2cError"), detail);
1876     }
1877   } else if(opType == "loadxml") {
1878     codecMsg.loadXMLFile(param1);
1879     response_content = codecMsg.asXMLString();
1880     return true; // OK
1881   } else if(opType == "diameterServerSessions") {
1882     int diameterServerSessions = atoi(param1.c_str());
1883     getOperatedServer()->setMaxConnections(diameterServerSessions);
1884
1885   } else if(opType == "answerxml2c") {
1886     if(param1 == "") { // programmed answers FIFO's to stdout
1887       response_content = getOperatedServer()->getReactingAnswers()->asString("ANSWERS TO CLIENT");
1888       return true; // OK
1889     } else if (param1 == "rotate") {
1890       getOperatedServer()->getReactingAnswers()->rotate(true);
1891     } else if (param1 == "exhaust") {
1892       getOperatedServer()->getReactingAnswers()->rotate(false);
1893     } else if (param1 == "clear") {
1894       getOperatedServer()->getReactingAnswers()->clear();
1895     } else if (param1 == "dump") {
1896       getOperatedServer()->getReactingAnswers()->dump("programmed_answer");
1897     } else {
1898       codecMsg.loadXMLFile(param1);
1899       updateOperatedOriginHostWithMessage(codecMsg);
1900       anna::diameter::codec::Message *message = getOperatedHost()->getCodecEngine()->createMessage(param1); // loads xml again, lesser of two evils
1901       LOGDEBUG(anna::Logger::debug(message->asXMLString(), ANNA_FILE_LOCATION));
1902
1903       if(message->isRequest())
1904         throw anna::RuntimeException("Cannot program diameter requests. Answer type must be provided", ANNA_FILE_LOCATION);
1905
1906       int code = message->getId().first;
1907       LOGDEBUG(anna::Logger::debug("Adding a new programed 'answer to client' to the FIFO queue corresponding to its message code ...", ANNA_FILE_LOCATION));
1908       getOperatedServer()->getReactingAnswers()->addMessage(code, message);
1909     }
1910   } else if(opType == "answerxml2e") {
1911
1912     if(param1 == "") { // programmed answers FIFO's to stdout
1913       response_content = getOperatedEntity()->getReactingAnswers()->asString("ANSWERS TO ENTITY");
1914       return true; // OK
1915     } else if (param1 == "rotate") {
1916       getOperatedEntity()->getReactingAnswers()->rotate(true);
1917     } else if (param1 == "exhaust") {
1918       getOperatedEntity()->getReactingAnswers()->rotate(false);
1919     } else if (param1 == "clear") {
1920       getOperatedEntity()->getReactingAnswers()->clear();
1921     } else if (param1 == "dump") {
1922       getOperatedEntity()->getReactingAnswers()->dump("programmed_answer");
1923     } else {
1924       codecMsg.loadXMLFile(param1);
1925       updateOperatedOriginHostWithMessage(codecMsg);
1926       anna::diameter::codec::Message *message = getOperatedHost()->getCodecEngine()->createMessage(param1); // loads xml again, lesser of two evils
1927       LOGDEBUG(anna::Logger::debug(message->asXMLString(), ANNA_FILE_LOCATION));
1928
1929       if(message->isRequest())
1930         throw anna::RuntimeException("Cannot program diameter requests. Answer type must be provided", ANNA_FILE_LOCATION);
1931
1932       int code = message->getId().first;
1933       LOGDEBUG(anna::Logger::debug("Adding a new programed 'answer to entity' to the FIFO queue corresponding to its message code ...", ANNA_FILE_LOCATION));
1934       getOperatedEntity()->getReactingAnswers()->addMessage(code, message);
1935     }
1936   } else {
1937     throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1938   }
1939
1940   // HTTP response
1941   response_content = "Operation correctly processed: "; response_content += operation;
1942   if (opt_response_content != "") {
1943     response_content += " => ";
1944     response_content += opt_response_content;
1945   }
1946
1947   return result;
1948 }
1949
1950 anna::xml::Node* Launcher::asXML(anna::xml::Node* parent) const
1951 throw() {
1952   anna::xml::Node* result = parent->createChild("launcher");
1953   anna::comm::Application::asXML(result);
1954   // Timming:
1955   result->createAttribute("StartTime", a_start_time.asString());
1956   result->createAttribute("InitialWorkingDirectory", a_initialWorkingDirectory);
1957   result->createAttribute("SecondsLifeTime", anna::time::functions::lapsedMilliseconds() / 1000);
1958   // Diameter:
1959   anna::diameter::comm::OriginHostManager &ohm = anna::diameter::comm::OriginHostManager::instantiate();
1960   for (diameter::comm::origin_hosts_it it = ohm.begin(); it != ohm.end(); it++) {
1961     it->second->asXML(result);
1962   }
1963
1964   // Registered codec engines:
1965   anna::diameter::codec::EngineManager &em = anna::diameter::codec::EngineManager::instantiate();
1966   em.asXML(result);
1967
1968   // OAM & statistics:
1969   oamAsXML(result);
1970   statsAsXML(result);
1971
1972   // Testing: could be heavy if test case reports are enabled
1973   anna::testing::TestManager::instantiate().asXML(result);
1974
1975   return result;
1976 }
1977
1978 anna::xml::Node* Launcher::oamAsXML(anna::xml::Node* parent) const
1979 throw() {
1980   anna::xml::Node* result = parent->createChild("Oam");
1981
1982   // OAM:
1983   anna::diameter::comm::OamModule::instantiate().asXML(result);
1984   anna::diameter::comm::ApplicationMessageOamModule::instantiate().asXML(result);
1985   anna::diameter::codec::OamModule::instantiate().asXML(result);
1986
1987   return result;
1988 }
1989
1990 anna::xml::Node* Launcher::statsAsXML(anna::xml::Node* parent) const
1991 throw() {
1992   anna::xml::Node* result = parent->createChild("Statistics");
1993
1994   // Statistics:
1995   anna::statistics::Engine::instantiate().asXML(result);
1996
1997   return result;
1998 }