From ea14381cada0d7173fd19eaaf781f82eb714325e Mon Sep 17 00:00:00 2001 From: Eduardo Ramos Testillano Date: Wed, 29 Jul 2015 22:10:03 +0200 Subject: [PATCH] Dynamic realm registration --- .../launcher/DEPLOY_clientAndServer.sh | 5 +- example/diameter/launcher/Launcher.cpp | 108 +++++++++++++----- example/diameter/launcher/Launcher.hpp | 6 +- example/diameter/launcher/main.cpp | 2 +- include/anna/diameter.comm/Engine.hpp | 11 +- source/app/Component.cpp | 2 +- source/diameter.comm/Engine.cpp | 8 ++ source/diameter/stack/Engine.cpp | 2 +- 8 files changed, 104 insertions(+), 40 deletions(-) diff --git a/example/diameter/launcher/DEPLOY_clientAndServer.sh b/example/diameter/launcher/DEPLOY_clientAndServer.sh index c8bf3e6..628c223 100755 --- a/example/diameter/launcher/DEPLOY_clientAndServer.sh +++ b/example/diameter/launcher/DEPLOY_clientAndServer.sh @@ -4,13 +4,14 @@ # VARIABLES # ############# DEPLOY_SCR=`dirname $0`/DEPLOY.sh +DEPLOY_DIR__dflt=$HOME/ADML ############# # FUNCTIONS # ############# usage() { echo - echo "Usage: $0 " + 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 diff --git a/example/diameter/launcher/Launcher.cpp b/example/diameter/launcher/Launcher.cpp index a1b1075..2cf882d 100644 --- a/example/diameter/launcher/Launcher.cpp +++ b/example/diameter/launcher/Launcher.cpp @@ -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) { // 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 ¶meter, 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| 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()); diff --git a/example/diameter/launcher/Launcher.hpp b/example/diameter/launcher/Launcher.hpp index e420a1b..c8417fb 100644 --- a/example/diameter/launcher/Launcher.hpp +++ b/example/diameter/launcher/Launcher.hpp @@ -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 ¶meter, 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. diff --git a/example/diameter/launcher/main.cpp b/example/diameter/launcher/main.cpp index ce23903..8054a3b 100644 --- a/example/diameter/launcher/main.cpp +++ b/example/diameter/launcher/main.cpp @@ -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."); diff --git a/include/anna/diameter.comm/Engine.hpp b/include/anna/diameter.comm/Engine.hpp index 92a23e7..8ec2757 100644 --- a/include/anna/diameter.comm/Engine.hpp +++ b/include/anna/diameter.comm/Engine.hpp @@ -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: diff --git a/source/app/Component.cpp b/source/app/Component.cpp index 7894b0a..0ab2d3b 100644 --- a/source/app/Component.cpp +++ b/source/app/Component.cpp @@ -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); } } diff --git a/source/diameter.comm/Engine.cpp b/source/diameter.comm/Engine.cpp index 538001a..3a7f843 100644 --- a/source/diameter.comm/Engine.cpp +++ b/source/diameter.comm/Engine.cpp @@ -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 +} diff --git a/source/diameter/stack/Engine.cpp b/source/diameter/stack/Engine.cpp index d338fef..d5f3468 100644 --- a/source/diameter/stack/Engine.cpp +++ b/source/diameter/stack/Engine.cpp @@ -207,7 +207,7 @@ anna::diameter::stack::Dictionary * anna::diameter::stack::Engine::createDictio Dictionary * result = const_cast(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()); -- 2.20.1