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