mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-09-01 14:35:29 +00:00
Merge remote-tracking branch 'origin/trac922'
This commit is contained in:
@@ -356,11 +356,40 @@ ModuleCCSession::checkCommand() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
ModuleCCSession::addRemoteConfig(const std::string& spec_file_name) {
|
ModuleCCSession::addRemoteConfig(const std::string& spec_name,
|
||||||
ModuleSpec rmod_spec = readModuleSpecification(spec_file_name);
|
void (*handler)(const std::string& module,
|
||||||
std::string module_name = rmod_spec.getFullSpec()->get("module_name")->stringValue();
|
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);
|
ConfigData rmod_config = ConfigData(rmod_spec);
|
||||||
session_.subscribe(module_name);
|
|
||||||
|
|
||||||
// Get the current configuration values for that module
|
// Get the current configuration values for that module
|
||||||
ConstElementPtr cmd = Element::fromJSON("{ \"command\": [\"get_config\", {\"module_name\":\"" + module_name + "\"} ] }");
|
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);
|
session_.group_recvmsg(env, answer, false, seq);
|
||||||
int rcode;
|
int rcode;
|
||||||
ConstElementPtr new_config = parseAnswer(rcode, answer);
|
ConstElementPtr new_config = parseAnswer(rcode, answer);
|
||||||
|
ElementPtr local_config;
|
||||||
if (rcode == 0 && new_config) {
|
if (rcode == 0 && new_config) {
|
||||||
ElementPtr local_config = rmod_config.getLocalConfig();
|
local_config = rmod_config.getLocalConfig();
|
||||||
isc::data::merge(local_config, new_config);
|
isc::data::merge(local_config, new_config);
|
||||||
rmod_config.setLocalConfig(local_config);
|
rmod_config.setLocalConfig(local_config);
|
||||||
} else {
|
} else {
|
||||||
@@ -380,6 +410,11 @@ ModuleCCSession::addRemoteConfig(const std::string& spec_file_name) {
|
|||||||
|
|
||||||
// all ok, add it
|
// all ok, add it
|
||||||
remote_module_configs_[module_name] = rmod_config;
|
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);
|
return (module_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -390,6 +425,7 @@ ModuleCCSession::removeRemoteConfig(const std::string& module_name) {
|
|||||||
it = remote_module_configs_.find(module_name);
|
it = remote_module_configs_.find(module_name);
|
||||||
if (it != remote_module_configs_.end()) {
|
if (it != remote_module_configs_.end()) {
|
||||||
remote_module_configs_.erase(it);
|
remote_module_configs_.erase(it);
|
||||||
|
remote_module_handlers_.erase(module_name);
|
||||||
session_.unsubscribe(module_name);
|
session_.unsubscribe(module_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -419,6 +455,11 @@ ModuleCCSession::updateRemoteConfig(const std::string& module_name,
|
|||||||
if (it != remote_module_configs_.end()) {
|
if (it != remote_module_configs_.end()) {
|
||||||
ElementPtr rconf = (*it).second.getLocalConfig();
|
ElementPtr rconf = (*it).second.getLocalConfig();
|
||||||
isc::data::merge(rconf, new_config);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -234,24 +234,43 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Gives access to the configuration values of a different module
|
* Gives access to the configuration values of a different module
|
||||||
* Once this function has been called with the name of the specification
|
* 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.
|
* \c getRemoteConfigValue() to get a specific setting.
|
||||||
* Changes are automatically updated, but you cannot specify handlers
|
* Changes are automatically updated, and you can specify handlers
|
||||||
* for those changes, must use \c getRemoteConfigValue() to get a value
|
* for those changes. This function will subscribe to the relevant module
|
||||||
* This function will subscribe to the relevant module channel.
|
* channel.
|
||||||
*
|
*
|
||||||
* \param spec_file_name The path to the specification file of
|
* \param spec_name This specifies the module to add. It is either a
|
||||||
* the module we want to have configuration
|
* filename of the spec file to use or a name of module
|
||||||
* values from
|
* (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
|
* \return The name of the module specified in the given specification
|
||||||
* file
|
* 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
|
* Removes the module with the given name from the remote config
|
||||||
* settings. If the module was not added with \c addRemoteConfig(),
|
* 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);
|
void removeRemoteConfig(const std::string& module_name);
|
||||||
|
|
||||||
@@ -296,7 +315,11 @@ private:
|
|||||||
const std::string& command,
|
const std::string& command,
|
||||||
isc::data::ConstElementPtr args);
|
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, ConfigData> remote_module_configs_;
|
||||||
|
std::map<std::string, RemoteHandler> remote_module_handlers_;
|
||||||
|
|
||||||
void updateRemoteConfig(const std::string& module_name,
|
void updateRemoteConfig(const std::string& module_name,
|
||||||
isc::data::ConstElementPtr new_config);
|
isc::data::ConstElementPtr new_config);
|
||||||
};
|
};
|
||||||
|
@@ -346,6 +346,18 @@ TEST_F(CCSessionTest, checkCommand2) {
|
|||||||
EXPECT_EQ(2, mccs.getValue("item1")->intValue());
|
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) {
|
TEST_F(CCSessionTest, remoteConfig) {
|
||||||
std::string module_name;
|
std::string module_name;
|
||||||
int item1;
|
int item1;
|
||||||
@@ -392,6 +404,91 @@ TEST_F(CCSessionTest, remoteConfig) {
|
|||||||
|
|
||||||
session.getMessages()->add(createAnswer());
|
session.getMessages()->add(createAnswer());
|
||||||
EXPECT_THROW(mccs.addRemoteConfig(ccspecfile("spec2.spec")), CCSessionError);
|
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) {
|
TEST_F(CCSessionTest, ignoreRemoteConfigCommands) {
|
||||||
|
Reference in New Issue
Block a user