Implement dynamic procedure at REST interface
[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       pos = regexp.find("\"", pos);
758       pos_1 = pos;
759       pos = regexp.find("\"", pos+1);
760       pos_2 = pos;
761       regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
762
763       pos = regexp.find("hop-by-hop-id=", 0u);
764       pos = regexp.find("\"", pos);
765       pos_1 = pos;
766       pos = regexp.find("\"", pos+1);
767       pos_2 = pos;
768       regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
769
770       // For this representation: <avp name="Origin-State-Id" data="1428633668"/>
771       //pos = regexp.find("Origin-State-Id", 0u);
772       //pos = regexp.find("\"", pos);
773       //pos = regexp.find("\"", pos+1);
774       //pos_1 = pos;
775       //pos = regexp.find("\"", pos+1);
776       //pos_2 = pos;
777       //regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
778       // But we have this one: <avp data="1428633668" name="Origin-State-Id"/>
779       pos = regexp.find("Origin-State-Id", 0u);
780       pos = regexp.rfind("\"", pos);
781       pos = regexp.rfind("\"", pos-1);
782       pos = regexp.rfind("\"", pos-1);
783       pos_1 = pos;
784       pos = regexp.find("\"", pos+1);
785       pos_2 = pos;
786       regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
787
788       //regexp.insert(0, "^");
789       //regexp += "$";
790     }
791
792     testManager.getTestCase(id)->addWaitDiameterRegexpXml(fe_or_fc, regexp);
793     response = "Done";
794   }
795   catch(anna::RuntimeException &ex) {
796     ex.trace();
797     response += "failed";
798     return false;
799   }
800
801   return true; // OK
802 }
803
804 bool EventOperation::test_id__waitfefc(std::string &response, unsigned int id, bool fe_or_fc,
805                          const std::string & code,
806                          const std::string & bitR,
807                          const std::string & hopByHop,
808                          const std::string & applicationId,
809                          const std::string & sessionId,
810                          const std::string & resultCode,
811                          const std::string & msisdn,
812                          const std::string & imsi,
813                          const std::string & serviceContextId) {
814
815   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
816   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
817
818   try { // [code]|[bitR]|[hopByHop]|[applicationId]|[sessionId]|[resultCode]|[msisdn]|[imsi]|[serviceContextId]
819     testManager.getTestCase(id)->addWaitDiameter(fe_or_fc, code, bitR, hopByHop, applicationId, sessionId, resultCode, msisdn, imsi, serviceContextId);
820     response = "Done";
821   }
822   catch(anna::RuntimeException &ex) {
823     ex.trace();
824     response += "failed";
825     return false;
826   }
827
828   return true; // OK
829 }
830
831 /////////////////////////
832 // Testcases execution //
833 /////////////////////////
834 bool EventOperation::test__ttps(std::string &response, int amount) {
835
836   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
837   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
838
839   bool success = testManager.configureTTPS(amount);
840   if (success) {
841     response = "Assigned new test launch rate to ";
842     response += anna::functions::asString(amount);
843     response += " events per second";
844   }
845   else {
846     response += "unable to configure the test rate provided";
847   }
848
849   return success; // OK
850 }
851
852 bool EventOperation::test__next(std::string &response, int syncAmount) {
853
854   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
855   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
856
857   if (syncAmount < 1) {
858     response += "the parameter 'sync-amount' must be a positive integer value";
859     return false;
860   }
861
862   bool success = testManager.execTestCases(syncAmount);
863
864   response = (success ? "P" : "Not completely p" /* completed cycle and no repeats, rare case */);
865   response += "rocessed ";
866   response += anna::functions::asString(syncAmount);
867   response += ((syncAmount > 1) ? " test cases synchronously" : " test case");
868
869   return success;
870 }
871
872 bool EventOperation::test__ip_limit(std::string &response, int amount) {
873
874   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
875   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
876
877   if (amount > -2) {
878     testManager.setInProgressLimit(amount);
879     response = "New in-progress limit: ";
880     response += (amount != -1) ? anna::functions::asString(amount) : "<no limit>";
881   }
882   else {
883     response = "In-progress limit amount: ";
884     int limit = testManager.getInProgressLimit();
885     response += (limit != -1) ? anna::functions::asString(limit) : "<no limit>";
886     response += "; currently there are ";
887     response += anna::functions::asString(testManager.getInProgressCount());
888     response += " test cases running";
889   }
890
891   return true; // OK
892 }
893
894 bool EventOperation::test__goto(std::string &response, int id) {
895
896   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
897   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
898
899   bool success = testManager.gotoTestCase(id);
900
901   if (success) {
902     response = "Position updated for id provided (";
903   }
904   else {
905     response += "cannot found test id (";
906   }
907   response += anna::functions::asString(id);
908   response += ")";
909
910   return success;
911 }
912
913 bool EventOperation::test__run(std::string &response, int id) {
914
915   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
916   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
917
918   bool success = testManager.runTestCase(id);
919
920   if (success) {
921     response = "Test executed for id provided (";
922   }
923   else {
924     response += "cannot found test id (";
925   }
926   response += anna::functions::asString(id);
927   response += ")";
928
929   return success;
930 }
931
932 bool EventOperation::test__look(std::string &response, int id) {
933
934   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
935   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
936
937   anna::testing::TestCase *testCase = testManager.findTestCase(id);
938   if (!testCase) {
939     if (id == -1) {
940        response += "no current test case detected (testing started ?)";
941     }
942     else {
943       response += "cannot found test id (";
944       response += anna::functions::asString(id);
945       response += ")";
946     }
947
948     return false;
949   }
950
951   if (a_http)
952     response = anna::functions::encodeBase64(testCase->asXMLString());
953   else
954     response = testCase->asXMLString();
955
956   return true; // OK
957 }
958
959 bool EventOperation::test__state(std::string &response, int id) {
960
961   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
962   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
963
964   anna::testing::TestCase *testCase = testManager.findTestCase(id);
965   if (!testCase) {
966     if (id == -1) {
967        response += "no current test case detected (testing started ?)";
968     }
969     else {
970       response += "cannot found test id (";
971       response += anna::functions::asString(id);
972       response += ")";
973     }
974
975     return false;
976   }
977
978   response = anna::testing::TestCase::asText(testCase->getState());
979   return testCase->isSuccess();
980 }
981
982 bool EventOperation::test__interact(std::string &response, int amount, unsigned int id) {
983
984   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
985   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
986
987   if (amount < -1) {
988     response += "interactive amount must be -1 (to disable interactive mode) or a positive number.";
989     return false;
990   }
991
992   anna::testing::TestCase *testCase = testManager.findTestCase(id);
993   if (testCase) {
994     if (amount == -1) {
995       testCase->makeInteractive(false);
996       response = "Interactive mode disabled";
997     }
998     else {
999       testCase->addInteractiveAmount(amount);
1000       response = "Added interactive amount of ";
1001       response += anna::functions::asString(amount);
1002       response += " units";
1003       if (amount == 0) response += " (0: freezing a non-interactive testcase, no effect on already interactive)";
1004     }
1005     response += " for test case id ";
1006     response += anna::functions::asString(id);
1007   }
1008   else {
1009     response += "cannot found test id (";
1010     response += anna::functions::asString(id);
1011     response += ")";
1012     return false;
1013   }
1014
1015   return true; // OK
1016 }
1017
1018 bool EventOperation::test__reset(std::string &response, bool soft_hard, int id) {
1019
1020   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1021   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1022
1023   anna::testing::TestCase *testCase = ((id != -1) ? testManager.findTestCase(id) : NULL);
1024   if (testCase) {
1025     bool done = testCase->reset(!soft_hard);
1026     response = "Test ";
1027     response += (soft_hard ? "soft":"hard");
1028     response += " reset for id ";
1029     response += anna::functions::asString(id);
1030     response += done ? ": done": ": not done";
1031   }
1032   else {
1033     if (id == -1) {
1034       bool anyReset = testManager.resetPool(!soft_hard);
1035       response = (soft_hard ? "Soft":"Hard");
1036       response += " reset have been sent to all programmed tests: "; response += anyReset ? "some/all have been reset" : "nothing was reset";
1037     }
1038     else {
1039       response += "cannot found test id (";
1040       response += anna::functions::asString(id);
1041       response += ")";
1042       return false;
1043     }
1044   }
1045
1046   return true; // OK
1047 }
1048
1049 bool EventOperation::test__repeats(std::string &response, int amount) {
1050
1051   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1052   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1053
1054   if (amount < 0) amount = -1;
1055   testManager.setPoolRepeats(amount);
1056   std::string nolimit = (amount != -1) ? "":" [no limit]";
1057   response = anna::functions::asString("Pool repeats: %d%s (current cycle: %d)", amount, nolimit.c_str(), testManager.getPoolCycle());
1058
1059   return true; // OK
1060 }
1061
1062 bool EventOperation::test__auto_reset(std::string &response, bool soft_hard) {
1063
1064   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1065   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1066
1067   testManager.setAutoResetHard(!soft_hard);
1068   response = anna::functions::asString("Auto-reset configured to '%s'", (soft_hard ? "soft":"hard"));
1069
1070   return true; // OK
1071 }
1072
1073 bool EventOperation::test__initialized(std::string &response) {
1074
1075   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1076   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1077
1078   response = anna::functions::asString("%lu", testManager.getInitializedCount());
1079
1080   return true; // OK
1081 }
1082
1083 bool EventOperation::test__finished(std::string &response) {
1084
1085   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1086   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1087
1088   response = anna::functions::asString("%lu", testManager.getFinishedCount());
1089
1090   return true; // OK
1091 }
1092
1093 bool EventOperation::test__clear(std::string &response) {
1094
1095   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1096   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1097
1098   return testManager.clearPool(response);
1099 }
1100
1101 bool EventOperation::test__junit(std::string &response, const std::string & targetFile) {
1102
1103   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1104   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1105
1106   std::ofstream out;
1107   out.open(targetFile.c_str());
1108
1109   if(out.is_open() == false) {
1110     response += "error opening '";
1111     response += targetFile;
1112     response += "'";
1113     return false;
1114   }
1115
1116   out << testManager.junitAsXMLString() << std::endl;
1117   out.close();
1118
1119   response = "Junit report written on '";
1120   response += targetFile;
1121   response += "'";
1122
1123   return true; // OK
1124 }
1125
1126 bool EventOperation::test__summary_counts(std::string &response) {
1127
1128   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1129   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1130
1131   response = anna::functions::encodeBase64(testManager.summaryCounts());
1132
1133   return true; // OK
1134 }
1135
1136 bool EventOperation::test__summary_states(std::string &response) {
1137
1138   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1139   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1140
1141   response = anna::functions::encodeBase64(testManager.summaryStates());
1142
1143   return true; // OK
1144 }
1145
1146 bool EventOperation::test__summary(std::string &response) {
1147
1148   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1149   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1150
1151   response = anna::functions::encodeBase64(testManager.asXMLString());
1152
1153   return true; // OK
1154 }
1155
1156 bool EventOperation::test__report(std::string &response, const std::string & state, bool enable) {
1157
1158   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1159   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1160
1161   std::string _state = state;
1162
1163   if(_state == "initialized")
1164     testManager.setDumpInitializedReports(enable);
1165   else if(_state == "in-progress")
1166     testManager.setDumpInProgressReports(enable);
1167   else if(_state == "failed")
1168     testManager.setDumpFailedReports(enable);
1169   else if(_state == "success")
1170     testManager.setDumpSuccessReports(enable);
1171   else if(_state == "all") {
1172     _state = "any";
1173     testManager.setDumpAllReports(enable);
1174   }
1175   else if(_state == "none") {
1176     enable = !enable;
1177     _state = "any";
1178     testManager.setDumpAllReports(enable);
1179   }
1180   else {
1181     response += "invalid state (allowed: initialized|in-progress|failed|success|[all]|none)";
1182     return false;
1183   }
1184
1185   response = (enable ? "Report enabled " : "Report disabled ");
1186   response += "for tests in '";
1187   response += _state;
1188   response += "' state";
1189
1190   return true; // OK
1191 }
1192
1193 bool EventOperation::test__report_hex(std::string &response, bool enable) {
1194
1195   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1196   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1197
1198   testManager.setDumpHex(enable);
1199   response = (testManager.getDumpHex() ? "Report includes hexadecimal messages" : "Report excludes hexadecimal messages");
1200
1201   return true; // OK
1202 }
1203
1204 bool EventOperation::test__dump_stdout(std::string &response, bool enable) {
1205
1206   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1207   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1208
1209   testManager.setDumpStdout(enable);
1210   response = (testManager.getDumpStdout() ? "Test manager dumps progress into stdout" : "Test manager does not dump progress into stdout");
1211
1212   return true; // OK
1213 }
1214
1215 bool EventOperation::test__dynamic(std::string &response, const nlohmann::json &arguments) {
1216
1217   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
1218
1219   Procedure p(&my_app);
1220   try {
1221     p.execute(arguments, response);
1222   }
1223   catch(anna::RuntimeException &ex) {
1224     ex.trace();
1225     response += ex.asString();
1226     return false;
1227   }
1228
1229   return true; // OK
1230 }
1231
1232