Dynamic realm registration
authorEduardo Ramos Testillano <eduardo.ramos.testillano@ericsson.com>
Wed, 29 Jul 2015 20:10:03 +0000 (22:10 +0200)
committerEduardo Ramos Testillano <eduardo.ramos.testillano@ericsson.com>
Wed, 29 Jul 2015 20:10:03 +0000 (22:10 +0200)
example/diameter/launcher/DEPLOY_clientAndServer.sh
example/diameter/launcher/Launcher.cpp
example/diameter/launcher/Launcher.hpp
example/diameter/launcher/main.cpp
include/anna/diameter.comm/Engine.hpp
source/app/Component.cpp
source/diameter.comm/Engine.cpp
source/diameter/stack/Engine.cpp

index c8bf3e6..628c223 100755 (executable)
@@ -4,13 +4,14 @@
 # VARIABLES #
 #############
 DEPLOY_SCR=`dirname $0`/DEPLOY.sh
+DEPLOY_DIR__dflt=$HOME/ADML
 
 #############
 # FUNCTIONS #
 #############
 usage() {
   echo
-  echo "Usage: $0 <empty directory>"
+  echo "Usage: $0 [empty directory: '$DEPLOY_DIR__dflt' by default]"
   echo
   exit 1
 }
@@ -19,7 +20,7 @@ usage() {
 # EXECUTION #
 #############
 DIR=$1
-[ "$DIR" = "" ] && usage
+[ "$DIR" = "" ] && DIR=$DEPLOY_DIR__dflt
 [ -d $DIR ] && usage
 
 $DEPLOY_SCR b $DIR/client
index a1b1075..2cf882d 100644 (file)
@@ -120,7 +120,7 @@ Launcher::Launcher() : anna::comm::Application("launcher", "DiameterLauncher", "
 
 
 
-void Launcher::servicesFromXML(const anna::xml::Node* servicesNode) throw(anna::RuntimeException) {
+void Launcher::servicesFromXML(const anna::xml::Node* servicesNode, bool eventOperation) throw(anna::RuntimeException) {
   //<!ATTLIST stack id CDATA #REQUIRED dictionary CDATA #REQUIRED>
   const anna::xml::Attribute  *id, *dictionary;
 
@@ -135,6 +135,13 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode) throw(anna::
   // Stacks
   anna::diameter::stack::Engine &stackEngine = anna::diameter::stack::Engine::instantiate();
   anna::diameter::stack::Dictionary *d;
+  ///////////////////////////////////////////
+  // APPLICATION MESSAGE OAM MODULE SCOPES //
+  ///////////////////////////////////////////
+  // We will register a scope per stack id registered. The counters will be dynamically registered at count method.
+  anna::diameter::comm::ApplicationMessageOamModule & appMsgOamModule = anna::diameter::comm::ApplicationMessageOamModule::instantiate();
+  appMsgOamModule.enableCounters(); // this special module is disabled by default (the only)
+  static int scope_id = 3;
 
   for(anna::xml::Node::const_child_iterator it = servicesNode->child_begin(); it != servicesNode->child_end(); it++) {
     std::string nodeName = (*it)->getName();
@@ -147,6 +154,11 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode) throw(anna::
       try {
         d = stackEngine.createDictionary(id->getIntegerValue(), dictionary->getValue());
         getCodecEngine()->setDictionary(d);
+
+        // OAM module for counters:
+        appMsgOamModule.createStackCounterScope(scope_id, id->getIntegerValue() /* application-id */);
+        scope_id++;
+
       } catch(anna::RuntimeException &ex) {
         //_exit(ex.asString());
         throw ex;
@@ -154,6 +166,12 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode) throw(anna::
     }
   }
 
+  // Show loaded stacks:
+  std::cout << "Stacks currently loaded:" << std::endl;
+  std::cout << anna::functions::tab(stackEngine.asString(false /* light */));
+  std::cout << std::endl;
+
+
   // Codec engine adjustments:
   // Auto stack selection based on Application-ID:
   bool multistack = (stackEngine.stack_size() > 1);
@@ -256,6 +274,7 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode) throw(anna::
           a_workingNode->setRequestRetransmissions(retransmissions);
           a_workingNode->getEntity()->setSessionBasedModelsType(sessionBasedModelsType);
           a_workingNode->getEntity()->setBalance(balance ? (balance->getValue() == "yes") : false); // for sendings
+          if (eventOperation) a_workingNode->getEntity()->bind();
         }
       }
 
@@ -275,15 +294,27 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode) throw(anna::
       a_workingNode->setLogs(s_log, b_splitLog, b_detailedLog, b_dumpLog, s_burstLog);
 
 
+      // Lazy initialization for comm engine:
+      if (eventOperation) commEngine->lazyInitialize();
+
       // New Node assignment //////////////////////////////////////////////////////////////////////
       a_nodes[originRealm->getValue()] = a_workingNode;
       /////////////////////////////////////////////////////////////////////////////////////////////
     }
   }
+
+  // Diameter comm engines which are loaded after application start (via management operation 'services') are not really started,
+  //  but this don't care because application startComponents() -> initialize() -> do_initialize() -> do nothing.
+  // And when stopped, running state is not checked and it will be stopped anyway.
 }
 
 
-void Launcher::loadServices(const std::string & xmlPathFile) throw(anna::RuntimeException) {
+void Launcher::loadServices(const std::string & xmlPathFile, bool eventOperation) throw(anna::RuntimeException) {
+
+  if (xmlPathFile == "null" || xmlPathFile == "") {
+    LOGWARNING(anna::Logger::warning("Ignoring services configuration on start: empty or 'null' string provided as xml file. Use management interface (operation 'services') in order to add services", ANNA_FILE_LOCATION));
+    return;
+  }
 
   LOGDEBUG(
     std::string trace = "Loading ADML services file '";
@@ -315,7 +346,7 @@ void Launcher::loadServices(const std::string & xmlPathFile) throw(anna::Runtime
     trace += anna::xml::Compiler().apply(rootNode);
     anna::Logger::debug(trace, ANNA_FILE_LOCATION);
   );
-  servicesFromXML(rootNode);
+  servicesFromXML(rootNode, eventOperation);
 }
 
 
@@ -360,6 +391,14 @@ anna::Millisecond Launcher::checkTimeMeasure(const std::string &parameter, const
   throw RuntimeException(msg, ANNA_FILE_LOCATION);
 }
 
+RealmNode *Launcher::getWorkingNode() const throw(anna::RuntimeException) {
+
+  if (!a_workingNode)
+    throw RuntimeException("No services yet loaded. Try 'services' operation (via management interface), or restart process using command-line 'services' parameter", ANNA_FILE_LOCATION);
+
+  return a_workingNode;
+}
+
 bool Launcher::setWorkingNode(const std::string &name) throw() {
   bool result = false;
 
@@ -379,8 +418,8 @@ bool Launcher::setWorkingNode(const std::string &name) throw() {
 }
 
 RealmNode *Launcher::getRealmNode(const std::string &realmName) const throw() {
-  for (realm_nodes_it it = a_nodes.begin(); it != a_nodes.end(); it++)
-    if(it->second->getMyDiameterEngine()->getRealm() == realmName) return it->second;
+  realm_nodes_it it = a_nodes.find(realmName);
+  if (it != a_nodes.end()) return it->second;
 
   return NULL; // this never happens
 }
@@ -405,13 +444,12 @@ throw(anna::RuntimeException) {
     a_counterRecorder = new MyCounterRecorder(cntDir + anna::functions::asString("/Counters.Pid%d", (int)getPid()));
   }
 
+  // Tracing:
+  if(cl.exists("trace"))
+    anna::Logger::setLevel(anna::Logger::asLevel(cl.getValue("trace")));
+
   // Load launcher services:
   loadServices(cl.getValue("services")); // before run (have components to be created)
-
-  // Show loaded stacks:
-  std::cout << "Stacks provided:" << std::endl;
-  std::cout << anna::functions::tab(anna::diameter::stack::Engine::instantiate().asString(false /* light */));
-  std::cout << std::endl;
 }
 
 void Launcher::run()
@@ -425,10 +463,6 @@ throw(anna::RuntimeException) {
   // Statistics:
   anna::statistics::Engine::instantiate().enable();
 
-  // Tracing:
-  if(cl.exists("trace"))
-    anna::Logger::setLevel(anna::Logger::asLevel(cl.getValue("trace")));
-
   LOGINFORMATION(
     // Help on startup traces:
     anna::Logger::information(help(), ANNA_FILE_LOCATION);
@@ -557,18 +591,6 @@ throw(anna::RuntimeException) {
   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::LevelValidation__FailedRuleForCardinalityMoreThanNeeded,                 "", 17 /*2017*/);
   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::LevelValidation__FailedGenericAvpRuleForCardinalityFoundDisregardedItem, "", 18 /*2018*/);
   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::LevelValidation__FoundDisregardedItemsAndGenericAVPWasNotSpecified,      "", 19 /*2019*/);
-  ///////////////////////////////////////////
-  // APPLICATION MESSAGE OAM MODULE SCOPES //
-  ///////////////////////////////////////////
-  // We will register a scope per stack id registered. The counters will be dynamically registered at count method.
-  anna::diameter::comm::ApplicationMessageOamModule & appMsgOamModule = anna::diameter::comm::ApplicationMessageOamModule::instantiate();
-  int scope_id = 3;
-  for (anna::diameter::stack::Engine::const_stack_iterator it = stackEngine.stack_begin(); it != stackEngine.stack_end(); it++) {
-    appMsgOamModule.createStackCounterScope(scope_id, it->first);
-    scope_id++;
-  }
-  appMsgOamModule.enableCounters(); // this special module is disabled by default (the only)
-
 
 
   /////////////////////////////////
@@ -577,7 +599,7 @@ throw(anna::RuntimeException) {
   if(a_counterRecorderClock) {
     oamDiameterComm.setCounterRecorder(a_counterRecorder);
     oamDiameterCodec.setCounterRecorder(a_counterRecorder);
-    appMsgOamModule.setCounterRecorder(a_counterRecorder);
+    anna::diameter::comm::ApplicationMessageOamModule::instantiate().setCounterRecorder(a_counterRecorder);
     a_timeEngine->activate(a_counterRecorderClock); // start clock
   }
 
@@ -640,7 +662,6 @@ throw(anna::RuntimeException) {
     if (entity) entity->bind();
   }
 
-
   // Go into communicator poll
   // Reconnection period (tcp reconnect retry time):
   const char *varname = "reconnectionPeriod";
@@ -817,6 +838,14 @@ std::string Launcher::help() const throw() {
   result += "\n";
   result += "\n------------------------------------------------------------------------------------------- Hot changes";
   result += "\n";
+  result += "\nservices[|source file]               Adds and starts the services specified in the xml file provided.";
+  result += "\n                                      (if missing, the file 'services.xml' will be used).";
+  result += "\n                                     The last loaded realm node will be automatically the new current";
+  result += "\n                                      working node. This is used to load new nodes once the ADML is";
+  result += "\n                                      started, regardless if '--services' command line parameter was";
+  result += "\n                                      used or not. Those services which are not correctly loaded, will";
+  result += "\n                                      be ignored, keeping the process alive.";
+  result += "\n";
   result += "\ndiameterServerSessions|<integer>     Updates the maximum number of accepted connections to diameter";
   result += "\n                                      server socket.";
   result += "\ncontext[|target file]                Application context could also be written by mean this operation,";
@@ -985,9 +1014,6 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
   std::string result = "";
   anna::DataBlock db_aux(true);
   anna::diameter::codec::Message codecMsg(getCodecEngine());
-  MyDiameterEntity *entity = getWorkingNode()->getEntity();
-  MyDiameterEngine *commEngine = getWorkingNode()->getMyDiameterEngine();
-  MyLocalServer *localServer = getWorkingNode()->getDiameterServer();
 
   ///////////////////////////////////////////////////////////////////
   // Simple operations without arguments:
@@ -1067,6 +1093,20 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
     return;
   }
 
+  if(opType == "services") {
+    std::string servicesFile = ((numParams == 1) ? param1 : "services.xml");
+    try {
+      loadServices(servicesFile, true /* bind entities */);
+    }
+    catch(anna::RuntimeException &ex) {
+      ex.trace();
+      response_content = anna::functions::asString("Loaded services from file '%s' with some problems (ignored ones)\n", servicesFile.c_str());
+      return;
+    }
+    response_content = anna::functions::asString("Loaded services from file '%s'\n", servicesFile.c_str());
+    return;
+  }
+
   // Realm switch:
   if(opType == "node") {
     if (param1 != "") {
@@ -1078,6 +1118,12 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
     return;
   }
 
+  // Diameter endpoints:
+  MyDiameterEntity *entity = getWorkingNode()->getEntity();
+  MyDiameterEngine *commEngine = getWorkingNode()->getMyDiameterEngine();
+  MyLocalServer *localServer = getWorkingNode()->getDiameterServer();
+
+
   if(opType == "code") {
     codecMsg.loadXML(param1);
     std::string hexString = anna::functions::asHexString(codecMsg.code());
index e420a1b..c8417fb 100644 (file)
@@ -62,7 +62,7 @@ class Launcher : public anna::comm::Application {
   anna::comm::ServerSocket* a_httpServerSocket; // HTTP
 
 
-  void servicesFromXML(const anna::xml::Node* servicesNode) throw(anna::RuntimeException);
+  void servicesFromXML(const anna::xml::Node* servicesNode, bool eventOperation) throw(anna::RuntimeException);
   anna::Millisecond checkTimeMeasure(const std::string &parameter, const std::string &value) throw(anna::RuntimeException);
   void initialize() throw(anna::RuntimeException); // HTTP
   void run() throw(anna::RuntimeException);
@@ -71,11 +71,11 @@ class Launcher : public anna::comm::Application {
 public:
   Launcher();
 
-  void loadServices(const std::string & xmlPathFile) throw(anna::RuntimeException);
+  void loadServices(const std::string & xmlPathFile, bool eventOperation = false) throw(anna::RuntimeException);
   void startServices() throw(anna::RuntimeException);
 
   anna::diameter::codec::Engine *getCodecEngine() const throw() { return a_codecEngine; }
-  RealmNode *getWorkingNode() const throw() { return a_workingNode; } // management operations working node
+  RealmNode *getWorkingNode() const throw(anna::RuntimeException); // management operations working node
   bool setWorkingNode(const std::string &name) throw(); // we could update ignoreFlagsOnValidation/integrationAndDebugging over the global codec engine
                                                         //  but finally, that configuration issues will be global to the process.
 
index ce23903..8054a3b 100644 (file)
@@ -29,7 +29,7 @@ int main(int argc, const char** argv) {
   try {
     anna::CommandLine& commandLine(anna::CommandLine::instantiate());
     // General
-    commandLine.add("services", anna::CommandLine::Argument::Mandatory, "Services xml path file. Shall be validated against dtd schema written on warning traces: 'Services DTD schema'");
+    commandLine.add("services", anna::CommandLine::Argument::Mandatory, "Services xml path file. Shall be validated against dtd schema written on warning traces: 'Services DTD schema'. Empty string or \"null\" name, to start without services (see help for management operation 'services').");
     commandLine.add("trace", anna::CommandLine::Argument::Optional, "Trace level (emergency, alert, critical, error, warning, notice, information, debug, local0..local7)");
     commandLine.add("cntDir", anna::CommandLine::Argument::Optional, "Counters directory. By default is the current execution directory. Warning: a counter file will be dump per record period; take care about the possible accumulation of files");
     commandLine.add("cntRecordPeriod", anna::CommandLine::Argument::Optional, "Counters record procedure period in milliseconds. If missing, default value of 300000 (5 minutes) will be assigned. Value of 0 disables the record procedure.");
index 92a23e7..8ec2757 100644 (file)
@@ -639,6 +639,15 @@ public:
   void resetStatistics() throw();
 
 
+  /**
+   * Engine lazy initialization. Used if the engine is created when application is already running; for example
+   * on dynamic realms registration. At the moment is not actually needed (nothing is done at initialization),
+   * but it is recommended to start the component and set its state as 'running' from the point of view of the
+   * application.
+   */
+  void lazyInitialize() throw(RuntimeException);
+
+
 protected:
   /**
      Constructor.
@@ -742,7 +751,7 @@ private:
   void eraseDeprecatedIdleEntities() throw();
 
   // Component:
-  void do_initialize() throw() {;}
+  void do_initialize() throw(anna::RuntimeException);
   void do_stop() throw();
 
   // Integrity:
index 7894b0a..0ab2d3b 100644 (file)
@@ -32,7 +32,7 @@ app::Component::Component(const char* className) :
     }
   } else if(Logger::isActive(Logger::Warning) == true) {
     string msg(asString());
-    msg += " | Not connected to application yet";
+    msg += " | Application already running. You may initialize this component manually (lazy initialization)";
     Logger::warning(msg, ANNA_FILE_LOCATION);
   }
 }
index 538001a..3a7f843 100644 (file)
@@ -1164,4 +1164,12 @@ void Engine::resetStatistics() throw() {
     localServer(it)->resetStatistics();
 }
 
+void Engine::do_initialize() throw(RuntimeException) {
+  LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "do_initialize", ANNA_FILE_LOCATION));
+  LOGDEBUG(anna::Logger::debug("Nothing special done on component initialization", ANNA_FILE_LOCATION));
+}
 
+void Engine::lazyInitialize() throw(RuntimeException) {
+  LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "lazyInitialize", ANNA_FILE_LOCATION));
+  anna::app::Component::initialize(); // this will invoke do_initialize
+}
index d338fef..d5f3468 100644 (file)
@@ -207,7 +207,7 @@ anna::diameter::stack::Dictionary *  anna::diameter::stack::Engine::createDictio
   Dictionary * result = const_cast<Dictionary *>(getDictionary(stackId));
 
   if(result)  // if exists, launch exception
-    throw anna::RuntimeException("Such provided stack id has already been created. Removes it before call this method", ANNA_FILE_LOCATION);
+    throw anna::RuntimeException("Such provided stack id has already been created (note: API allows you to remove any registered stack)", ANNA_FILE_LOCATION);
 
   // Register a new dictionary:
   result = registerDictionary(stackId, new Dictionary());