Browse Source

Merge remote-tracking branch 'origin/trac922'

Michal 'vorner' Vaner 14 years ago
parent
commit
2360fc72c2
3 changed files with 175 additions and 14 deletions
  1. 46 5
      src/lib/config/ccsession.cc
  2. 32 9
      src/lib/config/ccsession.h
  3. 97 0
      src/lib/config/tests/ccsession_unittests.cc

+ 46 - 5
src/lib/config/ccsession.cc

@@ -356,11 +356,40 @@ ModuleCCSession::checkCommand() {
 }
 
 std::string
-ModuleCCSession::addRemoteConfig(const std::string& spec_file_name) {
-    ModuleSpec rmod_spec = readModuleSpecification(spec_file_name);
-    std::string module_name = rmod_spec.getFullSpec()->get("module_name")->stringValue();
+ModuleCCSession::addRemoteConfig(const std::string& spec_name,
+                                 void (*handler)(const std::string& module,
+                                          ConstElementPtr),
+                                 bool spec_is_filename)
+{
+    std::string module_name;
+    ModuleSpec rmod_spec;
+    if (spec_is_filename) {
+        // It's a file name, so load it
+        rmod_spec = readModuleSpecification(spec_name);
+        module_name =
+            rmod_spec.getFullSpec()->get("module_name")->stringValue();
+    } else {
+        // It's module name, request it from config manager
+        ConstElementPtr cmd = Element::fromJSON("{ \"command\": ["
+                                                "\"get_module_spec\","
+                                                "{\"module_name\": \"" +
+                                                module_name + "\"} ] }");
+        unsigned int seq = session_.group_sendmsg(cmd, "ConfigManager");
+        ConstElementPtr env, answer;
+        session_.group_recvmsg(env, answer, false, seq);
+        int rcode;
+        ConstElementPtr spec_data = parseAnswer(rcode, answer);
+        if (rcode == 0 && spec_data) {
+            rmod_spec = ModuleSpec(spec_data);
+            module_name = spec_name;
+            if (module_name != rmod_spec.getModuleName()) {
+                isc_throw(CCSessionError, "Module name mismatch");
+            }
+        } else {
+            isc_throw(CCSessionError, "Error getting config for " + module_name + ": " + answer->str());
+        }
+    }
     ConfigData rmod_config = ConfigData(rmod_spec);
-    session_.subscribe(module_name);
 
     // Get the current configuration values for that module
     ConstElementPtr cmd = Element::fromJSON("{ \"command\": [\"get_config\", {\"module_name\":\"" + module_name + "\"} ] }");
@@ -370,8 +399,9 @@ ModuleCCSession::addRemoteConfig(const std::string& spec_file_name) {
     session_.group_recvmsg(env, answer, false, seq);
     int rcode;
     ConstElementPtr new_config = parseAnswer(rcode, answer);
+    ElementPtr local_config;
     if (rcode == 0 && new_config) {
-        ElementPtr local_config = rmod_config.getLocalConfig();
+        local_config = rmod_config.getLocalConfig();
         isc::data::merge(local_config, new_config);
         rmod_config.setLocalConfig(local_config);
     } else {
@@ -380,6 +410,11 @@ ModuleCCSession::addRemoteConfig(const std::string& spec_file_name) {
 
     // all ok, add it
     remote_module_configs_[module_name] = rmod_config;
+    if (handler) {
+        remote_module_handlers_[module_name] = handler;
+        handler(module_name, local_config);
+    }
+    session_.subscribe(module_name);
     return (module_name);
 }
 
@@ -390,6 +425,7 @@ ModuleCCSession::removeRemoteConfig(const std::string& module_name) {
     it = remote_module_configs_.find(module_name);
     if (it != remote_module_configs_.end()) {
         remote_module_configs_.erase(it);
+        remote_module_handlers_.erase(module_name);
         session_.unsubscribe(module_name);
     }
 }
@@ -419,6 +455,11 @@ ModuleCCSession::updateRemoteConfig(const std::string& module_name,
     if (it != remote_module_configs_.end()) {
         ElementPtr rconf = (*it).second.getLocalConfig();
         isc::data::merge(rconf, new_config);
+        std::map<std::string, RemoteHandler>::iterator hit =
+            remote_module_handlers_.find(module_name);
+        if (hit != remote_module_handlers_.end()) {
+            hit->second(module_name, new_config);
+        }
     }
 }
 

+ 32 - 9
src/lib/config/ccsession.h

@@ -234,24 +234,43 @@ public:
     /**
      * Gives access to the configuration values of a different module
      * Once this function has been called with the name of the specification
-     * file of the module you want the configuration of, you can use
+     * file or the module you want the configuration of, you can use
      * \c getRemoteConfigValue() to get a specific setting.
-     * Changes are automatically updated, but you cannot specify handlers
-     * for those changes, must use \c getRemoteConfigValue() to get a value
-     * This function will subscribe to the relevant module channel.
+     * Changes are automatically updated, and you can specify handlers
+     * for those changes. This function will subscribe to the relevant module
+     * channel.
      *
-     * \param spec_file_name The path to the specification file of
-     *                       the module we want to have configuration
-     *                       values from
+     * \param spec_name This specifies the module to add. It is either a
+     *                  filename of the spec file to use or a name of module
+     *                  (in case it's a module name, the spec data is
+     *                  downloaded from the configuration manager, therefore
+     *                  the configuration manager must know it). If
+     *                  spec_is_filenabe is true (the default), then a
+     *                  filename is assumed, otherwise a module name.
+     * \param handler The handler function called whenever there's a change.
+     *                Called once initally from this function. May be NULL
+     *                if you don't want any handler to be called and you're
+     *                fine with requesting the data through
+     *                getRemoteConfigValue() each time.
+     *
+     *                The handler should not throw, or it'll fall trough and
+     *                the exception will get into strange places, probably
+     *                aborting the application.
+     * \param spec_is_filename Says if spec_name is filename or module name.
      * \return The name of the module specified in the given specification
      *         file
      */
-    std::string addRemoteConfig(const std::string& spec_file_name);
+    std::string addRemoteConfig(const std::string& spec_name,
+                                void (*handler)(const std::string& module_name,
+                                                isc::data::ConstElementPtr
+                                                update) = NULL,
+                                bool spec_is_filename = true);
 
     /**
      * Removes the module with the given name from the remote config
      * settings. If the module was not added with \c addRemoteConfig(),
-     * nothing happens.
+     * nothing happens. If there was a handler for this config, it is
+     * removed as well.
      */
     void removeRemoteConfig(const std::string& module_name);
 
@@ -296,7 +315,11 @@ private:
         const std::string& command,
         isc::data::ConstElementPtr args);
 
+    typedef void (*RemoteHandler)(const std::string&,
+                                  isc::data::ConstElementPtr);
     std::map<std::string, ConfigData> remote_module_configs_;
+    std::map<std::string, RemoteHandler> remote_module_handlers_;
+
     void updateRemoteConfig(const std::string& module_name,
                             isc::data::ConstElementPtr new_config);
 };

+ 97 - 0
src/lib/config/tests/ccsession_unittests.cc

@@ -346,6 +346,18 @@ TEST_F(CCSessionTest, checkCommand2) {
     EXPECT_EQ(2, mccs.getValue("item1")->intValue());
 }
 
+std::string remote_module_name;
+int remote_item1(0);
+ConstElementPtr remote_config;
+ModuleCCSession *remote_mccs(NULL);
+
+void remoteHandler(const std::string& module_name, ConstElementPtr config) {
+    remote_module_name = module_name;
+    remote_item1 = remote_mccs->getRemoteConfigValue("Spec2", "item1")->
+        intValue();
+    remote_config = config;
+}
+
 TEST_F(CCSessionTest, remoteConfig) {
     std::string module_name;
     int item1;
@@ -392,6 +404,91 @@ TEST_F(CCSessionTest, remoteConfig) {
     
     session.getMessages()->add(createAnswer());
     EXPECT_THROW(mccs.addRemoteConfig(ccspecfile("spec2.spec")), CCSessionError);
+
+    {
+        SCOPED_TRACE("With module name");
+        // Try adding it with downloading the spec from config manager
+        ModuleSpec spec(moduleSpecFromFile(ccspecfile("spec2.spec")));
+        session.getMessages()->add(createAnswer(0, spec.getFullSpec()));
+        session.getMessages()->add(createAnswer(0, el("{}")));
+
+        EXPECT_NO_THROW(module_name = mccs.addRemoteConfig("Spec2", NULL,
+                                                           false));
+
+        EXPECT_EQ("Spec2", module_name);
+        EXPECT_NO_THROW(item1 =
+                        mccs.getRemoteConfigValue(module_name,
+                                                  "item1")->intValue());
+        EXPECT_EQ(1, item1);
+
+        mccs.removeRemoteConfig(module_name);
+    }
+
+    {
+        // Try adding it with a handler.
+        // Pass non-default value to see the handler is called after
+        // downloading the configuration, not too soon.
+        SCOPED_TRACE("With handler");
+        session.getMessages()->add(createAnswer(0, el("{ \"item1\": 2 }")));
+        remote_mccs = &mccs;
+        module_name = mccs.addRemoteConfig(ccspecfile("spec2.spec"),
+                                           remoteHandler);
+        {
+            SCOPED_TRACE("Before update");
+            EXPECT_EQ("Spec2", module_name);
+            EXPECT_TRUE(session.haveSubscription("Spec2", "*"));
+            // Now check the parameters the remote handler stored
+            // This also checks it was called
+            EXPECT_EQ("Spec2", remote_module_name);
+            remote_module_name = "";
+            EXPECT_EQ(2, remote_item1);
+            remote_item1 = 0;
+            if (remote_config) {
+                EXPECT_EQ(2, remote_config->get("item1")->intValue());
+            } else {
+                ADD_FAILURE() << "Remote config not set";
+            }
+            remote_config.reset();
+            // Make sure normal way still works
+            item1 = mccs.getRemoteConfigValue(module_name,
+                                              "item1")->intValue();
+            EXPECT_EQ(2, item1);
+        }
+
+        {
+            SCOPED_TRACE("After update");
+            session.addMessage(el("{ \"command\": [ \"config_update\", "
+                                  "{ \"item1\": 3 } ] }"), module_name, "*");
+            mccs.checkCommand();
+            EXPECT_EQ("Spec2", remote_module_name);
+            remote_module_name = "";
+            EXPECT_EQ(3, remote_item1);
+            remote_item1 = 0;
+            if (remote_config) {
+                EXPECT_EQ(3, remote_config->get("item1")->intValue());
+            } else {
+                ADD_FAILURE() << "Remote config not set";
+            }
+            remote_config.reset();
+            // Make sure normal way still works
+            item1 = mccs.getRemoteConfigValue(module_name,
+                                              "item1")->intValue();
+            EXPECT_EQ(3, item1);
+        }
+
+        remote_mccs = NULL;
+        mccs.removeRemoteConfig(module_name);
+
+        {
+            SCOPED_TRACE("When removed");
+            // Make sure nothing is called any more
+            session.addMessage(el("{ \"command\": [ \"config_update\", "
+                                  "{ \"item1\": 4 } ] }"), module_name, "*");
+            EXPECT_EQ("", remote_module_name);
+            EXPECT_EQ(0, remote_item1);
+            EXPECT_FALSE(remote_config);
+        }
+    }
 }
 
 TEST_F(CCSessionTest, ignoreRemoteConfigCommands) {