Fix local server for multiple applications
[anna.git] / example / diameter / launcher / EventOperation.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 <string>
11
12 // Process
13 #include <EventOperation.hpp>
14 #include <Launcher.hpp>
15 #include <Procedure.hpp>
16 #include <MyDiameterEngine.hpp>
17 #include <MyLocalServer.hpp>
18 #include <anna/testing/TestManager.hpp>
19
20 // Standard
21 #include <fstream>
22 #include <unistd.h> // chdir
23
24 // Project
25 #include <anna/diameter.comm/OriginHost.hpp>
26 #include <anna/json/functions.hpp>
27 #include <anna/diameter/codec/Message.hpp>
28 #include <anna/diameter/helpers/base/functions.hpp>
29 #include <anna/time/functions.hpp>
30 #include <anna/core/functions.hpp>
31 #include <anna/xml/xml.hpp>
32 #include <anna/diameter.comm/Message.hpp>
33
34
35 /////////////////////
36 // Node management //
37 /////////////////////
38 bool EventOperation::node(std::string &response, const std::string & name) {
39
40   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
41
42   anna::diameter::comm::OriginHost *workingNode;
43   try { workingNode = my_app.getWorkingNode(); } catch(anna::RuntimeException &ex) { ex.trace(); }
44
45   if (name != "") {
46     if (my_app.setWorkingNode(name)) {
47       response = anna::functions::asString("Forced node is now '%s'", name.c_str());
48       my_app.setOperatedHost(my_app.getWorkingNode()); // now is the new one
49     }
50     else {
51       response = anna::functions::asString("Node '%s' invalid. Nothing done", name.c_str());
52     }
53   }
54   else {
55     if (workingNode) {
56       if (a_http) {
57         response = anna::functions::encodeBase64(workingNode->asXMLString());
58       }
59       else {
60         response = workingNode->asXMLString();
61       }
62     }
63     else {
64       response = "Working node is automatic";
65     }
66   }
67   return true; // OK
68 }
69
70 bool EventOperation::node_auto(std::string &response) {
71
72   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
73
74   my_app.setNodeAuto();
75   response = "Working node has been set to automatic";
76
77   return true; // OK
78 }
79
80 ////////////////////////
81 // Parsing operations //
82 ////////////////////////
83 bool EventOperation::code(std::string &response, const std::string & diameterJson) {
84
85   bool success;
86   std::string diameterXml = anna::json::functions::json2xml(diameterJson, success);
87   if (!success) {
88     response += "json to xml failed, unable to encode !";
89     return false;
90   }
91   anna::diameter::codec::Message codecMsg; // auxiliary codec message
92   codecMsg.loadXMLString(diameterXml);
93   response = anna::functions::asHexString(codecMsg.code());
94
95   return true; // OK
96 }
97
98 bool EventOperation::decode(std::string &response, const std::string & diameterHex) {
99
100   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
101
102   anna::DataBlock db_aux(true);
103   anna::functions::fromHexString(diameterHex, db_aux);
104   anna::diameter::codec::Message codecMsg; // auxiliary codec message
105   try {
106     codecMsg.decode(db_aux);
107     std::string xmlString = codecMsg.asXMLString();
108     response = anna::functions::encodeBase64(xmlString);
109   }
110   catch(anna::RuntimeException &ex) { ex.trace(); }
111
112   return true; // OK
113 }
114
115 bool EventOperation::loadmsg(std::string &response, const std::string & diameterJson) {
116
117   bool success;
118   std::string diameterXml = anna::json::functions::json2xml(diameterJson, success);
119   if (!success) {
120     response += "json to xml failed, unable to load message !";
121     return false;
122   }
123   anna::diameter::codec::Message codecMsg; // auxiliary codec message
124   codecMsg.loadXMLString(diameterXml);
125   response = anna::functions::encodeBase64(codecMsg.asXMLString());
126
127   return true; // OK
128 }
129
130 /////////////////
131 // Hot changes //
132 /////////////////
133 bool EventOperation::services(std::string &response, const std::string & servicesJson) {
134
135   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
136
137   bool success;
138   std::string servicesXml = anna::json::functions::json2xml(servicesJson, success);
139   if (!success) {
140     response += "json to xml failed, unable to load services !";
141     return false;
142   }
143
144   try {
145     my_app.loadServicesFromXMLString(servicesXml, true /* bind entities */);
146   }
147   catch(anna::RuntimeException &ex) {
148     ex.trace();
149     response += "loaded services with errors";
150     return false;
151   }
152   response = "loaded services correctly";
153
154   return true; // OK
155 }
156
157 bool EventOperation::diameterServerSessions(std::string &response, int sessions) {
158
159   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
160
161   try {
162     my_app.getOperatedServer()->setMaxConnections(sessions);
163   }
164   catch(anna::RuntimeException &ex) {
165     ex.trace();
166     response += "fail to operate the server";
167     return false;
168   }
169   response = "new sessions successfully established on operated diameter server";
170
171   return true; // OK
172 }
173
174 bool EventOperation::change_dir(std::string &response, const std::string & directory) {
175
176   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
177
178   std::string dir = directory;
179   if (dir == "") dir = my_app.getInitialWorkingDirectory();
180   bool result = (chdir(dir.c_str()) == 0);
181
182   if (result)
183     response = "New execution directory configured: ";
184   else
185     response = "Cannot assign provided execution directory: ";
186
187   response += dir;
188   return result;
189 }
190
191 ////////////////////////////////
192 // Client sessions visibility //
193 ////////////////////////////////
194 bool EventOperation::visibility(std::string &response, const std::string & action, const std::string &addressPort, int socket) {
195
196   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
197
198   response = "";
199
200   if(addressPort != "") {
201     if(socket != -1) {
202       std::string key = addressPort;
203       key += "|";
204       key += anna::functions::asString(socket);
205
206       if(action == "hide") my_app.getOperatedEngine()->findClientSession(key)->hide();
207       if(action == "show") my_app.getOperatedEngine()->findClientSession(key)->show();
208       if(action == "hidden") response = my_app.getOperatedEngine()->findClientSession(key)->hidden() ? "true" : "false";
209       if(action == "shown") response = my_app.getOperatedEngine()->findClientSession(key)->shown() ? "true" : "false";
210     } else {
211       std::string address;
212       int port;
213       anna::functions::getAddressAndPortFromSocketLiteral(addressPort, address, port);
214
215       if(action == "hide") my_app.getOperatedEngine()->findServer(address, port)->hide();
216       if(action == "show") my_app.getOperatedEngine()->findServer(address, port)->show();
217       if(action == "hidden") response = my_app.getOperatedEngine()->findServer(address, port)->hidden() ? "true" : "false";
218       if(action == "shown") response = my_app.getOperatedEngine()->findServer(address, port)->shown() ? "true" : "false";
219     }
220   } else {
221     if(action == "hide") my_app.getOperatedEntity()->hide();
222     if(action == "show") my_app.getOperatedEntity()->show();
223     if(action == "hidden") response = my_app.getOperatedEntity()->hidden() ? "true" : "false";
224     if(action == "shown") response = my_app.getOperatedEntity()->shown() ? "true" : "false";
225   }
226
227   return true; // OK
228 }
229
230
231 ///////////////
232 // Snapshots //
233 ///////////////
234 bool EventOperation::collect(std::string &response) {
235
236   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
237
238   my_app.resetCounters();
239   my_app.resetStatistics();
240   response = "All process counters & statistic information have been reset";
241
242   return true; // OK
243 }
244
245 bool EventOperation::context(std::string &response, const std::string & targetFile) {
246
247   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
248
249   std::string contextFile = (targetFile != "") ? targetFile : anna::functions::asString("/var/tmp/anna.context.%05d", my_app.getPid());
250   my_app.writeContext(contextFile);
251   response = anna::functions::asString("Context dumped on file '%s'", contextFile.c_str());
252
253   return true; // OK
254 }
255
256 bool EventOperation::forceCountersRecord(std::string &response) {
257
258   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
259
260   my_app.forceCountersRecord();
261   response = "Current counters have been dump to disk";
262
263   return true; // OK
264 }
265
266 bool EventOperation::log_statistics_samples(std::string &response, const std::string & list) {
267
268   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
269
270   my_app.logStatisticsSamples(list);
271   response = anna::functions::asString("Log statistics samples for '%s' concepts", list.c_str());
272
273   return true; // OK
274 }
275
276 bool EventOperation::show_oam(std::string &response) {
277
278   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
279
280   anna::xml::Node root("root");
281   response = anna::xml::Compiler().apply(my_app.oamAsXML(&root));
282   if (a_http)
283      response = anna::functions::encodeBase64(response);
284
285   return true; // OK
286 }
287
288 bool EventOperation::show_stats(std::string &response) {
289
290   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
291
292   anna::xml::Node root("root");
293   response = anna::xml::Compiler().apply(my_app.statsAsXML(&root));
294   if (a_http)
295      response = anna::functions::encodeBase64(response);
296
297   return true; // OK
298 }
299
300 /////////////////////
301 // Flow operations //
302 /////////////////////
303 bool EventOperation::sendmsg_hex_2e(std::string &response, const std::string & diameterJson_or_Hex, bool msg_or_hex) {
304
305   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
306   anna::diameter::codec::Message codecMsg; // auxiliary codec message
307   bool success;
308   anna::diameter::comm::Message *msg;
309
310   if(msg_or_hex) {
311     std::string diameterXml = anna::json::functions::json2xml(diameterJson_or_Hex, success);
312     if (!success) {
313       response += "json to xml failed, unable to send message !";
314       return false;
315     }
316     codecMsg.loadXMLString(diameterXml);
317     try {
318       my_app.updateOperatedOriginHostWithMessage(codecMsg);
319       msg = my_app.getOperatedHost()->createCommMessage();
320       msg->clearBody();
321     }
322     catch(anna::RuntimeException &ex) {
323       ex.trace();
324       response += "invalid operated host";
325       return false;
326     }
327     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)
328     msg->setBody(codecMsg.code());
329   } else {
330     // Get DataBlock from hex content:
331     anna::DataBlock db_aux(true);
332     std::string hexString = diameterJson_or_Hex;
333     hexString.erase(std::remove(hexString.begin(), hexString.end(), ':'), hexString.end());
334     LOGDEBUG(
335         std::string msg = "Hex string (remove colons if exists): ";
336     msg += hexString;
337     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
338     );
339     anna::functions::fromHexString(hexString, db_aux); // could launch exception
340     try {
341       my_app.updateOperatedOriginHostWithMessage(db_aux);
342       msg = my_app.getOperatedHost()->createCommMessage();
343     }
344     catch(anna::RuntimeException &ex) {
345       ex.trace();
346       response += "invalid operated host";
347       return false;
348     }
349     msg->setBody(db_aux);
350     try { if(my_app.getOperatedHost()->logEnabled()) codecMsg.decode(db_aux); } catch(anna::RuntimeException &ex) { ex.trace(); }
351   }
352
353   success = my_app.getOperatedEntity()->send(msg);
354   my_app.getOperatedHost()->releaseCommMessage(msg);
355
356   // Detailed log:
357   if(my_app.getOperatedHost()->logEnabled()) {
358     anna::diameter::comm::Server *usedServer = my_app.getOperatedEntity()->getLastUsedResource();
359     anna::diameter::comm::ClientSession *usedClientSession = usedServer ? usedServer->getLastUsedResource() : NULL;
360     std::string detail = usedClientSession ? usedClientSession->asString() : "[null client session]"; // shouldn't happen
361     my_app.getOperatedHost()->writeLogFile(codecMsg, (success ? "sent2e" : "send2eError"), detail);
362   }
363
364
365   response = "Operation processed"; // could be failed
366   return success;
367 }
368
369 bool EventOperation::sendmsg_hex_2c(std::string &response, const std::string & diameterJson_or_Hex, bool msg_or_hex) {
370
371   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
372   anna::diameter::codec::Message codecMsg; // auxiliary codec message
373   bool success;
374   anna::diameter::comm::Message *msg;
375
376   if(msg_or_hex) {
377     std::string diameterXml = anna::json::functions::json2xml(diameterJson_or_Hex, success);
378     if (!success) {
379       response += "json to xml failed, unable to send message !";
380       return false;
381     }
382     codecMsg.loadXMLString(diameterXml);
383     try {
384       my_app.updateOperatedOriginHostWithMessage(codecMsg);
385       msg = my_app.getOperatedHost()->createCommMessage();
386       msg->clearBody();
387     }
388     catch(anna::RuntimeException &ex) {
389       ex.trace();
390       response += "invalid operated host";
391       return false;
392     }
393     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)
394     msg->setBody(codecMsg.code());
395   } else {
396     // Get DataBlock from hex content:
397     anna::DataBlock db_aux(true);
398     std::string hexString = diameterJson_or_Hex;
399     hexString.erase(std::remove(hexString.begin(), hexString.end(), ':'), hexString.end());
400     LOGDEBUG(
401         std::string msg = "Hex string (remove colons if exists): ";
402     msg += hexString;
403     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
404     );
405     anna::functions::fromHexString(hexString, db_aux); // could launch exception
406     try {
407       my_app.updateOperatedOriginHostWithMessage(db_aux);
408       msg = my_app.getOperatedHost()->createCommMessage();
409     }
410     catch(anna::RuntimeException &ex) {
411       ex.trace();
412       response += "invalid operated host";
413       return false;
414     }
415     msg->setBody(db_aux);
416     try { if(my_app.getOperatedHost()->logEnabled()) codecMsg.decode(db_aux); } catch(anna::RuntimeException &ex) { ex.trace(); }
417   }
418
419   success = my_app.getOperatedServer()->send(msg);
420   my_app.getOperatedHost()->releaseCommMessage(msg);
421
422   // Detailed log:
423   if(my_app.getOperatedHost()->logEnabled()) {
424     anna::diameter::comm::ServerSession *usedServerSession = my_app.getOperatedServer()->getLastUsedResource();
425     std::string detail = usedServerSession ? usedServerSession->asString() : "[null server session]"; // shouldn't happen
426     my_app.getOperatedHost()->writeLogFile(codecMsg, (success ? "sent2c" : "send2cError"), detail);
427   }
428
429
430   response = "Operation processed"; // could be failed
431   return success;
432 }
433
434 bool EventOperation::answermsg_action_2e(std::string &response, const std::string & diameterJson_or_action, bool msg_or_action) {
435
436   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
437   anna::diameter::codec::Message codecMsg; // auxiliary codec message
438   anna::diameter::codec::Message *message;
439   bool success;
440
441   if (msg_or_action) {
442
443     std::string diameterXml = anna::json::functions::json2xml(diameterJson_or_action, success);
444     if (!success) {
445       response += "json to xml failed, unable to send message !";
446       return false;
447     }
448     codecMsg.loadXMLString(diameterXml);
449     try {
450       my_app.updateOperatedOriginHostWithMessage(codecMsg);
451       message = my_app.getOperatedHost()->getCodecEngine()->createMessage(diameterXml, false /* is xml string */); // loads xml again, lesser of two evils
452       LOGDEBUG(anna::Logger::debug(message->asXMLString(), ANNA_FILE_LOCATION));
453     }
454     catch(anna::RuntimeException &ex) {
455       ex.trace();
456       response += "invalid operated host";
457       return false;
458     }
459
460     if(message->isRequest()) {
461       response += "cannot program diameter requests. Answer type must be provided";
462       return false;
463     }
464
465     int code = message->getId().first;
466     LOGDEBUG(anna::Logger::debug("Adding a new programed 'answer to entity' to the FIFO queue corresponding to its message code ...", ANNA_FILE_LOCATION));
467     response = "Added 'answer to entity' to the FIFO queue corresponding to its message code";
468     my_app.getOperatedHost()->getReactingAnswers()->addMessage(code, message);
469   }
470   else { // action
471
472     if(diameterJson_or_action == "list") { // programmed answers FIFO's to stdout
473       response = anna::functions::encodeBase64(my_app.getOperatedHost()->getReactingAnswers()->asString("ANSWERS TO ENTITY"));
474     } else if (diameterJson_or_action == "rotate") {
475       my_app.getOperatedHost()->getReactingAnswers()->rotate(true);
476       response = "rotate";
477     } else if (diameterJson_or_action == "exhaust") {
478       my_app.getOperatedHost()->getReactingAnswers()->rotate(false);
479       response = "exhaust";
480     } else if (diameterJson_or_action == "clear") {
481       my_app.getOperatedHost()->getReactingAnswers()->clear();
482       response = "clear";
483     } else if (diameterJson_or_action == "dump") {
484       my_app.getOperatedHost()->getReactingAnswers()->dump("programmed_answer");
485       response = "dump";
486     }
487   }
488
489
490   return true; // OK
491 }
492
493 bool EventOperation::answermsg_action_2c(std::string &response, const std::string & diameterJson_or_action, bool msg_or_action) {
494
495   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
496   anna::diameter::codec::Message codecMsg; // auxiliary codec message
497   anna::diameter::codec::Message *message;
498   bool success;
499
500   if (msg_or_action) {
501
502     std::string diameterXml = anna::json::functions::json2xml(diameterJson_or_action, success);
503     if (!success) {
504       response += "json to xml failed, unable to send message !";
505       return false;
506     }
507     codecMsg.loadXMLString(diameterXml);
508     try {
509       my_app.updateOperatedOriginHostWithMessage(codecMsg);
510       message = my_app.getOperatedHost()->getCodecEngine()->createMessage(diameterXml, false /* is xml string */); // loads xml again, lesser of two evils
511       LOGDEBUG(anna::Logger::debug(message->asXMLString(), ANNA_FILE_LOCATION));
512     }
513     catch(anna::RuntimeException &ex) {
514       ex.trace();
515       response += "invalid operated host";
516       return false;
517     }
518
519     if(message->isRequest()) {
520       response += "cannot program diameter requests. Answer type must be provided";
521       return false;
522     }
523
524     int code = message->getId().first;
525     LOGDEBUG(anna::Logger::debug("Adding a new programed 'answer to client' to the FIFO queue corresponding to its message code ...", ANNA_FILE_LOCATION));
526     my_app.getOperatedHost()->getReactingAnswers()->addMessage(code, message);
527     response = "Added 'answer to client' to the FIFO queue corresponding to its message code";
528   }
529   else { // action
530
531     if(diameterJson_or_action == "list") { // programmed answers FIFO's to stdout
532       response = anna::functions::encodeBase64(my_app.getOperatedHost()->getReactingAnswers()->asString("ANSWERS TO CLIENT"));
533     } else if (diameterJson_or_action == "rotate") {
534       my_app.getOperatedHost()->getReactingAnswers()->rotate(true);
535       response = "rotate";
536     } else if (diameterJson_or_action == "exhaust") {
537       my_app.getOperatedHost()->getReactingAnswers()->rotate(false);
538       response = "exhaust";
539     } else if (diameterJson_or_action == "clear") {
540       my_app.getOperatedHost()->getReactingAnswers()->clear();
541       response = "clear";
542     } else if (diameterJson_or_action == "dump") {
543       my_app.getOperatedHost()->getReactingAnswers()->dump("programmed_answer");
544       response = "dump";
545     }
546   }
547
548
549   return true; // OK
550 }
551
552 /////////////////
553 // FSM testing //
554 /////////////////
555 bool EventOperation::test_id__description(std::string &response, unsigned int id, const std::string & description) {
556
557   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
558   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
559
560   try {
561     anna::testing::TestCase *tc = testManager.getTestCase(id); // creates / reuses
562     tc->setDescription(description);
563     response = std::to_string(tc->getId());
564   }
565   catch(anna::RuntimeException &ex) {
566     ex.trace();
567     response += "invalid ip-limit";
568     return false;
569   }
570
571   return true; // OK
572 }
573
574 bool EventOperation::test_id__ip_limit(std::string &response, unsigned int id, int amount) {
575
576   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
577   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
578
579   try {
580     anna::testing::TestCase *tc = testManager.getTestCase(id); // creates / reuses
581     tc->addIpLimit(amount);
582     response = std::to_string(tc->getId());
583   }
584   catch(anna::RuntimeException &ex) {
585     ex.trace();
586     response += "invalid ip-limit";
587     return false;
588   }
589
590   return true; // OK
591 }
592
593 bool EventOperation::test_id__timeout(std::string &response, unsigned int id, int msecs) {
594
595   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
596   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
597
598   try {
599     anna::Millisecond timeout = my_app.checkTimeMeasure("Test case timeout", anna::functions::asString(msecs));
600     anna::testing::TestCase *tc = testManager.getTestCase(id); // creates / reuses
601     tc->addTimeout(timeout);
602     response = std::to_string(tc->getId());
603   }
604   catch(anna::RuntimeException &ex) {
605     ex.trace();
606     response += "invalid timeout";
607     return false;
608   }
609
610   return true; // OK
611 }
612
613 bool EventOperation::test_id__sendmsg2e_2c(std::string &response, unsigned int id, bool _2e_or_2c, const std::string & diameterJson, int stepNumber) {
614
615   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
616   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
617
618   bool success;
619   std::string diameterXml = anna::json::functions::json2xml(diameterJson, success);
620   if (!success) {
621     response += "json to xml failed, unable to load message !";
622     return false;
623   }
624   anna::diameter::codec::Message codecMsg; // auxiliary codec message
625   codecMsg.loadXMLString(diameterXml);
626
627   LOGWARNING(
628      if (!codecMsg.isRequest() && (stepNumber == -1))
629       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);
630   );
631
632   try {
633     anna::testing::TestCase *tc = testManager.getTestCase(id); // creates / reuses
634     my_app.updateOperatedOriginHostWithMessage(codecMsg);
635     if (_2e_or_2c)
636       tc->addSendDiameterXml2e(codecMsg.code(), my_app.getOperatedHost(), stepNumber);
637     else
638       tc->addSendDiameterXml2c(codecMsg.code(), my_app.getOperatedHost(), stepNumber);
639
640     response = std::to_string(tc->getId());
641   }
642   catch(anna::RuntimeException &ex) {
643     ex.trace();
644     response += "failed";
645     return false;
646   }
647
648   return true; // OK
649 }
650
651 bool EventOperation::test_id__delay(std::string &response, unsigned int id, int msecs) {
652
653   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
654   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
655
656   try {
657     anna::testing::TestCase *tc = testManager.getTestCase(id); // creates / reuses
658     anna::Millisecond delay = ((msecs == 0 /* special case */) ? (anna::Millisecond)0 : my_app.checkTimeMeasure("Test case delay step", anna::functions::asString(msecs)));
659     tc->addDelay(delay); // creates / reuses
660     response = std::to_string(tc->getId());
661   }
662   catch(anna::RuntimeException &ex) {
663     ex.trace();
664     response += "invalid delay";
665     return false;
666   }
667
668   return true; // OK
669 }
670
671 bool EventOperation::test_id__sh_command(std::string &response, unsigned int id, const std::string & script) {
672
673   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
674   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
675
676   try {
677     anna::testing::TestCase *tc = testManager.getTestCase(id); // creates / reuses
678     tc->addCommand(script); // creates / reuses
679     response = std::to_string(tc->getId());
680   }
681   catch(anna::RuntimeException &ex) {
682     ex.trace();
683     response += "failed";
684     return false;
685   }
686
687   return true; // OK
688 }
689
690 bool EventOperation::test_id__waitfefc_hex(std::string &response, unsigned int id, bool fe_or_fc, const std::string & hex, bool strict) {
691
692   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
693   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
694
695   // Get DataBlock from hex content:
696   anna::DataBlock db_aux(true);
697   std::string hexString = hex;
698   hexString.erase(std::remove(hexString.begin(), hexString.end(), ':'), hexString.end());
699   LOGDEBUG(
700       std::string msg = "Hex string (remove colons if exists): ";
701   msg += hexString;
702   anna::Logger::debug(msg, ANNA_FILE_LOCATION);
703   );
704
705   std::string regexp;
706   try {
707     anna::functions::fromHexString(hexString, db_aux); // could launch exception
708     regexp = anna::functions::asHexString(db_aux);
709   }
710   catch(anna::RuntimeException &ex) {
711     ex.trace();
712     response += "failed";
713     return false;
714   }
715
716   // optional 'full':
717   if(!strict) {
718     //// If request, we will ignore sequence data:
719     //if (anna::diameter::codec::functions::requestBit(db_aux))
720     regexp.replace (24, 16, "[0-9A-Fa-f]{16}");
721
722     regexp.insert(0, "^");
723     regexp += "$";
724   }
725
726   try {
727     anna::testing::TestCase *tc = testManager.getTestCase(id); // creates / reuses
728     tc->addWaitDiameterRegexpHex(fe_or_fc, regexp);
729     response = std::to_string(tc->getId());
730   }
731   catch(anna::RuntimeException &ex) {
732     ex.trace();
733     response += "failed";
734     return false;
735   }
736
737   return true; // OK
738 }
739
740 bool EventOperation::test_id__waitfefc_msg(std::string &response, unsigned int id, bool fe_or_fc, const std::string & diameterJson, bool strict) {
741
742   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
743   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
744
745   bool success;
746   std::string diameterXml = anna::json::functions::json2xml(diameterJson, success);
747   if (!success) {
748     response += "json to xml failed, unable to load message !";
749     return false;
750   }
751
752   try {
753     anna::diameter::codec::Message codecMsg; // auxiliary codec message
754     codecMsg.loadXMLString(diameterXml);
755     std::string regexp = codecMsg.asXMLString(true /* normalization */);
756
757     // Now we must insert regular expressions in hop-by-hop, end-to-end and Origin-State-Id:
758
759     // optional 'full':
760     if(!strict) {
761       std::string::size_type pos, pos_1, pos_2;
762
763       pos = regexp.find("end-to-end-id=", 0u);
764       if (pos != std::string::npos) {
765         pos = regexp.find("\"", pos);
766         pos_1 = pos;
767         pos = regexp.find("\"", pos+1);
768         pos_2 = pos;
769         regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
770       }
771
772       pos = regexp.find("hop-by-hop-id=", 0u);
773       if (pos != std::string::npos) {
774         pos = regexp.find("\"", pos);
775         pos_1 = pos;
776         pos = regexp.find("\"", pos+1);
777         pos_2 = pos;
778         regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
779       }
780
781       // For this representation: <avp name="Origin-State-Id" data="1428633668"/>
782       //pos = regexp.find("Origin-State-Id", 0u);
783       //pos = regexp.find("\"", pos);
784       //pos = regexp.find("\"", pos+1);
785       //pos_1 = pos;
786       //pos = regexp.find("\"", pos+1);
787       //pos_2 = pos;
788       //regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
789       // But we have this one: <avp data="1428633668" name="Origin-State-Id"/>
790       pos = regexp.find("Origin-State-Id", 0u);
791       if (pos != std::string::npos) {
792         pos = regexp.rfind("\"", pos);
793         pos = regexp.rfind("\"", pos-1);
794         pos = regexp.rfind("\"", pos-1);
795         pos_1 = pos;
796         pos = regexp.find("\"", pos+1);
797         pos_2 = pos;
798         regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
799       }
800
801       //regexp.insert(0, "^");
802       //regexp += "$";
803     }
804
805     anna::testing::TestCase *tc = testManager.getTestCase(id); // creates / reuses
806     tc->addWaitDiameterRegexpXml(fe_or_fc, regexp);
807     response = std::to_string(tc->getId());
808   }
809   catch(anna::RuntimeException &ex) {
810     ex.trace();
811     response += "failed";
812     return false;
813   }
814
815   return true; // OK
816 }
817
818 bool EventOperation::test_id__waitfefc(std::string &response, unsigned int id, bool fe_or_fc,
819                          const std::string & code,
820                          const std::string & bitR,
821                          const std::string & hopByHop,
822                          const std::string & applicationId,
823                          const std::string & sessionId,
824                          const std::string & resultCode,
825                          const std::string & msisdn,
826                          const std::string & imsi,
827                          const std::string & serviceContextId) {
828
829   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
830   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
831
832   try { // [code]|[bitR]|[hopByHop]|[applicationId]|[sessionId]|[resultCode]|[msisdn]|[imsi]|[serviceContextId]
833     anna::testing::TestCase *tc = testManager.getTestCase(id); // creates / reuses
834     tc->addWaitDiameter(fe_or_fc, code, bitR, hopByHop, applicationId, sessionId, resultCode, msisdn, imsi, serviceContextId);
835     response = std::to_string(tc->getId());
836   }
837   catch(anna::RuntimeException &ex) {
838     ex.trace();
839     response += "failed";
840     return false;
841   }
842
843   return true; // OK
844 }
845
846 /////////////////////////
847 // Testcases execution //
848 /////////////////////////
849 bool EventOperation::test__ttps(std::string &response, int amount) {
850
851   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
852   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
853
854   bool success = testManager.configureTTPS(amount);
855   if (success) {
856     response = "Assigned new test launch rate to ";
857     response += anna::functions::asString(amount);
858     response += " events per second";
859   }
860   else {
861     response += "unable to configure the test rate provided";
862   }
863
864   return success; // OK
865 }
866
867 bool EventOperation::test__next(std::string &response, int syncAmount) {
868
869   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
870   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
871
872   if (syncAmount < 1) {
873     response += "the parameter 'sync-amount' must be a positive integer value";
874     return false;
875   }
876
877   bool success = testManager.execTestCases(syncAmount);
878
879   response = (success ? "P" : "Not completely p" /* completed cycle and no repeats, rare case */);
880   response += "rocessed ";
881   response += anna::functions::asString(syncAmount);
882   response += ((syncAmount > 1) ? " test cases synchronously" : " test case");
883
884   return success;
885 }
886
887 bool EventOperation::test__ip_limit(std::string &response, int amount) {
888
889   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
890   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
891
892   if (amount > -2) {
893     testManager.setInProgressLimit(amount);
894     response = "New in-progress limit: ";
895     response += (amount != -1) ? anna::functions::asString(amount) : "[no limit]";
896   }
897   else {
898     response = "In-progress limit amount: ";
899     int limit = testManager.getInProgressLimit();
900     response += (limit != -1) ? anna::functions::asString(limit) : "[no limit]";
901     response += "; currently there are ";
902     response += anna::functions::asString(testManager.getInProgressCount());
903     response += " test cases running";
904   }
905
906   return true; // OK
907 }
908
909 bool EventOperation::test__goto(std::string &response, int id) {
910
911   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
912   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
913
914   bool success = testManager.gotoTestCase(id);
915
916   if (success) {
917     response = "Position updated for id provided (";
918   }
919   else {
920     response += "cannot found test id (";
921   }
922   response += anna::functions::asString(id);
923   response += ")";
924
925   return success;
926 }
927
928 bool EventOperation::test__run(std::string &response, int id) {
929
930   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
931   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
932
933   bool success = testManager.runTestCase(id);
934
935   if (success) {
936     response = "Test executed for id provided (";
937   }
938   else {
939     response += "cannot found test id (";
940   }
941   response += anna::functions::asString(id);
942   response += ")";
943
944   return success;
945 }
946
947 bool EventOperation::test__look(std::string &response, int id) {
948
949   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
950   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
951
952   anna::testing::TestCase *testCase = testManager.findTestCase(id);
953   if (!testCase) {
954     if (id == -1) {
955        response += "no current test case detected (testing started ?)";
956     }
957     else {
958       response += "cannot found test id (";
959       response += anna::functions::asString(id);
960       response += ")";
961     }
962
963     return false;
964   }
965
966   if (a_http)
967     response = anna::functions::encodeBase64(testCase->asXMLString());
968   else
969     response = testCase->asXMLString();
970
971   return true; // OK
972 }
973
974 bool EventOperation::test__state(std::string &response, int id) {
975
976   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
977   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
978
979   anna::testing::TestCase *testCase = testManager.findTestCase(id);
980   if (!testCase) {
981     if (id == -1) {
982        response += "no current test case detected (testing started ?)";
983     }
984     else {
985       response += "cannot found test id (";
986       response += anna::functions::asString(id);
987       response += ")";
988     }
989
990     return false;
991   }
992
993   response = anna::testing::TestCase::asText(testCase->getState());
994   return testCase->isSuccess();
995 }
996
997 bool EventOperation::test__interact(std::string &response, int amount, unsigned int id) {
998
999   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1000   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1001
1002   if (amount < -1) {
1003     response += "interactive amount must be -1 (to disable interactive mode) or a positive number.";
1004     return false;
1005   }
1006
1007   anna::testing::TestCase *testCase = testManager.findTestCase(id);
1008   if (testCase) {
1009     if (amount == -1) {
1010       testCase->makeInteractive(false);
1011       response = "Interactive mode disabled";
1012     }
1013     else {
1014       testCase->addInteractiveAmount(amount);
1015       response = "Added interactive amount of ";
1016       response += anna::functions::asString(amount);
1017       response += " units";
1018       if (amount == 0) response += " (0: freezing a non-interactive testcase, no effect on already interactive)";
1019     }
1020     response += " for test case id ";
1021     response += anna::functions::asString(id);
1022   }
1023   else {
1024     response += "cannot found test id (";
1025     response += anna::functions::asString(id);
1026     response += ")";
1027     return false;
1028   }
1029
1030   return true; // OK
1031 }
1032
1033 bool EventOperation::test__reset(std::string &response, bool soft_hard, int id) {
1034
1035   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1036   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1037
1038   anna::testing::TestCase *testCase = ((id != -1) ? testManager.findTestCase(id) : NULL);
1039   if (testCase) {
1040     bool done = testCase->reset(!soft_hard);
1041     response = "Test ";
1042     response += (soft_hard ? "soft":"hard");
1043     response += " reset for id ";
1044     response += anna::functions::asString(id);
1045     response += done ? ": done": ": not done";
1046   }
1047   else {
1048     if (id == -1) {
1049       bool anyReset = testManager.resetPool(!soft_hard);
1050       response = (soft_hard ? "Soft":"Hard");
1051       response += " reset have been sent to all programmed tests: "; response += anyReset ? "some/all have been reset" : "nothing was reset";
1052     }
1053     else {
1054       response += "cannot found test id (";
1055       response += anna::functions::asString(id);
1056       response += ")";
1057       return false;
1058     }
1059   }
1060
1061   return true; // OK
1062 }
1063
1064 bool EventOperation::test__repeats(std::string &response, int amount) {
1065
1066   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1067   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1068
1069   if (amount < 0) amount = -1;
1070   testManager.setPoolRepeats(amount);
1071   std::string nolimit = (amount != -1) ? "":" [no limit]";
1072   response = anna::functions::asString("Pool repeats: %d%s (current cycle: %d)", amount, nolimit.c_str(), testManager.getPoolCycle());
1073
1074   return true; // OK
1075 }
1076
1077 bool EventOperation::test__auto_reset(std::string &response, bool soft_hard) {
1078
1079   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1080   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1081
1082   testManager.setAutoResetHard(!soft_hard);
1083   response = anna::functions::asString("Auto-reset configured to '%s'", (soft_hard ? "soft":"hard"));
1084
1085   return true; // OK
1086 }
1087
1088 bool EventOperation::test__initialized(std::string &response) {
1089
1090   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1091   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1092
1093   response = anna::functions::asString("%lu", testManager.getInitializedCount());
1094
1095   return true; // OK
1096 }
1097
1098 bool EventOperation::test__finished(std::string &response) {
1099
1100   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1101   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1102
1103   response = anna::functions::asString("%lu", testManager.getFinishedCount());
1104
1105   return true; // OK
1106 }
1107
1108 bool EventOperation::test__clear(std::string &response, int id) {
1109
1110   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1111   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1112
1113   if (id == -1) {
1114      return testManager.clearPool(response);
1115   }
1116
1117   if (!testManager.findTestCase(id)) {
1118       response += "cannot found test id (";
1119       response += anna::functions::asString(id);
1120       response += ")";
1121       return false;
1122   }
1123
1124   return testManager.clearTestCase(response, id);
1125 }
1126
1127 bool EventOperation::test__junit(std::string &response, const std::string & targetFile) {
1128
1129   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1130   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1131
1132   std::ofstream out;
1133   out.open(targetFile.c_str());
1134
1135   if(out.is_open() == false) {
1136     response += "error opening '";
1137     response += targetFile;
1138     response += "'";
1139     return false;
1140   }
1141
1142   out << testManager.junitAsXMLString() << std::endl;
1143   out.close();
1144
1145   response = "Junit report written on '";
1146   response += targetFile;
1147   response += "'";
1148
1149   return true; // OK
1150 }
1151
1152 bool EventOperation::test__summary_counts(std::string &response) {
1153
1154   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1155   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1156
1157   response = anna::functions::encodeBase64(testManager.summaryCounts());
1158
1159   return true; // OK
1160 }
1161
1162 bool EventOperation::test__summary_states(std::string &response) {
1163
1164   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1165   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1166
1167   response = anna::functions::encodeBase64(testManager.summaryStates());
1168
1169   return true; // OK
1170 }
1171
1172 bool EventOperation::test__summary(std::string &response) {
1173
1174   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1175   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1176
1177   response = anna::functions::encodeBase64(testManager.asXMLString());
1178
1179   return true; // OK
1180 }
1181
1182 bool EventOperation::test__report(std::string &response, const std::string & state, bool enable) {
1183
1184   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1185   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1186
1187   std::string _state = state;
1188
1189   if(_state == "initialized")
1190     testManager.setDumpInitializedReports(enable);
1191   else if(_state == "in-progress")
1192     testManager.setDumpInProgressReports(enable);
1193   else if(_state == "failed")
1194     testManager.setDumpFailedReports(enable);
1195   else if(_state == "success")
1196     testManager.setDumpSuccessReports(enable);
1197   else if(_state == "all") {
1198     _state = "any";
1199     testManager.setDumpAllReports(enable);
1200   }
1201   else if(_state == "none") {
1202     enable = !enable;
1203     _state = "any";
1204     testManager.setDumpAllReports(enable);
1205   }
1206   else {
1207     response += "invalid state (allowed: initialized|in-progress|failed|success|[all]|none)";
1208     return false;
1209   }
1210
1211   response = (enable ? "Report enabled " : "Report disabled ");
1212   response += "for tests in '";
1213   response += _state;
1214   response += "' state";
1215
1216   return true; // OK
1217 }
1218
1219 bool EventOperation::test__report_hex(std::string &response, bool enable) {
1220
1221   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1222   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1223
1224   testManager.setDumpHex(enable);
1225   response = (testManager.getDumpHex() ? "Report includes hexadecimal messages" : "Report excludes hexadecimal messages");
1226
1227   return true; // OK
1228 }
1229
1230 bool EventOperation::test__dump_stdout(std::string &response, bool enable) {
1231
1232   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1233   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1234
1235   testManager.setDumpStdout(enable);
1236   response = (testManager.getDumpStdout() ? "Test manager dumps progress into stdout" : "Test manager does not dump progress into stdout");
1237
1238   return true; // OK
1239 }
1240
1241 bool EventOperation::test__dynamic(std::string &response, const nlohmann::json &arguments) {
1242
1243   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1244
1245   Procedure p(&my_app);
1246   try {
1247     p.execute(arguments, response);
1248   }
1249   catch(anna::RuntimeException &ex) {
1250     ex.trace();
1251     response += ex.asString();
1252     return false;
1253   }
1254
1255   return true; // OK
1256 }
1257
1258