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