]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[65-libyang-extend-syntax] Extended syntax 65-libyang-extend-syntax
authorFrancis Dupont <fdupont@isc.org>
Wed, 10 Oct 2018 00:12:48 +0000 (02:12 +0200)
committerFrancis Dupont <fdupont@isc.org>
Wed, 10 Oct 2018 00:12:48 +0000 (02:12 +0200)
12 files changed:
doc/examples/netconf/simple-dhcp4.json
doc/examples/netconf/simple.json
src/bin/netconf/netconf_cfg_mgr.cc
src/bin/netconf/netconf_cfg_mgr.h
src/bin/netconf/netconf_config.cc
src/bin/netconf/netconf_config.h
src/bin/netconf/simple_parser.cc
src/bin/netconf/simple_parser.h
src/bin/netconf/tests/netconf_cfg_mgr_unittests.cc
src/bin/netconf/tests/netconf_controller_unittests.cc
src/bin/netconf/tests/parser_unittests.cc
src/bin/netconf/tests/testdata/get_config.json

index 5a869c5585b584b18c0a5958c766a5a5ee73ea25..5258a2182dac260b8e66e14090e833ddef2610d2 100644 (file)
@@ -4,6 +4,16 @@
 {
     "Netconf":
     {
+        // Three flags control netconf (default values are true):
+        // - "boot-update" about the YANG configuration load when
+        //   netconf boots.
+        // - "subscribe-changes" about the subscription to notifications
+        //   when the running YANG module is changed.
+        // - "validate-changes" alloes to validate or not changes.
+        "boot-update": true,
+        "subscribe-changes": true,
+        "validate-changes": true,
+
         // This map specifies how each server is managed:
         // the YANG model to use and the control channel.
         "managed-servers":
                 // DHCPv4 server is kea-dhcp4-server model.
                 "model": "kea-dhcp4-server",
 
+                // The three control flags can be defined in this scope too
+                // and takes precedence over global and default values.
+                "boot-update": false,
+                "subscribe-changes": false,
+                "validate-changes": false,
+
                 // Currently three control channel types are supported:
                 // - "stdout" which output the configuration on the standard
                 //   output (this is mainly for testing purposes, but you can
index a9902e17382c250dce1653bc75adb1ce54b8f74e..cc014f309424ddf9afbe8ea3a6a13483487beee2 100644 (file)
@@ -3,6 +3,14 @@
 {
     "Netconf":
     {
+        // Control flags can be defined in the global scope or
+        // in a managed server scope. Precedence are:
+        // - use the default value (true)
+        // - use the global value
+        // - use the local value.
+        // So this overwrites the default value:
+        "boot-update": false,
+
         // This map specifies how each server is managed:
         // the YANG model to use and the control channel.
         // Currently three control channel types are supported:
index 2e52efc40addd40d2a604bfa1cfe7aa49e921d99..5cddc3a5c289542a32a1ffe3b194098b8aabed80 100644 (file)
@@ -21,12 +21,29 @@ namespace isc {
 namespace netconf {
 
 NetconfConfig::NetconfConfig()
-    : servers_map_(new CfgServersMap()) {
+    : configured_globals_(Element::createMap()),
+      servers_map_(new CfgServersMap()) {
 }
 
 NetconfConfig::NetconfConfig(const NetconfConfig& orig)
-    : ConfigBase(), servers_map_(orig.servers_map_),
-      hooks_config_(orig.hooks_config_) {
+    : ConfigBase(), configured_globals_(orig.configured_globals_),
+      servers_map_(orig.servers_map_), hooks_config_(orig.hooks_config_) {
+}
+
+void
+NetconfConfig::extractConfiguredGlobals(ConstElementPtr config) {
+    if (config->getType() != Element::map) {
+        isc_throw(BadValue,
+                  "extractConfiguredGlobals must be given a map element");
+    }
+
+    const std::map<std::string, ConstElementPtr>& values = config->mapValue();
+    for (auto value = values.begin(); value != values.end(); ++value) {
+        if (value->second->getType() != Element::list &&
+            value->second->getType() != Element::map) {
+            addConfiguredGlobal(value->first, value->second);
+        }
+    }
 }
 
 NetconfCfgMgr::NetconfCfgMgr()
@@ -81,9 +98,13 @@ NetconfCfgMgr::parse(isc::data::ConstElementPtr config_set,
 
     NetconfConfigPtr ctx = getNetconfConfig();
 
-    // Set the defaults
+    // Preserve all scalar global parameters.
+    ctx->extractConfiguredGlobals(config_set);
+
+    // Set the defaults and derive parameters.
     ElementPtr cfg = boost::const_pointer_cast<Element>(config_set);
     NetconfSimpleParser::setAllDefaults(cfg);
+    NetconfSimpleParser::deriveParameters(cfg);
 
     // And parse the configuration.
     ConstElementPtr answer;
@@ -121,13 +142,13 @@ NetconfCfgMgr::parse(isc::data::ConstElementPtr config_set,
     return (answer);
 }
 
-
-
 ElementPtr
 NetconfConfig::toElement() const {
     ElementPtr netconf = Element::createMap();
     // Set user-context
     contextToElement(netconf);
+    // Add in explicitly configured globals.
+    netconf->setValue(configured_globals_->mapValue());
     // Set hooks-libraries
     netconf->set("hooks-libraries", hooks_config_.toElement());
     // Set managed-servers
index 131f4e696bae2e35e761394c9ff75e61efd45c5b..ba1906ae3974807af39d8839069dcf969281030c 100644 (file)
@@ -35,6 +35,23 @@ public:
     /// @brief Default constructor
     NetconfConfig();
 
+    /// @brief Returns pointer to configured global parameters.
+    isc::data::ConstElementPtr getConfiguredGlobals() const {
+        return (isc::data::ConstElementPtr(configured_globals_));
+    }
+
+    /// @brief Saves scalar elements from the global scope of a configuration.
+    void extractConfiguredGlobals(isc::data::ConstElementPtr config);
+
+    /// @brief Adds a parameter to the collection configured globals.
+    ///
+    /// @param name std::string name of the global to add.
+    /// @param value ElementPtr containing the value of the global.
+    void addConfiguredGlobal(const std::string& name,
+                             isc::data::ConstElementPtr value) {
+        configured_globals_->set(name, value);
+    }
+
     /// @brief Returns non-const reference to the managed servers map.
     ///
     /// @return non-const reference to the managed servers map.
@@ -88,6 +105,9 @@ private:
     /// @param rhs Context to be assigned.
     NetconfConfig& operator=(const NetconfConfig& rhs);
 
+    /// @brief Stores the global parameters specified via configuration.
+    isc::data::ElementPtr configured_globals_;
+
     /// @brief CfgServers map.
     CfgServersMapPtr servers_map_;
 
index 0784d15aed576409ff1b3174f510c6f3643ceff3..79f68ffa347568b500123d433a42f8f8ae4a74a1 100644 (file)
@@ -79,7 +79,8 @@ CfgControlSocket::toElement() const {
 
 // *********************** CfgServer  *************************
 CfgServer::CfgServer(const string& model, CfgControlSocketPtr ctrl_sock)
-    : model_(model), control_socket_(ctrl_sock) {
+    : model_(model), boot_update_(true), subscribe_changes_(true),
+      validate_changes_(true), control_socket_(ctrl_sock) {
 }
 
 CfgServer::~CfgServer() {
@@ -114,6 +115,12 @@ CfgServer::toElement() const {
     contextToElement(result);
     // Set model
     result->set("model", Element::create(model_));
+    // Set boot-update
+    result->set("boot-update", Element::create(boot_update_));
+    // Set subscribe-changes
+    result->set("subscribe-changes", Element::create(subscribe_changes_));
+    // Set validate-changes
+    result->set("validate-changes", Element::create(validate_changes_));
     // Set control-socket
     if (control_socket_) {
         result->set("control-socket", control_socket_->toElement());
@@ -192,6 +199,11 @@ ServerConfigParser::parse(ConstElementPtr server_config) {
                   << server_config->getPosition() << ")");
     }
 
+    // Add flags.
+    result->setBootUpdate(getBoolean(server_config, "boot-update"));
+    result->setSubscribeChanges(getBoolean(server_config, "subscribe-changes"));
+    result->setValidateChanges(getBoolean(server_config, "validate-changes"));
+
     // Add user-context.
     if (user_context) {
         result->setContext(user_context);
index 43bf1c11aff949a237cff02b4d94d8653785fbbc..34620c636808606ea1c60b6ac4ec577ddd2332ff 100644 (file)
@@ -172,6 +172,48 @@ public:
         return (control_socket_);
     }
 
+    /// @brief Getter which returns the boot-update flag.
+    ///
+    /// @return returns the boot-update flag as a bool.
+    bool getBootUpdate() const {
+        return (boot_update_);
+    }
+
+    /// @brief Set the boot-update flag.
+    ///
+    /// @param boot_update The boot-update flag.
+    void setBootUpdate(bool boot_update) {
+        boot_update_ = boot_update;
+    }
+
+    /// @brief Getter which returns the subscribe-changes flag.
+    ///
+    /// @return returns the subscribe-changes flag as a bool.
+    bool getSubscribeChanges() const {
+        return (subscribe_changes_);
+    }
+
+    /// @brief Set the subscribe-changes flag.
+    ///
+    /// @param subscribe_changes The subscribe-changes flag.
+    void setSubscribeChanges(bool subscribe_changes) {
+        subscribe_changes_ = subscribe_changes;
+    }
+
+    /// @brief Getter which returns the validate-changes flag.
+    ///
+    /// @return returns the validate-changes flag as a bool.
+    bool getValidateChanges() const {
+        return (validate_changes_);
+    }
+
+    /// @brief Set the validate-changes flag.
+    ///
+    /// @param validate_changes The validate-changes flag.
+    void setValidateChanges(bool validate_changes) {
+        validate_changes_ = validate_changes;
+    }
+
     /// @brief Returns a text representation for the server.
     std::string toText() const;
 
@@ -184,6 +226,24 @@ private:
     /// @brief The model name.
     const std::string model_;
 
+    /// @brief The boot-update flag.
+    ///
+    /// If true (the defaul) Kea server configuration is updated at (netconf
+    /// agent) boot time.
+    bool boot_update_;
+
+    /// @brief The subscribe-changes flag.
+    ///
+    /// If true (the deault) the netconf agent subscribes module changes
+    /// so will be notified when the YANG running configuration is changed.
+    bool subscribe_changes_;
+
+    /// @brief The validate-changes flag.
+    ///
+    /// If true (the deault) the netconf agent validates module changes
+    /// and can reject bad configurations.
+    bool validate_changes_;
+
     /// @brief The control socket.
     CfgControlSocketPtr control_socket_;
 };
index 5f5591b069eb1e2d81e89d245c8af1074cdc53e2..0a4734b05865e08c9921c20457f655c873bf5f1a 100644 (file)
@@ -36,6 +36,9 @@ namespace netconf {
 ///
 /// These are global Netconf parameters.
 const SimpleDefaults NetconfSimpleParser::NETCONF_DEFAULTS = {
+    { "boot-update",       Element::boolean, "true" },
+    { "subscribe-changes", Element::boolean, "true" },
+    { "validate-changes",  Element::boolean, "true" }
 };
 
 /// Supplies defaults for control-socket elements
@@ -65,6 +68,18 @@ const SimpleDefaults NetconfSimpleParser::CA_DEFAULTS = {
     { "model", Element::string, "kea-ctrl-agent" }
 };
 
+/// @brief List of parameters that can be inherited to managed-servers scope.
+///
+/// Some parameters may be defined on both global (directly in Netconf) and
+/// servers (Netconf/managed-servers/...) scope. If not defined in the
+/// managed-servers scope, the value is being inherited (derived) from
+/// the global scope. This array lists all of such parameters.
+const ParamsList NetconfSimpleParser::INHERIT_TO_SERVERS = {
+    "boot-update",
+    "subscribe-changes",
+    "validate-changes"
+};
+
 /// @}
 
 /// ---------------------------------------------------------------------------
@@ -87,6 +102,24 @@ size_t NetconfSimpleParser::setAllDefaults(const ElementPtr& global) {
     return (cnt);
 }
 
+size_t NetconfSimpleParser::deriveParameters(ConstElementPtr global) {
+    size_t cnt = 0;
+
+    // Now derive global parameters into managed-servers.
+    ConstElementPtr servers = global->get("managed-servers");
+    if (servers) {
+        for (auto it : servers->mapValue()) {
+            ElementPtr mutable_server =
+                boost::const_pointer_cast<Element>(it.second);
+            cnt += SimpleParser::deriveParams(global,
+                                              mutable_server,
+                                              INHERIT_TO_SERVERS);
+        }
+    }
+
+    return (cnt);
+}
+
 size_t
 NetconfSimpleParser::setServerDefaults(const std::string name,
                                        ConstElementPtr server) {
index eb13983e11d967e20b521bbf9a93c0e5d1ad7af6..5c9324dae904356e663d6cc195cac784b947c3ec 100644 (file)
@@ -30,6 +30,14 @@ public:
     /// @return number of default values added
     static size_t setAllDefaults(const isc::data::ElementPtr& global);
 
+    /// @brief Derives (inherits) all parameters from global to more specific scopes.
+    ///
+    /// This method currently does the following:
+    /// - derives global parameters to managed servers (flags for now)
+    /// @param global scope to be modified if needed
+    /// @return number of default values derived
+    static size_t deriveParameters(isc::data::ConstElementPtr global);
+
     /// @brief Adds default values to a Managed server entry.
     ///
     /// Adds server specific defaults, e.g. the default model.
@@ -58,6 +66,7 @@ public:
     static const isc::data::SimpleDefaults DHCP6_DEFAULTS;
     static const isc::data::SimpleDefaults D2_DEFAULTS;
     static const isc::data::SimpleDefaults CA_DEFAULTS;
+    static const isc::data::ParamsList INHERIT_TO_SERVERS;
 };
 
 };
index c08156245abaac7382b847ee6aac78dd9b10d949..d4634391036804d6292a86e869878b02435fdd59 100644 (file)
 #include <process/testutils/d_test_stubs.h>
 #include <process/d_cfg_mgr.h>
 #include <yang/yang_models.h>
+#include <testutils/test_to_element.h>
 #include <netconf/tests/test_libraries.h>
 #include <boost/scoped_ptr.hpp>
 #include <gtest/gtest.h>
 
 using namespace std;
+using namespace isc;
 using namespace isc::netconf;
 using namespace isc::config;
 using namespace isc::data;
@@ -135,6 +137,59 @@ TEST(NetconfCfgMgr, contextHookParams) {
     EXPECT_EQ(libs.get(), stored_libs.get());
 }
 
+// Tests if the context can store and retrieve globals.
+TEST(NetconfCfgMgr, contextGlobals) {
+    NetconfConfig ctx;
+
+    // By default there should be no globals.
+    ConstElementPtr globals = ctx.getConfiguredGlobals();
+    ASSERT_TRUE(globals);
+    ASSERT_EQ(Element::map, globals->getType());
+    EXPECT_EQ(0, globals->mapValue().size());
+
+    // Attempting to extract globals from a non-map should throw.
+    ASSERT_THROW(ctx.extractConfiguredGlobals(Element::create(777)), BadValue);
+
+    // Now let's create a configuration from which to extract global scalars.
+    // Extraction (currently) has no business logic, so the elements we use
+    // can be arbitrary.
+    ConstElementPtr global_cfg;
+    string global_cfg_str =
+    "{\n"
+    " \"astring\": \"okay\",\n"
+    " \"amap\": { \"not-this\":777, \"not-that\": \"poo\" },\n"
+    " \"anint\": 444,\n"
+    " \"alist\": [ 1, 2, 3 ],\n"
+    " \"abool\": true\n"
+        "}\n";
+    ASSERT_NO_THROW(global_cfg = Element::fromJSON(global_cfg_str));
+
+    // Extract globals from the config.
+    ASSERT_NO_THROW(ctx.extractConfiguredGlobals(global_cfg));
+
+    // Now see if the extract was correct.
+    globals = ctx.getConfiguredGlobals();
+    ASSERT_TRUE(globals);
+    ASSERT_EQ(Element::map, globals->getType());
+    EXPECT_NE(0, globals->mapValue().size());
+
+    // Maps and lists should be excluded.
+    for (auto it : globals->mapValue()) {
+        if (it.first == "astring") {
+            ASSERT_EQ(Element::string, it.second->getType());
+            EXPECT_EQ("okay", it.second->stringValue());
+        } else if (it.first == "anint") {
+            ASSERT_EQ(Element::integer, it.second->getType());
+            EXPECT_EQ(444, it.second->intValue());
+        } else if (it.first == "abool") {
+            ASSERT_EQ(Element::boolean, it.second->getType());
+            EXPECT_TRUE(it.second->boolValue());
+        } else {
+            ADD_FAILURE() << "unexpected element found:" << it.first;
+        }
+    }
+}
+
 /// Netconf configurations used in tests.
 const char* NETCONF_CONFIGS[] = {
 
@@ -143,12 +198,17 @@ const char* NETCONF_CONFIGS[] = {
 
     // Configuration 1: global parameters only (no server, not hooks)
     "{\n"
+    "    \"boot-update\": false,\n"
+    "    \"subscribe-changes\": false,\n"
+    "    \"validate-changes\": false\n"
     "}",
 
     // Configuration 2: 1 server
     "{\n"
+    "    \"boot-update\": false,\n"
     "    \"managed-servers\": {\n"
     "        \"dhcp4\": {\n"
+    "            \"boot-update\": true,\n"
     "            \"control-socket\": {\n"
     "                \"socket-name\": \"/tmp/socket-v4\"\n"
     "            }\n"
@@ -158,8 +218,10 @@ const char* NETCONF_CONFIGS[] = {
 
     // Configuration 3: all 4 servers
     "{\n"
+    "    \"boot-update\": false,\n"
     "    \"managed-servers\": {\n"
     "        \"dhcp4\": {\n"
+    "            \"boot-update\": true,\n"
     "            \"control-socket\": {\n"
     "                \"socket-name\": \"/tmp/socket-v4\"\n"
     "            }\n"
@@ -170,6 +232,7 @@ const char* NETCONF_CONFIGS[] = {
     "            }\n"
     "        },\n"
     "        \"d2\": {\n"
+    "            \"subscribe-changes\": false,\n"
     "            \"control-socket\": {\n"
     "                \"socket-name\": \"/tmp/socket-d2\"\n"
     "            }\n"
@@ -207,6 +270,7 @@ const char* NETCONF_CONFIGS[] = {
     "{\n"
     "    \"managed-servers\": {\n"
     "        \"d2\": {\n"
+    "            \"subscribe-changes\": false,\n"
     "            \"control-socket\": {\n"
     "                \"socket-name\": \"/tmp/socket-d2\"\n"
     "            }\n"
@@ -255,6 +319,9 @@ const char* NETCONF_CONFIGS[] = {
 
     // Configuration 9: empty control socket
     "{\n"
+    "    \"boot-update\": false,\n"
+    "    \"subscribe-changes\": false,\n"
+    "    \"validate-changes\": false,\n"
     "    \"managed-servers\": {\n"
     "        \"dhcp4\": {\n"
     "            \"control-socket\": {\n"
@@ -354,6 +421,13 @@ TEST_F(NetconfParserTest, configParseGlobalOnly) {
     ASSERT_TRUE(ctx);
     ASSERT_TRUE(ctx->getCfgServersMap());
     EXPECT_EQ(0, ctx->getCfgServersMap()->size());
+    ConstElementPtr globals = ctx->getConfiguredGlobals();
+    ASSERT_TRUE(globals);
+    string expected = "{ "
+        "\"boot-update\": false, "
+        "\"subscribe-changes\": false, "
+        "\"validate-changes\": false }";
+    EXPECT_EQ(expected, globals->str());
 }
 
 // Tests if an empty (i.e. without a control socket) can be configured.
@@ -369,6 +443,10 @@ TEST_F(NetconfParserTest, configParseEmptyCfgServer) {
     CfgServerPtr server = ctx->getCfgServersMap()->at("dhcp4");
     ASSERT_TRUE(server);
     EXPECT_EQ(KEA_DHCP4_SERVER, server->getModel());
+    // Defaults.
+    EXPECT_TRUE(server->getBootUpdate());
+    EXPECT_TRUE(server->getSubscribeChanges());
+    EXPECT_TRUE(server->getValidateChanges());
     CfgControlSocketPtr socket = server->getCfgControlSocket();
     EXPECT_FALSE(socket);
 }
@@ -386,6 +464,10 @@ TEST_F(NetconfParserTest, configParseDefaults) {
     CfgServerPtr server = ctx->getCfgServersMap()->at("dhcp4");
     ASSERT_TRUE(server);
     EXPECT_EQ(KEA_DHCP4_SERVER, server->getModel());
+    // Globals overwrite defaults.
+    EXPECT_FALSE(server->getBootUpdate());
+    EXPECT_FALSE(server->getSubscribeChanges());
+    EXPECT_FALSE(server->getValidateChanges());
     CfgControlSocketPtr socket = server->getCfgControlSocket();
     ASSERT_TRUE(socket);
 
@@ -407,6 +489,10 @@ TEST_F(NetconfParserTest, configParseServerDhcp4) {
     CfgServerPtr server = ctx->getCfgServersMap()->at("dhcp4");
     ASSERT_TRUE(server);
     EXPECT_EQ(KEA_DHCP4_SERVER, server->getModel());
+    // Locals overwrite globals.
+    EXPECT_TRUE(server->getBootUpdate());
+    EXPECT_TRUE(server->getSubscribeChanges());
+    EXPECT_TRUE(server->getValidateChanges());
     CfgControlSocketPtr socket = server->getCfgControlSocket();
     ASSERT_TRUE(socket);
     EXPECT_EQ(CfgControlSocket::Type::STDOUT, socket->getType());
@@ -426,6 +512,9 @@ TEST_F(NetconfParserTest, configParseServerD2) {
     CfgServerPtr server = ctx->getCfgServersMap()->at("d2");
     ASSERT_TRUE(server);
     EXPECT_EQ(KEA_DHCP_DDNS, server->getModel());
+    EXPECT_TRUE(server->getBootUpdate());
+    EXPECT_FALSE(server->getSubscribeChanges());
+    EXPECT_TRUE(server->getValidateChanges());
     CfgControlSocketPtr socket = server->getCfgControlSocket();
     ASSERT_TRUE(socket);
     EXPECT_EQ(CfgControlSocket::Type::STDOUT, socket->getType());
@@ -466,6 +555,9 @@ TEST_F(NetconfParserTest, configParse4Servers) {
     CfgServerPtr server = ctx->getCfgServersMap()->at("dhcp4");
     ASSERT_TRUE(server);
     EXPECT_EQ(KEA_DHCP4_SERVER, server->getModel());
+    EXPECT_TRUE(server->getBootUpdate());
+    EXPECT_TRUE(server->getSubscribeChanges());
+    EXPECT_TRUE(server->getValidateChanges());
     CfgControlSocketPtr socket = server->getCfgControlSocket();
     ASSERT_TRUE(socket);
     EXPECT_EQ(CfgControlSocket::Type::STDOUT, socket->getType());
@@ -477,6 +569,9 @@ TEST_F(NetconfParserTest, configParse4Servers) {
     ASSERT_TRUE(server);
     EXPECT_EQ(KEA_DHCP6_SERVER, server->getModel());
     socket = server->getCfgControlSocket();
+    EXPECT_FALSE(server->getBootUpdate());
+    EXPECT_TRUE(server->getSubscribeChanges());
+    EXPECT_TRUE(server->getValidateChanges());
     ASSERT_TRUE(socket);
     EXPECT_EQ(CfgControlSocket::Type::STDOUT, socket->getType());
     EXPECT_EQ("/tmp/socket-v6", socket->getName());
@@ -486,6 +581,9 @@ TEST_F(NetconfParserTest, configParse4Servers) {
     server = ctx->getCfgServersMap()->at("d2");
     ASSERT_TRUE(server);
     EXPECT_EQ(KEA_DHCP_DDNS, server->getModel());
+    EXPECT_FALSE(server->getBootUpdate());
+    EXPECT_FALSE(server->getSubscribeChanges());
+    EXPECT_TRUE(server->getValidateChanges());
     socket = server->getCfgControlSocket();
     ASSERT_TRUE(socket);
     EXPECT_EQ(CfgControlSocket::Type::STDOUT, socket->getType());
@@ -496,11 +594,69 @@ TEST_F(NetconfParserTest, configParse4Servers) {
     server = ctx->getCfgServersMap()->at("ca");
     ASSERT_TRUE(server);
     EXPECT_EQ(KEA_CTRL_AGENT, server->getModel());
+    EXPECT_FALSE(server->getBootUpdate());
+    EXPECT_TRUE(server->getSubscribeChanges());
+    EXPECT_TRUE(server->getValidateChanges());
     socket = server->getCfgControlSocket();
     ASSERT_TRUE(socket);
     EXPECT_EQ(CfgControlSocket::Type::STDOUT, socket->getType());
     EXPECT_EQ("/tmp/socket-ca", socket->getName());
     EXPECT_EQ("http://127.0.0.1:8000/", socket->getUrl().toText());
+
+    // Check unparsing.
+    string expected =  "{\n"
+        "    \"Netconf\": {\n"
+        "        \"boot-update\": false,\n"
+        "        \"managed-servers\": {\n"
+        "            \"dhcp4\": {\n"
+        "                \"model\": \"kea-dhcp4-server\",\n"
+        "                \"boot-update\": true,\n"
+        "                \"subscribe-changes\": true,\n"
+        "                \"validate-changes\": true,\n"
+        "                \"control-socket\": {\n"
+        "                    \"socket-type\": \"stdout\",\n"
+        "                    \"socket-name\": \"/tmp/socket-v4\",\n"
+        "                    \"socket-url\": \"http://127.0.0.1:8000/\"\n"
+        "                }\n"
+        "            },\n"
+        "            \"dhcp6\": {\n"
+        "                \"model\": \"kea-dhcp6-server\",\n"
+        "                \"boot-update\": false,\n"
+        "                \"subscribe-changes\": true,\n"
+        "                \"validate-changes\": true,\n"
+        "                \"control-socket\": {\n"
+        "                    \"socket-type\": \"stdout\",\n"
+        "                    \"socket-name\": \"/tmp/socket-v6\",\n"
+        "                    \"socket-url\": \"http://127.0.0.1:8000/\"\n"
+        "                }\n"
+        "            },\n"
+        "            \"d2\": {\n"
+        "                \"model\": \"kea-dhcp-ddns\",\n"
+        "                \"boot-update\": false,\n"
+        "                \"subscribe-changes\": false,\n"
+        "                \"validate-changes\": true,\n"
+        "                \"control-socket\": {\n"
+        "                    \"socket-type\": \"stdout\",\n"
+        "                    \"socket-name\": \"/tmp/socket-d2\",\n"
+        "                    \"socket-url\": \"http://127.0.0.1:8000/\"\n"
+        "                }\n"
+        "            },\n"
+        "            \"ca\": {\n"
+        "                \"model\": \"kea-ctrl-agent\",\n"
+        "                \"boot-update\": false,\n"
+        "                \"subscribe-changes\": true,\n"
+        "                \"validate-changes\": true,\n"
+        "                \"control-socket\": {\n"
+        "                    \"socket-type\": \"stdout\",\n"
+        "                    \"socket-name\": \"/tmp/socket-ca\",\n"
+        "                    \"socket-url\": \"http://127.0.0.1:8000/\"\n"
+        "                }\n"
+        "            }\n"
+        "        },\n"
+        "        \"hooks-libraries\": [ ]\n"
+        "    }\n"
+        "}";
+    isc::test::runToElementTest<NetconfConfig>(expected, *ctx);
 }
 
 // Tests the handling of invalid socket URL.
index 12c80a2e52d09becba87df0978b435ee99437e46..1b26a9f198639ecc727b00154c1c2bd28ab0cad8 100644 (file)
@@ -24,6 +24,8 @@ namespace {
 /// @brief Valid Netconf Config used in tests.
 const char* valid_netconf_config =
     "{"
+    "  \"boot-update\": false,"
+    "  \"subscribe-changes\": false,"
     "  \"managed-servers\": {"
     "    \"dhcp4\": {"
     "      \"control-socket\": {"
index 0bcd33829d662a7c54f8a598e02f95ddb5bd4a8b..f3d8ff2b5b4860cbaf3e544c3fd2d3d0482e8759 100644 (file)
@@ -124,9 +124,15 @@ TEST(ParserTest, keywordJSON) {
 // be parsed with syntactic checking (and as pure JSON).
 TEST(ParserTest, keywordNetconf) {
     string txt = "{ \"Netconf\": {\n"
+        "    \"boot-update\": true,"
+        "    \"subscribe-changes\": true,"
+        "    \"validate-changes\": true,"
         "    \"managed-servers\": {"
         "        \"dhcp4\": {"
         "            \"model\": \"kea-dhcp4-server\","
+        "            \"boot-update\": false,"
+        "            \"subscribe-changes\": false,"
+        "            \"validate-changes\": false,"
         "            \"control-socket\": {"
         "                \"socket-type\": \"unix\","
         "                \"socket-name\": \"/path/to/the/unix/socket-v4\""
@@ -134,6 +140,9 @@ TEST(ParserTest, keywordNetconf) {
         "        },"
         "        \"dhcp6\": {"
         "            \"model\": \"kea-dhcp6-server\","
+        "            \"boot-update\": false,"
+        "            \"subscribe-changes\": false,"
+        "            \"validate-changes\": false,"
         "            \"control-socket\": {"
         "                \"socket-type\": \"http\","
         "                \"socket-url\": \"http://127.0.0.1:12345/\""
@@ -141,12 +150,18 @@ TEST(ParserTest, keywordNetconf) {
         "        },"
         "        \"d2\": {"
         "            \"model\": \"kea-dhcp-ddns\","
+        "            \"boot-update\": false,"
+        "            \"subscribe-changes\": false,"
+        "            \"validate-changes\": false,"
         "            \"control-socket\": {"
         "                \"socket-type\": \"stdout\""
         "            }"
         "        },"
         "        \"ca\": {"
         "            \"model\": \"kea-ctrl-agent\","
+        "            \"boot-update\": false,"
+        "            \"subscribe-changes\": false,"
+        "            \"validate-changes\": false,"
         "            \"control-socket\": {"
         "                \"socket-type\": \"http\","
         "                \"user-context\": { \"use default\": true }"
@@ -174,9 +189,15 @@ TEST(ParserTest, keywordSubNetconf) {
     // This is similar to previous test, but note the lack of outer
     // map and Netconf.
     string txt = "{\n"
+        "    \"boot-update\": true,"
+        "    \"subscribe-changes\": true,"
+        "    \"validate-changes\": true,"
         "    \"managed-servers\": {"
         "        \"dhcp4\": {"
         "            \"model\": \"kea-dhcp4-server\","
+        "            \"boot-update\": false,"
+        "            \"subscribe-changes\": false,"
+        "            \"validate-changes\": false,"
         "            \"control-socket\": {"
         "                \"socket-type\": \"unix\","
         "                \"socket-name\": \"/path/to/the/unix/socket-v4\""
@@ -184,6 +205,9 @@ TEST(ParserTest, keywordSubNetconf) {
         "        },"
         "        \"dhcp6\": {"
         "            \"model\": \"kea-dhcp6-server\","
+        "            \"boot-update\": false,"
+        "            \"subscribe-changes\": false,"
+        "            \"validate-changes\": false,"
         "            \"control-socket\": {"
         "                \"socket-type\": \"http\","
         "                \"socket-url\": \"http://127.0.0.1:12345/\""
@@ -191,13 +215,18 @@ TEST(ParserTest, keywordSubNetconf) {
         "        },"
         "        \"d2\": {"
         "            \"model\": \"kea-dhcp-ddns\","
+        "            \"boot-update\": false,"
+        "            \"subscribe-changes\": false,"
+        "            \"validate-changes\": false,"
         "            \"control-socket\": {"
         "                \"socket-type\": \"stdout\""
         "            }"
         "        },"
         "        \"ca\": {"
         "            \"model\": \"kea-ctrl-agent\","
-        "            \"model\": \"kea-dhcp6-server\","
+        "            \"boot-update\": false,"
+        "            \"subscribe-changes\": false,"
+        "            \"validate-changes\": false,"
         "            \"control-socket\": {"
         "                \"socket-type\": \"http\","
         "                \"user-context\": { \"use default\": true }"
index 02185fa42f137884b60ca2979bd2814427ea7790..fbcf8fc755e2c76c8789fe91aadaa658ca9b6fb9 100644 (file)
@@ -1,5 +1,6 @@
 {
     "Netconf": {
+        "boot-update": false,
         "hooks-libraries": [
             {
                 "library": "/tmp/ky/src/bin/netconf/tests/.libs/libbasic.so",
         ],
         "managed-servers": {
             "ca": {
+                "boot-update": false,
                 "control-socket": {
                     "socket-name": "",
                     "socket-type": "http",
                     "socket-url": "http://127.0.0.1:8000/"
                 },
-                "model": "kea-ctrl-agent"
+                "model": "kea-ctrl-agent",
+                "subscribe-changes": true,
+                "validate-changes": true
             },
             "d2": {
+                "boot-update": false,
                 "control-socket": {
                     "socket-name": "",
                     "socket-type": "stdout",
                         "in-use": false
                     }
                 },
-                "model": "kea-dhcp-ddns"
+                "model": "kea-dhcp-ddns",
+                "subscribe-changes": true,
+                "validate-changes": true
             },
             "dhcp4": {
                 "comment": "DHCP4 server",
+                "boot-update": false,
                 "control-socket": {
                     "socket-name": "/path/to/the/unix/socket-v4",
                     "socket-type": "unix",
                     "socket-url": "http://127.0.0.1:8000/"
                 },
-                "model": "kea-dhcp4-server"
+                "model": "kea-dhcp4-server",
+                "subscribe-changes": true,
+                "validate-changes": true
             },
             "dhcp6": {
+                "boot-update": false,
                 "control-socket": {
                     "socket-name": "/path/to/the/unix/socket-v6",
                     "socket-type": "unix",
                     "socket-url": "http://127.0.0.1:8000/"
                 },
-                "model": "kea-dhcp6-server"
+                "model": "kea-dhcp6-server",
+                "subscribe-changes": true,
+                "validate-changes": true
             }
         }
     }