Fix bug on regexp insertion for xml messages
[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.getOperatedEntity()->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.getOperatedEntity()->getReactingAnswers()->asString("ANSWERS TO ENTITY"));
474     } else if (diameterJson_or_action == "rotate") {
475       my_app.getOperatedEntity()->getReactingAnswers()->rotate(true);
476       response = "rotate";
477     } else if (diameterJson_or_action == "exhaust") {
478       my_app.getOperatedEntity()->getReactingAnswers()->rotate(false);
479       response = "exhaust";
480     } else if (diameterJson_or_action == "clear") {
481       my_app.getOperatedEntity()->getReactingAnswers()->clear();
482       response = "clear";
483     } else if (diameterJson_or_action == "dump") {
484       my_app.getOperatedEntity()->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.getOperatedServer()->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.getOperatedServer()->getReactingAnswers()->asString("ANSWERS TO CLIENT"));
533     } else if (diameterJson_or_action == "rotate") {
534       my_app.getOperatedServer()->getReactingAnswers()->rotate(true);
535       response = "rotate";
536     } else if (diameterJson_or_action == "exhaust") {
537       my_app.getOperatedServer()->getReactingAnswers()->rotate(false);
538       response = "exhaust";
539     } else if (diameterJson_or_action == "clear") {
540       my_app.getOperatedServer()->getReactingAnswers()->clear();
541       response = "clear";
542     } else if (diameterJson_or_action == "dump") {
543       my_app.getOperatedServer()->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     testManager.getTestCase(id)->setDescription(description); // creates / reuses
562     response = "Done";
563   }
564   catch(anna::RuntimeException &ex) {
565     ex.trace();
566     response += "invalid ip-limit";
567     return false;
568   }
569
570   return true; // OK
571 }
572
573 bool EventOperation::test_id__ip_limit(std::string &response, unsigned int id, int amount) {
574
575   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
576   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
577
578   try {
579     testManager.getTestCase(id)->addIpLimit(amount); // creates / reuses
580     response = "Done";
581   }
582   catch(anna::RuntimeException &ex) {
583     ex.trace();
584     response += "invalid ip-limit";
585     return false;
586   }
587
588   return true; // OK
589 }
590
591 bool EventOperation::test_id__timeout(std::string &response, unsigned int id, int msecs) {
592
593   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
594   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
595
596   try {
597     anna::Millisecond timeout = my_app.checkTimeMeasure("Test case timeout", anna::functions::asString(msecs));
598     testManager.getTestCase(id)->addTimeout(timeout); // creates / reuses
599     response = "Done";
600   }
601   catch(anna::RuntimeException &ex) {
602     ex.trace();
603     response += "invalid timeout";
604     return false;
605   }
606
607   return true; // OK
608 }
609
610 bool EventOperation::test_id__sendmsg2e_2c(std::string &response, unsigned int id, bool _2e_or_2c, const std::string & diameterJson, int stepNumber) {
611
612   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
613   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
614
615   bool success;
616   std::string diameterXml = anna::json::functions::json2xml(diameterJson, success);
617   if (!success) {
618     response += "json to xml failed, unable to load message !";
619     return false;
620   }
621   anna::diameter::codec::Message codecMsg; // auxiliary codec message
622   codecMsg.loadXMLString(diameterXml);
623
624   LOGWARNING(
625      if (!codecMsg.isRequest() && (stepNumber == -1))
626       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);
627   );
628
629   try {
630     my_app.updateOperatedOriginHostWithMessage(codecMsg);
631     if (_2e_or_2c)
632       testManager.getTestCase(id)->addSendDiameterXml2e(codecMsg.code(), my_app.getOperatedHost(), stepNumber); // creates / reuses
633     else
634       testManager.getTestCase(id)->addSendDiameterXml2c(codecMsg.code(), my_app.getOperatedHost(), stepNumber); // creates / reuses
635
636     response = "Done";
637   }
638   catch(anna::RuntimeException &ex) {
639     ex.trace();
640     response += "failed";
641     return false;
642   }
643
644   return true; // OK
645 }
646
647 bool EventOperation::test_id__delay(std::string &response, unsigned int id, int msecs) {
648
649   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
650   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
651
652   try {
653     anna::Millisecond delay = ((msecs == 0 /* special case */) ? (anna::Millisecond)0 : my_app.checkTimeMeasure("Test case delay step", anna::functions::asString(msecs)));
654     testManager.getTestCase(id)->addDelay(delay); // creates / reuses
655     response = "Done";
656   }
657   catch(anna::RuntimeException &ex) {
658     ex.trace();
659     response += "invalid delay";
660     return false;
661   }
662
663   return true; // OK
664 }
665
666 bool EventOperation::test_id__sh_command(std::string &response, unsigned int id, const std::string & script) {
667
668   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
669   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
670
671   try {
672     testManager.getTestCase(id)->addCommand(script); // creates / reuses
673     response = "Done";
674   }
675   catch(anna::RuntimeException &ex) {
676     ex.trace();
677     response += "failed";
678     return false;
679   }
680
681   return true; // OK
682 }
683
684 bool EventOperation::test_id__waitfefc_hex(std::string &response, unsigned int id, bool fe_or_fc, const std::string & hex, bool strict) {
685
686   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
687   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
688
689   // Get DataBlock from hex content:
690   anna::DataBlock db_aux(true);
691   std::string hexString = hex;
692   hexString.erase(std::remove(hexString.begin(), hexString.end(), ':'), hexString.end());
693   LOGDEBUG(
694       std::string msg = "Hex string (remove colons if exists): ";
695   msg += hexString;
696   anna::Logger::debug(msg, ANNA_FILE_LOCATION);
697   );
698
699   std::string regexp;
700   try {
701     anna::functions::fromHexString(hexString, db_aux); // could launch exception
702     regexp = anna::functions::asHexString(db_aux);
703   }
704   catch(anna::RuntimeException &ex) {
705     ex.trace();
706     response += "failed";
707     return false;
708   }
709
710   // optional 'full':
711   if(!strict) {
712     //// If request, we will ignore sequence data:
713     //if (anna::diameter::codec::functions::requestBit(db_aux))
714     regexp.replace (24, 16, "[0-9A-Fa-f]{16}");
715
716     regexp.insert(0, "^");
717     regexp += "$";
718   }
719
720   try {
721     testManager.getTestCase(id)->addWaitDiameterRegexpHex(fe_or_fc, regexp);
722     response = "Done";
723   }
724   catch(anna::RuntimeException &ex) {
725     ex.trace();
726     response += "failed";
727     return false;
728   }
729
730   return true; // OK
731 }
732
733 bool EventOperation::test_id__waitfefc_msg(std::string &response, unsigned int id, bool fe_or_fc, const std::string & diameterJson, bool strict) {
734
735   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
736   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
737
738   bool success;
739   std::string diameterXml = anna::json::functions::json2xml(diameterJson, success);
740   if (!success) {
741     response += "json to xml failed, unable to load message !";
742     return false;
743   }
744
745   try {
746     anna::diameter::codec::Message codecMsg; // auxiliary codec message
747     codecMsg.loadXMLString(diameterXml);
748     std::string regexp = codecMsg.asXMLString(true /* normalization */);
749
750     // Now we must insert regular expressions in hop-by-hop, end-to-end and Origin-State-Id:
751
752     // optional 'full':
753     if(!strict) {
754       std::string::size_type pos, pos_1, pos_2;
755
756       pos = regexp.find("end-to-end-id=", 0u);
757       if (pos != std::string::npos) {
758         pos = regexp.find("\"", pos);
759         pos_1 = pos;
760         pos = regexp.find("\"", pos+1);
761         pos_2 = pos;
762         regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
763       }
764
765       pos = regexp.find("hop-by-hop-id=", 0u);
766       if (pos != std::string::npos) {
767         pos = regexp.find("\"", pos);
768         pos_1 = pos;
769         pos = regexp.find("\"", pos+1);
770         pos_2 = pos;
771         regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
772       }
773
774       // For this representation: <avp name="Origin-State-Id" data="1428633668"/>
775       //pos = regexp.find("Origin-State-Id", 0u);
776       //pos = regexp.find("\"", pos);
777       //pos = regexp.find("\"", pos+1);
778       //pos_1 = pos;
779       //pos = regexp.find("\"", pos+1);
780       //pos_2 = pos;
781       //regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
782       // But we have this one: <avp data="1428633668" name="Origin-State-Id"/>
783       pos = regexp.find("Origin-State-Id", 0u);
784       if (pos != std::string::npos) {
785         pos = regexp.rfind("\"", pos);
786         pos = regexp.rfind("\"", pos-1);
787         pos = regexp.rfind("\"", pos-1);
788         pos_1 = pos;
789         pos = regexp.find("\"", pos+1);
790         pos_2 = pos;
791         regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
792       }
793
794       //regexp.insert(0, "^");
795       //regexp += "$";
796     }
797
798     testManager.getTestCase(id)->addWaitDiameterRegexpXml(fe_or_fc, regexp);
799     response = "Done";
800   }
801   catch(anna::RuntimeException &ex) {
802     ex.trace();
803     response += "failed";
804     return false;
805   }
806
807   return true; // OK
808 }
809
810 bool EventOperation::test_id__waitfefc(std::string &response, unsigned int id, bool fe_or_fc,
811                          const std::string & code,
812                          const std::string & bitR,
813                          const std::string & hopByHop,
814                          const std::string & applicationId,
815                          const std::string & sessionId,
816                          const std::string & resultCode,
817                          const std::string & msisdn,
818                          const std::string & imsi,
819                          const std::string & serviceContextId) {
820
821   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
822   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
823
824   try { // [code]|[bitR]|[hopByHop]|[applicationId]|[sessionId]|[resultCode]|[msisdn]|[imsi]|[serviceContextId]
825     testManager.getTestCase(id)->addWaitDiameter(fe_or_fc, code, bitR, hopByHop, applicationId, sessionId, resultCode, msisdn, imsi, serviceContextId);
826     response = "Done";
827   }
828   catch(anna::RuntimeException &ex) {
829     ex.trace();
830     response += "failed";
831     return false;
832   }
833
834   return true; // OK
835 }
836
837 /////////////////////////
838 // Testcases execution //
839 /////////////////////////
840 bool EventOperation::test__ttps(std::string &response, int amount) {
841
842   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
843   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
844
845   bool success = testManager.configureTTPS(amount);
846   if (success) {
847     response = "Assigned new test launch rate to ";
848     response += anna::functions::asString(amount);
849     response += " events per second";
850   }
851   else {
852     response += "unable to configure the test rate provided";
853   }
854
855   return success; // OK
856 }
857
858 bool EventOperation::test__next(std::string &response, int syncAmount) {
859
860   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
861   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
862
863   if (syncAmount < 1) {
864     response += "the parameter 'sync-amount' must be a positive integer value";
865     return false;
866   }
867
868   bool success = testManager.execTestCases(syncAmount);
869
870   response = (success ? "P" : "Not completely p" /* completed cycle and no repeats, rare case */);
871   response += "rocessed ";
872   response += anna::functions::asString(syncAmount);
873   response += ((syncAmount > 1) ? " test cases synchronously" : " test case");
874
875   return success;
876 }
877
878 bool EventOperation::test__ip_limit(std::string &response, int amount) {
879
880   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
881   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
882
883   if (amount > -2) {
884     testManager.setInProgressLimit(amount);
885     response = "New in-progress limit: ";
886     response += (amount != -1) ? anna::functions::asString(amount) : "[no limit]";
887   }
888   else {
889     response = "In-progress limit amount: ";
890     int limit = testManager.getInProgressLimit();
891     response += (limit != -1) ? anna::functions::asString(limit) : "[no limit]";
892     response += "; currently there are ";
893     response += anna::functions::asString(testManager.getInProgressCount());
894     response += " test cases running";
895   }
896
897   return true; // OK
898 }
899
900 bool EventOperation::test__goto(std::string &response, int id) {
901
902   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
903   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
904
905   bool success = testManager.gotoTestCase(id);
906
907   if (success) {
908     response = "Position updated for id provided (";
909   }
910   else {
911     response += "cannot found test id (";
912   }
913   response += anna::functions::asString(id);
914   response += ")";
915
916   return success;
917 }
918
919 bool EventOperation::test__run(std::string &response, int id) {
920
921   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
922   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
923
924   bool success = testManager.runTestCase(id);
925
926   if (success) {
927     response = "Test executed for id provided (";
928   }
929   else {
930     response += "cannot found test id (";
931   }
932   response += anna::functions::asString(id);
933   response += ")";
934
935   return success;
936 }
937
938 bool EventOperation::test__look(std::string &response, int id) {
939
940   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
941   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
942
943   anna::testing::TestCase *testCase = testManager.findTestCase(id);
944   if (!testCase) {
945     if (id == -1) {
946        response += "no current test case detected (testing started ?)";
947     }
948     else {
949       response += "cannot found test id (";
950       response += anna::functions::asString(id);
951       response += ")";
952     }
953
954     return false;
955   }
956
957   if (a_http)
958     response = anna::functions::encodeBase64(testCase->asXMLString());
959   else
960     response = testCase->asXMLString();
961
962   return true; // OK
963 }
964
965 bool EventOperation::test__state(std::string &response, int id) {
966
967   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
968   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
969
970   anna::testing::TestCase *testCase = testManager.findTestCase(id);
971   if (!testCase) {
972     if (id == -1) {
973        response += "no current test case detected (testing started ?)";
974     }
975     else {
976       response += "cannot found test id (";
977       response += anna::functions::asString(id);
978       response += ")";
979     }
980
981     return false;
982   }
983
984   response = anna::testing::TestCase::asText(testCase->getState());
985   return testCase->isSuccess();
986 }
987
988 bool EventOperation::test__interact(std::string &response, int amount, unsigned int id) {
989
990   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
991   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
992
993   if (amount < -1) {
994     response += "interactive amount must be -1 (to disable interactive mode) or a positive number.";
995     return false;
996   }
997
998   anna::testing::TestCase *testCase = testManager.findTestCase(id);
999   if (testCase) {
1000     if (amount == -1) {
1001       testCase->makeInteractive(false);
1002       response = "Interactive mode disabled";
1003     }
1004     else {
1005       testCase->addInteractiveAmount(amount);
1006       response = "Added interactive amount of ";
1007       response += anna::functions::asString(amount);
1008       response += " units";
1009       if (amount == 0) response += " (0: freezing a non-interactive testcase, no effect on already interactive)";
1010     }
1011     response += " for test case id ";
1012     response += anna::functions::asString(id);
1013   }
1014   else {
1015     response += "cannot found test id (";
1016     response += anna::functions::asString(id);
1017     response += ")";
1018     return false;
1019   }
1020
1021   return true; // OK
1022 }
1023
1024 bool EventOperation::test__reset(std::string &response, bool soft_hard, int id) {
1025
1026   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1027   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1028
1029   anna::testing::TestCase *testCase = ((id != -1) ? testManager.findTestCase(id) : NULL);
1030   if (testCase) {
1031     bool done = testCase->reset(!soft_hard);
1032     response = "Test ";
1033     response += (soft_hard ? "soft":"hard");
1034     response += " reset for id ";
1035     response += anna::functions::asString(id);
1036     response += done ? ": done": ": not done";
1037   }
1038   else {
1039     if (id == -1) {
1040       bool anyReset = testManager.resetPool(!soft_hard);
1041       response = (soft_hard ? "Soft":"Hard");
1042       response += " reset have been sent to all programmed tests: "; response += anyReset ? "some/all have been reset" : "nothing was reset";
1043     }
1044     else {
1045       response += "cannot found test id (";
1046       response += anna::functions::asString(id);
1047       response += ")";
1048       return false;
1049     }
1050   }
1051
1052   return true; // OK
1053 }
1054
1055 bool EventOperation::test__repeats(std::string &response, int amount) {
1056
1057   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1058   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1059
1060   if (amount < 0) amount = -1;
1061   testManager.setPoolRepeats(amount);
1062   std::string nolimit = (amount != -1) ? "":" [no limit]";
1063   response = anna::functions::asString("Pool repeats: %d%s (current cycle: %d)", amount, nolimit.c_str(), testManager.getPoolCycle());
1064
1065   return true; // OK
1066 }
1067
1068 bool EventOperation::test__auto_reset(std::string &response, bool soft_hard) {
1069
1070   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1071   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1072
1073   testManager.setAutoResetHard(!soft_hard);
1074   response = anna::functions::asString("Auto-reset configured to '%s'", (soft_hard ? "soft":"hard"));
1075
1076   return true; // OK
1077 }
1078
1079 bool EventOperation::test__initialized(std::string &response) {
1080
1081   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1082   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1083
1084   response = anna::functions::asString("%lu", testManager.getInitializedCount());
1085
1086   return true; // OK
1087 }
1088
1089 bool EventOperation::test__finished(std::string &response) {
1090
1091   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1092   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1093
1094   response = anna::functions::asString("%lu", testManager.getFinishedCount());
1095
1096   return true; // OK
1097 }
1098
1099 bool EventOperation::test__clear(std::string &response) {
1100
1101   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1102   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1103
1104   return testManager.clearPool(response);
1105 }
1106
1107 bool EventOperation::test__junit(std::string &response, const std::string & targetFile) {
1108
1109   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1110   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1111
1112   std::ofstream out;
1113   out.open(targetFile.c_str());
1114
1115   if(out.is_open() == false) {
1116     response += "error opening '";
1117     response += targetFile;
1118     response += "'";
1119     return false;
1120   }
1121
1122   out << testManager.junitAsXMLString() << std::endl;
1123   out.close();
1124
1125   response = "Junit report written on '";
1126   response += targetFile;
1127   response += "'";
1128
1129   return true; // OK
1130 }
1131
1132 bool EventOperation::test__summary_counts(std::string &response) {
1133
1134   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1135   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1136
1137   response = anna::functions::encodeBase64(testManager.summaryCounts());
1138
1139   return true; // OK
1140 }
1141
1142 bool EventOperation::test__summary_states(std::string &response) {
1143
1144   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1145   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1146
1147   response = anna::functions::encodeBase64(testManager.summaryStates());
1148
1149   return true; // OK
1150 }
1151
1152 bool EventOperation::test__summary(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.asXMLString());
1158
1159   return true; // OK
1160 }
1161
1162 bool EventOperation::test__report(std::string &response, const std::string & state, bool enable) {
1163
1164   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1165   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1166
1167   std::string _state = state;
1168
1169   if(_state == "initialized")
1170     testManager.setDumpInitializedReports(enable);
1171   else if(_state == "in-progress")
1172     testManager.setDumpInProgressReports(enable);
1173   else if(_state == "failed")
1174     testManager.setDumpFailedReports(enable);
1175   else if(_state == "success")
1176     testManager.setDumpSuccessReports(enable);
1177   else if(_state == "all") {
1178     _state = "any";
1179     testManager.setDumpAllReports(enable);
1180   }
1181   else if(_state == "none") {
1182     enable = !enable;
1183     _state = "any";
1184     testManager.setDumpAllReports(enable);
1185   }
1186   else {
1187     response += "invalid state (allowed: initialized|in-progress|failed|success|[all]|none)";
1188     return false;
1189   }
1190
1191   response = (enable ? "Report enabled " : "Report disabled ");
1192   response += "for tests in '";
1193   response += _state;
1194   response += "' state";
1195
1196   return true; // OK
1197 }
1198
1199 bool EventOperation::test__report_hex(std::string &response, bool enable) {
1200
1201   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1202   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1203
1204   testManager.setDumpHex(enable);
1205   response = (testManager.getDumpHex() ? "Report includes hexadecimal messages" : "Report excludes hexadecimal messages");
1206
1207   return true; // OK
1208 }
1209
1210 bool EventOperation::test__dump_stdout(std::string &response, bool enable) {
1211
1212   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1213   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1214
1215   testManager.setDumpStdout(enable);
1216   response = (testManager.getDumpStdout() ? "Test manager dumps progress into stdout" : "Test manager does not dump progress into stdout");
1217
1218   return true; // OK
1219 }
1220
1221 bool EventOperation::test__dynamic(std::string &response, const nlohmann::json &arguments) {
1222
1223   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1224
1225   Procedure p(&my_app);
1226   try {
1227     p.execute(arguments, response);
1228   }
1229   catch(anna::RuntimeException &ex) {
1230     ex.trace();
1231     response += ex.asString();
1232     return false;
1233   }
1234
1235   return true; // OK
1236 }
1237
1238