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