]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3944] add support for CB global scalar lists
authorRazvan Becheriu <razvan@isc.org>
Thu, 17 Jul 2025 21:03:07 +0000 (00:03 +0300)
committerRazvan Becheriu <razvan@isc.org>
Thu, 24 Jul 2025 10:41:29 +0000 (13:41 +0300)
30 files changed:
doc/sphinx/arm/dhcp4-srv.rst
doc/sphinx/arm/dhcp6-srv.rst
src/bin/dhcp4/json_config_parser.cc
src/bin/dhcp4/tests/config_backend_unittest.cc
src/bin/dhcp6/json_config_parser.cc
src/bin/dhcp6/tests/config_backend_unittest.cc
src/bin/dhcp6/tests/parser_unittest.cc
src/lib/dhcpsrv/cb_ctl_dhcp.h
src/lib/dhcpsrv/cb_ctl_dhcp4.cc
src/lib/dhcpsrv/cb_ctl_dhcp6.cc
src/lib/dhcpsrv/cfg_globals.cc
src/lib/dhcpsrv/cfg_globals.h
src/lib/dhcpsrv/parsers/host_reservation_parser.cc
src/lib/dhcpsrv/parsers/host_reservation_parser.h
src/lib/dhcpsrv/parsers/simple_parser4.cc
src/lib/dhcpsrv/parsers/simple_parser4.h
src/lib/dhcpsrv/parsers/simple_parser6.cc
src/lib/dhcpsrv/parsers/simple_parser6.h
src/lib/dhcpsrv/srv_config.cc
src/lib/dhcpsrv/srv_config.h
src/lib/dhcpsrv/tests/host_reservation_parser_unittest.cc
src/lib/dhcpsrv/tests/srv_config_unittest.cc
src/lib/dhcpsrv/testutils/generic_backend_unittest.cc
src/lib/dhcpsrv/testutils/generic_backend_unittest.h
src/share/api/remote-global-parameter4-get-all.json
src/share/api/remote-global-parameter4-get.json
src/share/api/remote-global-parameter4-set.json
src/share/api/remote-global-parameter6-get-all.json
src/share/api/remote-global-parameter6-get.json
src/share/api/remote-global-parameter6-set.json

index f24e063e82bd33aa36a42b28a8edbbc8b388bdc8..9fb21f8f64ceb838b7dd2f4918e1a480f2fa28bc 100644 (file)
@@ -8569,6 +8569,16 @@ Some scalar parameters contained by top-level global maps are supported by the c
    | dhcp-queue-control.capacity                                      | dhcp-queue-control           | capacity                         |
    +------------------------------------------------------------------+------------------------------+----------------------------------+
 
+Some scalar parameters contained by top-level global list are supported by the configuration backend.
+
+.. table:: List of DHCPv4 list parameters supported by the configuration backend
+
+   +------------------------------------------------------------------+
+   | Parameter name                | List container parameter type    |
+   +==================================================================+
+   | host-reservation-identifiers  | string                           |
+   +------------------------------------------------------------------+
+
 .. _dhcp4-cb-json:
 
 Enabling the Configuration Backend
index d8e87e538d79d925c15e378951ba647e04ab8181..7275043484554b010396af5c1ecfdee9590a8572 100644 (file)
@@ -8404,6 +8404,16 @@ Some scalar parameters contained by top level global maps are supported by the c
    | dhcp-queue-control.capacity                                      | dhcp-queue-control           | capacity                         |
    +------------------------------------------------------------------+------------------------------+----------------------------------+
 
+Some scalar parameters contained by top-level global list are supported by the configuration backend.
+
+.. table:: List of DHCPv6 list parameters supported by the configuration backend
+
+   +------------------------------------------------------------------+
+   | Parameter name                | List container parameter type    |
+   +==================================================================+
+   | host-reservation-identifiers  | string                           |
+   +------------------------------------------------------------------+
+
 .. _dhcp6-cb-json:
 
 Enabling the Configuration Backend
index ebab5530e52c82d1c16f446a2532c65f7e1cb290..8bee0e3585a1ddf64fb38fd375650712899f3fab 100644 (file)
@@ -459,7 +459,7 @@ processDhcp4Config(isc::data::ConstElementPtr config_set) {
             mutable_cfg->get("host-reservation-identifiers");
         if (hr_identifiers) {
             parameter_name = "host-reservation-identifiers";
-            HostReservationIdsParser4 parser;
+            HostReservationIdsParser4 parser(srv_config->getCfgHostOperations4());
             parser.parse(hr_identifiers);
         }
 
index 4b5f08fd55c74ffaf75ceab32fe0f0cd7b9a45c5..dc8f7df2f7c657168ee8acc9acdcea78c19dc4ba 100644 (file)
@@ -194,6 +194,7 @@ TEST_F(Dhcp4CBTest, mergeGlobals) {
     StampedValuePtr renew_timer(new StampedValue("renew-timer", Element::create(500)));
     StampedValuePtr mt_enabled(new StampedValue("multi-threading.enable-multi-threading", Element::create(true)));
     StampedValuePtr mt_pool_size(new StampedValue("multi-threading.thread-pool-size", Element::create(256)));
+    StampedValuePtr hr_identifiers(new StampedValue("host-reservation-identifiers", "[ \"hw-address\", \"flex-id\" ]"));
 
     // Let's add all of the globals to the second backend.  This will verify
     // we find them there.
@@ -204,6 +205,7 @@ TEST_F(Dhcp4CBTest, mergeGlobals) {
     db2_->createUpdateGlobalParameter4(ServerSelector::ALL(), renew_timer);
     db2_->createUpdateGlobalParameter4(ServerSelector::ALL(), mt_enabled);
     db2_->createUpdateGlobalParameter4(ServerSelector::ALL(), mt_pool_size);
+    db2_->createUpdateGlobalParameter4(ServerSelector::ALL(), hr_identifiers);
 
     // Should parse and merge without error.
     ASSERT_NO_FATAL_FAILURE(configure(base_config, CONTROL_RESULT_SUCCESS, ""));
@@ -233,6 +235,12 @@ TEST_F(Dhcp4CBTest, mergeGlobals) {
     ASSERT_NO_FATAL_FAILURE(checkConfiguredGlobal(staging_cfg, renew_timer));
     ASSERT_NO_FATAL_FAILURE(checkConfiguredGlobal(staging_cfg, mt_enabled));
     ASSERT_NO_FATAL_FAILURE(checkConfiguredGlobal(staging_cfg, mt_pool_size));
+    ASSERT_NO_FATAL_FAILURE(checkConfiguredGlobal(staging_cfg, hr_identifiers, true));
+
+    auto const& ex_hr_i = staging_cfg->getCfgHostOperations4()->getIdentifierTypes();
+    EXPECT_EQ(ex_hr_i.size(), 2);
+    EXPECT_EQ(ex_hr_i.front(), Host::IDENT_HWADDR);
+    EXPECT_EQ(ex_hr_i.back(), Host::IDENT_FLEX);
 }
 
 // This test verifies that externally configured option definitions
index 1f9aec6760714058966132c3e31328412d194100..43911551337f22d3a4b33aed8dc690508362f3c0 100644 (file)
@@ -561,7 +561,7 @@ processDhcp6Config(isc::data::ConstElementPtr config_set) {
             mutable_cfg->get("host-reservation-identifiers");
         if (hr_identifiers) {
             parameter_name = "host-reservation-identifiers";
-            HostReservationIdsParser6 parser;
+            HostReservationIdsParser6 parser(srv_config->getCfgHostOperations6());
             parser.parse(hr_identifiers);
         }
 
index 690c6745a60e07b8b905768a0852c3640050cfff..afc62db890a379078f9f22d6bd0021eaa6d08a1a 100644 (file)
@@ -194,6 +194,7 @@ TEST_F(Dhcp6CBTest, mergeGlobals) {
     StampedValuePtr renew_timer(new StampedValue("renew-timer", Element::create(500)));
     StampedValuePtr mt_enabled(new StampedValue("multi-threading.enable-multi-threading", Element::create(true)));
     StampedValuePtr mt_pool_size(new StampedValue("multi-threading.thread-pool-size", Element::create(256)));
+    StampedValuePtr hr_identifiers(new StampedValue("host-reservation-identifiers", "[ \"hw-address\", \"flex-id\" ]"));
 
     // Let's add all of the globals to the second backend.  This will verify
     // we find them there.
@@ -202,6 +203,7 @@ TEST_F(Dhcp6CBTest, mergeGlobals) {
     db2_->createUpdateGlobalParameter6(ServerSelector::ALL(), renew_timer);
     db2_->createUpdateGlobalParameter6(ServerSelector::ALL(), mt_enabled);
     db2_->createUpdateGlobalParameter6(ServerSelector::ALL(), mt_pool_size);
+    db2_->createUpdateGlobalParameter6(ServerSelector::ALL(), hr_identifiers);
 
     // Should parse and merge without error.
     ASSERT_NO_FATAL_FAILURE(configure(base_config, CONTROL_RESULT_SUCCESS, ""));
@@ -225,6 +227,12 @@ TEST_F(Dhcp6CBTest, mergeGlobals) {
     ASSERT_NO_FATAL_FAILURE(checkConfiguredGlobal(staging_cfg, renew_timer));
     ASSERT_NO_FATAL_FAILURE(checkConfiguredGlobal(staging_cfg, mt_enabled));
     ASSERT_NO_FATAL_FAILURE(checkConfiguredGlobal(staging_cfg, mt_pool_size));
+    ASSERT_NO_FATAL_FAILURE(checkConfiguredGlobal(staging_cfg, hr_identifiers, true));
+
+    auto const& ex_hr_i = staging_cfg->getCfgHostOperations6()->getIdentifierTypes();
+    EXPECT_EQ(ex_hr_i.size(), 2);
+    EXPECT_EQ(ex_hr_i.front(), Host::IDENT_HWADDR);
+    EXPECT_EQ(ex_hr_i.back(), Host::IDENT_FLEX);
 }
 
 // This test verifies that externally configured option definitions
index a82ca5c149f00f0529678e817f62870d2e83be62..1e7389c330fecf5a9a7a3e9352836c8a47af261c 100644 (file)
@@ -299,34 +299,33 @@ void testFile(const std::string& fname) {
 // twice: first with the existing Element::fromJSONFile() and then
 // the second time with Parser6. Both JSON trees are then compared.
 TEST(ParserTest, file) {
-    vector<string> configs;
-    configs.push_back("advanced.json");
-    configs.push_back("all-keys.json");
-    configs.push_back("all-options.json");
-    configs.push_back("backends.json");
-    configs.push_back("classify.json");
-    configs.push_back("classify2.json");
-    configs.push_back("comments.json");
-    configs.push_back("config-backend.json");
-    configs.push_back("dhcpv4-over-dhcpv6.json");
-    configs.push_back("duid.json");
-    configs.push_back("global-reservations.json");
-    configs.push_back("ha-hot-standby-server1-with-tls.json");
-    configs.push_back("ha-hot-standby-server2.json");
-    configs.push_back("hooks.json");
-    configs.push_back("iPXE.json");
-    configs.push_back("leases-expiration.json");
-    configs.push_back("multiple-options.json");
-    configs.push_back("mysql-reservations.json");
-    configs.push_back("pgsql-reservations.json");
-    configs.push_back("reservations.json");
-    configs.push_back("several-subnets.json");
-    configs.push_back("shared-network.json");
-    configs.push_back("simple.json");
-    configs.push_back("softwire46.json");
-    configs.push_back("stateless.json");
-    configs.push_back("tee-times.json");
-    configs.push_back("with-ddns.json");
+    vector<string> configs = { "advanced.json",
+                               "all-keys.json",
+                               "all-options.json",
+                               "backends.json",
+                               "classify.json",
+                               "classify2.json",
+                               "comments.json",
+                               "config-backend.json",
+                               "dhcpv4-over-dhcpv6.json",
+                               "duid.json",
+                               "global-reservations.json",
+                               "ha-hot-standby-server1-with-tls.json",
+                               "ha-hot-standby-server2.json",
+                               "hooks.json",
+                               "iPXE.json",
+                               "leases-expiration.json",
+                               "multiple-options.json",
+                               "mysql-reservations.json",
+                               "pgsql-reservations.json",
+                               "reservations.json",
+                               "several-subnets.json",
+                               "shared-network.json",
+                               "simple.json",
+                               "softwire46.json",
+                               "stateless.json",
+                               "tee-times.json",
+                               "with-ddns.json" };
 
     for (unsigned i = 0; i<configs.size(); i++) {
         testFile(string(CFG_EXAMPLES) + "/" + configs[i]);
index fb686ad09c78c249997d830f4b749d85ed0561b2..66aa1e3600df4719dbd5af5f1f79e4744a4bd700 100644 (file)
@@ -60,24 +60,33 @@ protected:
 
     /// @brief It translates the top level map parameters from flat naming
     /// format (e.g. param-name.sub-param-name) to proper ElementMap objects and
-    /// adds all globals fetched from config backend(s) to a SrvConfig instance
+    /// adds all globals fetched from config backend(s) to a SrvConfig instance.
+    /// The top level list parameters are converted from StringElement objects to
+    /// ListElement objects.
     ///
     /// Iterates over the given collection of global parameters and adds them to
     /// the given configuration's list of configured globals.
     ///
-    ///
     /// @param external_cfg SrvConfig instance to update
     /// @param cb_globals collection of global parameters supplied by configuration
     /// backend
+    /// @param global_lists All keywords matching supported global list parameters.
     void translateAndAddGlobalsToConfig(SrvConfigPtr external_cfg,
-                                        data::StampedValueCollection& cb_globals) const {
+                                        data::StampedValueCollection& cb_globals,
+                                        data::SimpleKeywords global_lists) const {
         auto const& index = cb_globals.get<data::StampedValueNameIndexTag>();
         for (auto const& cb_global : index) {
-
             if (cb_global->amNull()) {
                 continue;
             }
-
+            data::ConstElementPtr value = cb_global->getElementValue();
+            // All ListElement parameters must be converted from StringElement
+            // (used by CB to store global list parameters).
+            data::ElementPtr mutable_value = boost::const_pointer_cast<data::Element>(value);
+            if (global_lists.count(cb_global->getName())) {
+                mutable_value = data::Element::fromJSON(value->stringValue());
+            }
+            data::ConstElementPtr global_value = boost::const_pointer_cast<data::Element>(mutable_value);
             std::string param_name;
             std::string sub_param_name;
             if (translateName(cb_global->getName(), param_name, sub_param_name)) {
@@ -85,11 +94,11 @@ protected:
                 if (!sub_param) {
                     sub_param = data::Element::createMap();
                 }
-                sub_param->set(sub_param_name, cb_global->getElementValue());
+                sub_param->set(sub_param_name, global_value);
                 external_cfg->addConfiguredGlobal(param_name, sub_param);
             } else {
                 // Reuse name and value.
-                external_cfg->addConfiguredGlobal(param_name, cb_global->getElementValue());
+                external_cfg->addConfiguredGlobal(param_name, global_value);
             }
         }
     }
index a24502cc43044c03894e2db40f3566117d1cee82..dadc5c3f5f0475b1d4ff8a34326dcb6e0dc4c4b4 100644 (file)
@@ -79,7 +79,7 @@ CBControlDHCPv4::databaseConfigApply(const BackendSelector& backend_selector,
             // database query and the number of global parameters is small.
             data::StampedValueCollection globals;
             globals = getMgr().getPool()->getAllGlobalParameters4(backend_selector, server_selector);
-            translateAndAddGlobalsToConfig(external_cfg, globals);
+            translateAndAddGlobalsToConfig(external_cfg, globals, SimpleParser4::GLOBAL4_LIST_PARAMETERS);
 
             // Add defaults.
             external_cfg->applyDefaultsConfiguredGlobals(SimpleParser4::GLOBAL4_DEFAULTS);
@@ -166,7 +166,7 @@ CBControlDHCPv4::databaseConfigApply(const BackendSelector& backend_selector,
             data::StampedValueCollection globals;
             globals = getMgr().getPool()->getModifiedGlobalParameters4(backend_selector, server_selector,
                                                                        lb_modification_time);
-            translateAndAddGlobalsToConfig(external_cfg, globals);
+            translateAndAddGlobalsToConfig(external_cfg, globals, SimpleParser4::GLOBAL4_LIST_PARAMETERS);
             globals_fetched = true;
         }
     }
index 552c8553f22297f7ac7b3535098db6bd20564ef4..6aa9b02631170ce91e28aee3130553654019c54d 100644 (file)
@@ -77,7 +77,7 @@ CBControlDHCPv6::databaseConfigApply(const db::BackendSelector& backend_selector
             // database query and the number of global parameters is small.
             data::StampedValueCollection globals;
             globals = getMgr().getPool()->getAllGlobalParameters6(backend_selector, server_selector);
-            translateAndAddGlobalsToConfig(external_cfg, globals);
+            translateAndAddGlobalsToConfig(external_cfg, globals, SimpleParser6::GLOBAL6_LIST_PARAMETERS);
 
             // Add defaults.
             external_cfg->applyDefaultsConfiguredGlobals(SimpleParser6::GLOBAL6_DEFAULTS);
@@ -165,7 +165,7 @@ CBControlDHCPv6::databaseConfigApply(const db::BackendSelector& backend_selector
             data::StampedValueCollection globals;
             globals = getMgr().getPool()->getModifiedGlobalParameters6(backend_selector, server_selector,
                                                                        lb_modification_time);
-            translateAndAddGlobalsToConfig(external_cfg, globals);
+            translateAndAddGlobalsToConfig(external_cfg, globals, SimpleParser6::GLOBAL6_LIST_PARAMETERS);
             globals_fetched = true;
         }
     }
index f9c334eff9ca72ed1f549d7721890b722658b9e0..076d802174a2d53d97cc36443023ab56001a7e63 100644 (file)
@@ -60,6 +60,7 @@ CfgGlobals::nameToIndex = {
     { "ddns-ttl", DDNS_TTL },
     { "ddns-ttl-min", DDNS_TTL_MIN },
     { "ddns-ttl-max", DDNS_TTL_MAX },
+    { "host-reservation-identifiers", HOST_RESERVATION_IDENTIFIERS },
 
     // DHCPv4 specific parameters.
     { "echo-client-id", ECHO_CLIENT_ID },
index f0bccedef3839e0209c53a05bcaa96a6f453c69c..33c938805371bb97e7f0c2b804cdf81c509b9ff4 100644 (file)
@@ -83,6 +83,7 @@ public:
         DDNS_TTL,
         DDNS_TTL_MIN,
         DDNS_TTL_MAX,
+        HOST_RESERVATION_IDENTIFIERS,
 
         // DHCPv4 specific parameters.
         ECHO_CLIENT_ID,
index c7bb9ff6c2bf198f8f0f36733607f446df0e6be8..0196aab9935d4ada405f8d33b636e573f17afea0 100644 (file)
@@ -414,8 +414,8 @@ HostReservationParser6::getSupportedParameters(const bool identifiers_only) cons
     return (getSupportedParams6(identifiers_only));
 }
 
-HostReservationIdsParser::HostReservationIdsParser()
-    : staging_cfg_() {
+HostReservationIdsParser::HostReservationIdsParser(CfgHostOperationsPtr cfg)
+    : staging_cfg_(cfg) {
 }
 
 void
@@ -476,9 +476,8 @@ HostReservationIdsParser::parseInternal(isc::data::ConstElementPtr ids_list) {
 
 }
 
-HostReservationIdsParser4::HostReservationIdsParser4()
-    : HostReservationIdsParser() {
-    staging_cfg_ = CfgMgr::instance().getStagingCfg()->getCfgHostOperations4();
+HostReservationIdsParser4::HostReservationIdsParser4(CfgHostOperationsPtr cfg)
+    : HostReservationIdsParser(cfg) {
 }
 
 bool
@@ -486,9 +485,8 @@ HostReservationIdsParser4::isSupportedIdentifier(const std::string& id_name) con
     return (getSupportedParams4(true).count(id_name) > 0);
 }
 
-HostReservationIdsParser6::HostReservationIdsParser6()
-    : HostReservationIdsParser() {
-    staging_cfg_ = CfgMgr::instance().getStagingCfg()->getCfgHostOperations6();
+HostReservationIdsParser6::HostReservationIdsParser6(CfgHostOperationsPtr cfg)
+    : HostReservationIdsParser(cfg) {
 }
 
 bool
index 2c2f260e5d671142ddb81b5640142689fb5c7a79..d4eacb7c32efb26a9a8a704bc3a2712655c06d60 100644 (file)
@@ -153,7 +153,9 @@ class HostReservationIdsParser : public isc::data::SimpleParser {
 public:
 
     /// @brief Constructor.
-    HostReservationIdsParser();
+    ///
+    /// @param cfg Pointer to the object holding configuration.
+    HostReservationIdsParser(CfgHostOperationsPtr cfg);
 
     /// @brief Destructor.
     virtual ~HostReservationIdsParser() { }
@@ -203,7 +205,9 @@ public:
     ///
     /// Initializes staging configuration pointer to the one used for DHCPv4
     /// configuration.
-    HostReservationIdsParser4();
+    ///
+    /// @param cfg Pointer to the object holding configuration.
+    HostReservationIdsParser4(CfgHostOperationsPtr cfg);
 
 protected:
 
@@ -224,7 +228,9 @@ public:
     ///
     /// Initializes staging configuration pointer to the one used for DHCPv6
     /// configuration.
-    HostReservationIdsParser6();
+    ///
+    /// @param cfg Pointer to the object holding configuration.
+    HostReservationIdsParser6(CfgHostOperationsPtr cfg);
 
 protected:
 
index 473c06bf68ab09343b9923524ec524744a9607ee..2974a82192adc7b660c8a50cedf54c0a94cf9e41 100644 (file)
@@ -149,6 +149,20 @@ const SimpleDefaults SimpleParser4::GLOBAL4_DEFAULTS = {
     { "cache-threshold",                  Element::real,    "0.25" },
 };
 
+const SimpleKeywords SimpleParser4::GLOBAL4_LIST_PARAMETERS = {
+    { "host-reservation-identifiers", Element::list },
+    /* not yet supported
+    { "interfaces-config.interfaces", Element::list },
+    */
+};
+
+const SimpleKeywords SimpleParser4::GLOBAL4_LIST_PARAMETER_TYPES = {
+    { "host-reservation-identifiers", Element::string },
+    /* not yet supported
+    { "interfaces-config.interfaces", Element::string },
+    */
+};
+
 /// @brief This table defines all option definition parameters.
 ///
 /// Boolean, integer, real and string types are for scalar parameters,
index e209ab492499fb04fd79473857d706a209bf50ee..614fa319fe29a6071e9b5adda657bd6d891ed792 100644 (file)
@@ -44,6 +44,9 @@ public:
     static const isc::data::SimpleKeywords OPTION4_DEF_PARAMETERS;
     static const isc::data::SimpleDefaults OPTION4_DEF_DEFAULTS;
 
+    static const isc::data::SimpleKeywords GLOBAL4_LIST_PARAMETERS;
+    static const isc::data::SimpleKeywords GLOBAL4_LIST_PARAMETER_TYPES;
+
     static const isc::data::SimpleKeywords OPTION4_PARAMETERS;
     static const isc::data::SimpleDefaults OPTION4_DEFAULTS;
 
index 87f82fcd8b3239cc85667f368f53f64e1f84fd84..1fe8b36d2befffe0a51ce6662bbc7e93db2dc30f 100644 (file)
@@ -144,6 +144,20 @@ const SimpleDefaults SimpleParser6::GLOBAL6_DEFAULTS = {
     { "cache-threshold",                  Element::real,    "0.25" },
 };
 
+const SimpleKeywords SimpleParser6::GLOBAL6_LIST_PARAMETERS = {
+    { "host-reservation-identifiers", Element::list },
+    /* not yet supported
+    { "interfaces-config.interfaces", Element::list },
+    */
+};
+
+const SimpleKeywords SimpleParser6::GLOBAL6_LIST_PARAMETER_TYPES = {
+    { "host-reservation-identifiers", Element::string },
+    /* not yet supported
+    { "interfaces-config.interfaces", Element::string },
+    */
+};
+
 /// @brief This table defines all option definition parameters.
 ///
 /// Boolean, integer, real and string types are for scalar parameters,
index 8eae3bfd1cf138fb984bd266818791babec35658..b7e504bb4cae7654867ca7eea296f47a971e1f8c 100644 (file)
@@ -44,6 +44,9 @@ public:
     static const isc::data::SimpleKeywords OPTION6_DEF_PARAMETERS;
     static const isc::data::SimpleDefaults OPTION6_DEF_DEFAULTS;
 
+    static const isc::data::SimpleKeywords GLOBAL6_LIST_PARAMETERS;
+    static const isc::data::SimpleKeywords GLOBAL6_LIST_PARAMETER_TYPES;
+
     static const isc::data::SimpleKeywords OPTION6_PARAMETERS;
     static const isc::data::SimpleDefaults OPTION6_DEFAULTS;
 
index 43a57aabd0e72f5f9064c843c828f80bf80e73fe..36a814422d852165b1139b092556437fe3561c07 100644 (file)
@@ -11,6 +11,7 @@
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/parsers/base_network_parser.h>
 #include <dhcpsrv/parsers/dhcp_parsers.h>
+#include <dhcpsrv/parsers/host_reservation_parser.h>
 #include <dhcpsrv/parsers/expiration_config_parser.h>
 #include <dhcpsrv/parsers/multi_threading_config_parser.h>
 #include <dhcpsrv/parsers/sanity_checks_parser.h>
@@ -187,8 +188,8 @@ SrvConfig::merge(ConfigBase& other) {
         // Merge globals.
         mergeGlobals(other_srv_config);
 
-        // Merge global maps.
-        mergeGlobalMaps(other_srv_config);
+        // Merge global containers.
+        mergeGlobalContainers(other_srv_config);
 
         // Merge option defs. We need to do this next so we
         // pass these into subsequent merges so option instances
@@ -282,13 +283,27 @@ SrvConfig::mergeGlobals(SrvConfig& other) {
 }
 
 void
-SrvConfig::mergeGlobalMaps(SrvConfig& other) {
+SrvConfig::mergeGlobalContainers(SrvConfig& other) {
     ElementPtr config = Element::createMap();
     for (auto const& other_global : other.getConfiguredGlobals()->valuesMap()) {
         config->set(other_global.first, other_global.second);
     }
     std::string parameter_name;
     try {
+        // Merge list containers.
+        ConstElementPtr host_reservation_identifiers = config->get("host-reservation-identifiers");
+        parameter_name = "host-reservation-identifiers";
+        if (host_reservation_identifiers) {
+            if (CfgMgr::instance().getFamily() == AF_INET) {
+                HostReservationIdsParser4 parser(getCfgHostOperations4());
+                parser.parse(host_reservation_identifiers);
+            } else {
+                HostReservationIdsParser6 parser(getCfgHostOperations6());
+                parser.parse(host_reservation_identifiers);
+            }
+            addConfiguredGlobal("host-reservation-identifiers", host_reservation_identifiers);
+        }
+        // Merge map containers.
         ConstElementPtr compatibility = config->get("compatibility");
         parameter_name = "compatibility";
         if (compatibility) {
index 9263aff5027a5c00c1364d7576e06b65cc430131..b6f4944ac616be5af3cad4f3118f20df5057c469 100644 (file)
@@ -975,7 +975,7 @@ private:
     /// into this configuration.
     void mergeGlobals(SrvConfig& other);
 
-    /// @brief Merges the global maps specified in the given configuration
+    /// @brief Merges the global containers specified in the given configuration
     /// into this configuration.
     ///
     /// Configurable global values may be specified either via JSON
@@ -997,7 +997,7 @@ private:
     ///
     /// @param other An object holding the configuration to be merged
     /// into this configuration.
-    void mergeGlobalMaps(SrvConfig& other);
+    void mergeGlobalContainers(SrvConfig& other);
 
     /// @brief Sequence number identifying the configuration.
     uint32_t sequence_;
index 0815ed82d2a60c11670c82710fc4970d4045efe5..5a793424d2af9ef9f0b0b635d7d006ead7c4d4b7 100644 (file)
@@ -1467,12 +1467,13 @@ public:
     /// @brief Test verifies that invalid configuration causes an error.
     ///
     /// @param config Configuration string.
+    /// @param cfg Pointer to the object holding configuration.
     /// @tparam ParserType @ref HostReservationIdsParser4 or
     /// @ref HostReservationIdsParser6
     template<typename ParserType>
-    void testInvalidConfig(const std::string& config) const {
+    void testInvalidConfig(const std::string& config, CfgHostOperationsPtr cfg) const {
         ElementPtr config_element = Element::fromJSON(config);
-        ParserType parser;
+        ParserType parser(cfg);
         EXPECT_THROW(parser.parse(config_element), DhcpConfigError);
     }
 
@@ -1486,7 +1487,7 @@ TEST_F(HostReservationIdsParserTest, dhcp4Identifiers) {
 
     ElementPtr config_element = Element::fromJSON(config);
 
-    HostReservationIdsParser4 parser;
+    HostReservationIdsParser4 parser(CfgMgr::instance().getStagingCfg()->getCfgHostOperations4());
     ASSERT_NO_THROW(parser.parse(config_element));
 
     ConstCfgHostOperationsPtr cfg = CfgMgr::instance().getStagingCfg()->
@@ -1510,7 +1511,7 @@ TEST_F(HostReservationIdsParserTest, dhcp6Identifiers) {
 
     ElementPtr config_element = Element::fromJSON(config);
 
-    HostReservationIdsParser6 parser;
+    HostReservationIdsParser6 parser(CfgMgr::instance().getStagingCfg()->getCfgHostOperations6());
     ASSERT_NO_THROW(parser.parse(config_element));
 
     ConstCfgHostOperationsPtr cfg = CfgMgr::instance().getStagingCfg()->
@@ -1529,7 +1530,7 @@ TEST_F(HostReservationIdsParserTest, dhcp6Identifiers) {
 TEST_F(HostReservationIdsParserTest, dhcp4InvalidIdentifier) {
     // Create configuration including unsupported identifier.
     std::string config = "[ \"unsupported-id\" ]";
-    testInvalidConfig<HostReservationIdsParser4>(config);
+    testInvalidConfig<HostReservationIdsParser4>(config, CfgMgr::instance().getStagingCfg()->getCfgHostOperations4());
 }
 
 // Test that invalid DHCPv6 identifier causes error.
@@ -1537,7 +1538,7 @@ TEST_F(HostReservationIdsParserTest, dhcp6InvalidIdentifier) {
     // Create configuration including unsupported identifier for DHCPv6.
     // The circuit-id is only supported in DHCPv4.
     std::string config = "[ \"circuit-id\" ]";
-    testInvalidConfig<HostReservationIdsParser6>(config);
+    testInvalidConfig<HostReservationIdsParser6>(config, CfgMgr::instance().getStagingCfg()->getCfgHostOperations6());
 }
 
 // Check that all supported identifiers are used when 'auto' keyword
@@ -1546,7 +1547,7 @@ TEST_F(HostReservationIdsParserTest, dhcp4AutoIdentifiers) {
     std::string config = "[ \"auto\" ]";
     ElementPtr config_element = Element::fromJSON(config);
 
-    HostReservationIdsParser4 parser;
+    HostReservationIdsParser4 parser(CfgMgr::instance().getStagingCfg()->getCfgHostOperations4());
     ASSERT_NO_THROW(parser.parse(config_element));
 
     ConstCfgHostOperationsPtr cfg = CfgMgr::instance().getStagingCfg()->
@@ -1567,7 +1568,7 @@ TEST_F(HostReservationIdsParserTest, dhcp4AutoIdentifiers) {
 // identifier.
 TEST_F(HostReservationIdsParserTest, dhcp4AutoBeforeIdentifier) {
     std::string config = "[ \"auto\", \"duid\" ]";
-    testInvalidConfig<HostReservationIdsParser4>(config);
+    testInvalidConfig<HostReservationIdsParser4>(config, CfgMgr::instance().getStagingCfg()->getCfgHostOperations4());
 }
 
 // This test verifies that use of "auto" together with an explicit
@@ -1575,13 +1576,13 @@ TEST_F(HostReservationIdsParserTest, dhcp4AutoBeforeIdentifier) {
 // identifier.
 TEST_F(HostReservationIdsParserTest, dhcp4AutoAfterIdentifier) {
     std::string config = "[ \"duid\", \"auto\" ]";
-    testInvalidConfig<HostReservationIdsParser4>(config);
+    testInvalidConfig<HostReservationIdsParser4>(config, CfgMgr::instance().getStagingCfg()->getCfgHostOperations4());
 }
 
 // Test that empty list of identifier types is not allowed.
 TEST_F(HostReservationIdsParserTest, dhcp4EmptyList) {
     std::string config = "[ ]";
-    testInvalidConfig<HostReservationIdsParser4>(config);
+    testInvalidConfig<HostReservationIdsParser4>(config, CfgMgr::instance().getStagingCfg()->getCfgHostOperations4());
 }
 
 // Check that all supported identifiers are used when 'auto' keyword
@@ -1590,7 +1591,7 @@ TEST_F(HostReservationIdsParserTest, dhcp6AutoIdentifiers) {
     std::string config = "[ \"auto\" ]";
     ElementPtr config_element = Element::fromJSON(config);
 
-    HostReservationIdsParser6 parser;
+    HostReservationIdsParser6 parser(CfgMgr::instance().getStagingCfg()->getCfgHostOperations6());
     ASSERT_NO_THROW(parser.parse(config_element));
 
     ConstCfgHostOperationsPtr cfg = CfgMgr::instance().getStagingCfg()->
@@ -1609,7 +1610,7 @@ TEST_F(HostReservationIdsParserTest, dhcp6AutoIdentifiers) {
 // identifier.
 TEST_F(HostReservationIdsParserTest, dhcp6AutoBeforeIdentifier) {
     std::string config = "[ \"auto\", \"duid\" ]";
-    testInvalidConfig<HostReservationIdsParser6>(config);
+    testInvalidConfig<HostReservationIdsParser6>(config, CfgMgr::instance().getStagingCfg()->getCfgHostOperations6());
 }
 
 // This test verifies that use of "auto" together with an explicit
@@ -1617,13 +1618,13 @@ TEST_F(HostReservationIdsParserTest, dhcp6AutoBeforeIdentifier) {
 // identifier.
 TEST_F(HostReservationIdsParserTest, dhcp6AutoAfterIdentifier) {
     std::string config = "[ \"duid\", \"auto\" ]";
-    testInvalidConfig<HostReservationIdsParser6>(config);
+    testInvalidConfig<HostReservationIdsParser6>(config, CfgMgr::instance().getStagingCfg()->getCfgHostOperations6());
 }
 
 // Test that empty list of identifier types is not allowed.
 TEST_F(HostReservationIdsParserTest, dhcp6EmptyList) {
     std::string config = "[ ]";
-    testInvalidConfig<HostReservationIdsParser6>(config);
+    testInvalidConfig<HostReservationIdsParser6>(config, CfgMgr::instance().getStagingCfg()->getCfgHostOperations6());
 }
 
 } // end of anonymous namespace
index b73a6d43d4cbe5735e14b66b58bb957083b4fc4a..962c60bf0e5618ed716049e4399c4b86075e8cdc 100644 (file)
@@ -1121,6 +1121,10 @@ TEST_F(SrvConfigTest, mergeGlobals4) {
     cfg_from.addConfiguredGlobal("multi-threading", mt);
     mt->set("enable-multi-threading", Element::create(false));
     mt->set("thread-pool-size", Element::create(256));
+    ElementPtr hr_i = Element::createList();
+    cfg_from.addConfiguredGlobal("host-reservation-identifiers", hr_i);
+    hr_i->add(Element::create("hw-address"));
+    hr_i->add(Element::create("flex-id"));
 
     // Now let's merge.
     ASSERT_NO_THROW(cfg_to.merge(cfg_from));
@@ -1133,13 +1137,13 @@ TEST_F(SrvConfigTest, mergeGlobals4) {
     // echo-client-id should be the preserved "to" member value.
     EXPECT_FALSE(cfg_to.getEchoClientId());
 
-    //  dhcp4o6-port should be the "from" configured value.
+    // dhcp4o6-port should be the "from" configured value.
     EXPECT_EQ(999, cfg_to.getDhcp4o6Port());
 
-    //  server-tag port should be the "from" configured value.
+    // server-tag port should be the "from" configured value.
     EXPECT_EQ("use_this_server", cfg_to.getServerTag().get());
 
-    //  reservations-lookup-first should be the "from" configured value.
+    // reservations-lookup-first should be the "from" configured value.
     EXPECT_TRUE(cfg_to.getReservationsLookupFirst());
 
     // ip-reservations-unique
@@ -1148,6 +1152,12 @@ TEST_F(SrvConfigTest, mergeGlobals4) {
     // multi-threading
     EXPECT_TRUE(cfg_to.getDHCPMultiThreading());
 
+    // host-reservation-identifiers
+    auto const& ex_hr_i = cfg_to.getCfgHostOperations4()->getIdentifierTypes();
+    EXPECT_EQ(ex_hr_i.size(), 2);
+    EXPECT_EQ(ex_hr_i.front(), Host::IDENT_HWADDR);
+    EXPECT_EQ(ex_hr_i.back(), Host::IDENT_FLEX);
+
     // Next we check the explicitly "configured" globals.
     // The list should be all of the "to" + "from", with the
     // latter overwriting the former.
@@ -1157,7 +1167,8 @@ TEST_F(SrvConfigTest, mergeGlobals4) {
         "   \"dhcp4o6-port\": 999, \n"
         "   \"ip-reservations-unique\": false, \n"
         "   \"server-tag\": \"use_this_server\", \n"
-        "   \"reservations-lookup-first\": true,"
+        "   \"reservations-lookup-first\": true, \n"
+        "   \"host-reservation-identifiers\": [ \"hw-address\", \"flex-id\" ], \n"
         "   \"multi-threading\": { \"enable-multi-threading\": false, \n"
         "                          \"packet-queue-size\": 64, \n"
         "                          \"thread-pool-size\": 256 \n"
@@ -1214,6 +1225,10 @@ TEST_F(SrvConfigTest, mergeGlobals6) {
     cfg_from.addConfiguredGlobal("multi-threading", mt);
     mt->set("enable-multi-threading", Element::create(false));
     mt->set("thread-pool-size", Element::create(256));
+    ElementPtr hr_i = Element::createList();
+    cfg_from.addConfiguredGlobal("host-reservation-identifiers", hr_i);
+    hr_i->add(Element::create("hw-address"));
+    hr_i->add(Element::create("flex-id"));
 
     // Now let's merge.
     ASSERT_NO_THROW(cfg_to.merge(cfg_from));
@@ -1223,13 +1238,13 @@ TEST_F(SrvConfigTest, mergeGlobals6) {
     // decline-probation-period should be the "to" configured value.
     EXPECT_EQ(300, cfg_to.getDeclinePeriod());
 
-    //  dhcp4o6-port should be the "from" configured value.
+    // dhcp4o6-port should be the "from" configured value.
     EXPECT_EQ(999, cfg_to.getDhcp4o6Port());
 
-    //  server-tag port should be the "from" configured value.
+    // server-tag port should be the "from" configured value.
     EXPECT_EQ("use_this_server", cfg_to.getServerTag().get());
 
-    //  reservations-lookup-first should be the "from" configured value.
+    // reservations-lookup-first should be the "from" configured value.
     EXPECT_TRUE(cfg_to.getReservationsLookupFirst());
 
     // ip-reservations-unique
@@ -1238,6 +1253,12 @@ TEST_F(SrvConfigTest, mergeGlobals6) {
     // multi-threading
     EXPECT_TRUE(cfg_to.getDHCPMultiThreading());
 
+    // host-reservation-identifiers
+    auto const& ex_hr_i = cfg_to.getCfgHostOperations6()->getIdentifierTypes();
+    EXPECT_EQ(ex_hr_i.size(), 2);
+    EXPECT_EQ(ex_hr_i.front(), Host::IDENT_HWADDR);
+    EXPECT_EQ(ex_hr_i.back(), Host::IDENT_FLEX);
+
     // Next we check the explicitly "configured" globals.
     // The list should be all of the "to" + "from", with the
     // latter overwriting the former.
@@ -1248,6 +1269,7 @@ TEST_F(SrvConfigTest, mergeGlobals6) {
         "   \"ip-reservations-unique\": false, \n"
         "   \"server-tag\": \"use_this_server\", \n"
         "   \"reservations-lookup-first\": true, \n"
+        "   \"host-reservation-identifiers\": [ \"hw-address\", \"flex-id\" ], \n"
         "   \"multi-threading\": { \"enable-multi-threading\": false, \n"
         "                          \"packet-queue-size\": 64, \n"
         "                          \"thread-pool-size\": 256 \n"
index f66105b844238828dc337813c1b101f5588b8e23..8ebb85ee82b4ad93c894bc973af767ec396d8dc3 100644 (file)
@@ -123,7 +123,8 @@ GenericBackendTest::testOptionsEquivalent(const OptionDescriptor& ref_option,
 void
 GenericBackendTest::checkConfiguredGlobal(const SrvConfigPtr& srv_cfg,
                                           const std::string &name,
-                                          ConstElementPtr exp_value) {
+                                          ConstElementPtr exp_value,
+                                          bool is_list) {
     ConstCfgGlobalsPtr globals = srv_cfg->getConfiguredGlobals();
     std::string param_name;
     std::string sub_param_name;
@@ -141,17 +142,24 @@ GenericBackendTest::checkConfiguredGlobal(const SrvConfigPtr& srv_cfg,
                                   << name << " not found";
     }
 
-    ASSERT_EQ(exp_value->getType(), found_global->getType())
-        << "expected global: " << name << " has wrong type";
-
-    ASSERT_EQ(*exp_value, *found_global)
-        << "expected global: " << name << " has wrong value";
+    if (is_list) {
+        ASSERT_EQ(Element::list, found_global->getType())
+            << "expected global: " << name << " has wrong type";
+        ASSERT_EQ(*data::Element::fromJSON(exp_value->stringValue()), *found_global)
+            << "expected global: " << name << " has wrong value";
+    } else {
+        ASSERT_EQ(exp_value->getType(), found_global->getType())
+            << "expected global: " << name << " has wrong type";
+        ASSERT_EQ(*exp_value, *found_global)
+            << "expected global: " << name << " has wrong value";
+    }
 }
 
 void
 GenericBackendTest::checkConfiguredGlobal(const SrvConfigPtr& srv_cfg,
-                                          StampedValuePtr& exp_global) {
-    checkConfiguredGlobal(srv_cfg, exp_global->getName(), exp_global->getElementValue());
+                                          StampedValuePtr& exp_global,
+                                          bool is_list) {
+    checkConfiguredGlobal(srv_cfg, exp_global->getName(), exp_global->getElementValue(), is_list);
 }
 
 void
index 8b18d958c38910a55603a7e2f2eb947ab0044b2f..94bf7cece1503281bd3323094e4f4560eeaedbab 100644 (file)
@@ -277,21 +277,25 @@ public:
     /// @brief Tests that a given global is in the configured globals
     ///
     /// @param srv_cfg server config where the global should be checked.
-    /// @param name name of the global parameter
-    /// @param exp_value expected value of the global parameter as an Element
+    /// @param name name of the global parameter.
+    /// @param exp_value expected value of the global parameter as an Element.
+    /// @param is_list Flag which indicates if the parameter is a global list.
     void checkConfiguredGlobal(const SrvConfigPtr& srv_cfg,
                                const std::string &name,
-                               data::ConstElementPtr exp_value);
+                               data::ConstElementPtr exp_value,
+                               bool is_list = false);
 
     /// @brief Tests that a given global is in the configured globals
     ///
     /// @param srv_cfg server config where the global should be checked.
-    /// @param exp_global StampedValue representing the global value to verify
+    /// @param exp_global StampedValue representing the global value to verify.
+    /// @param is_list Flag which indicates if the parameter is a global list.
     ///
     /// @todo At the point in time StampedVlaue carries type, exp_type should be
-    /// replaced with exp_global->getType()
+    /// replaced with exp_global->getType().
     void checkConfiguredGlobal(const SrvConfigPtr& srv_cfg,
-                               data::StampedValuePtr& exp_global);
+                               data::StampedValuePtr& exp_global,
+                               bool is_list = false);
 
     /// @brief Tests that the new audit entry is added.
     ///
index efb6a1984d26d1be3431bcd6e6910fea5a5b5951..2901dacd04a65b7e1d0507f4731ab42f9b94e7ff 100644 (file)
@@ -21,7 +21,7 @@
     "hook": "cb_cmds",
     "name": "remote-global-parameter4-get-all",
     "resp-comment": [
-        "The returned response contains a list of maps. Each map contains a global parameter name:value pair. The value may be a JSON string, integer, real, boolean or a map containing only one of these types. The metadata is appended to each parameter and provides database-specific information associated with the returned objects. If the server tag \"all\" is included in the command, the response contains the global parameters shared among all servers. It excludes server-specific global parameters. If an explicit server tag is included in the command, the response contains all global parameters directly associated with the given server, and the global parameters associated with all servers when server-specific values are not present."
+        "The returned response contains a list of maps. Each map contains a global parameter name:value pair. The value may be a JSON string, integer, real, boolean, list of these scalar types or a map containing only one of these scalar types. The metadata is appended to each parameter and provides database-specific information associated with the returned objects. If the server tag \"all\" is included in the command, the response contains the global parameters shared among all servers. It excludes server-specific global parameters. If an explicit server tag is included in the command, the response contains all global parameters directly associated with the given server, and the global parameters associated with all servers when server-specific values are not present."
     ],
     "resp-syntax": [
         "{",
index 9fd98bbf20ff5919f212767e618da2164582706b..051a703c4ac666f8fc2b25e753eafc773af0435c 100644 (file)
@@ -22,7 +22,7 @@
     "hook": "cb_cmds",
     "name": "remote-global-parameter4-get",
     "resp-comment": [
-        "The returned response contains a map with a global parameter name:value pair. The value may be a JSON string, integer, real, boolean or a map containing only one of these types. The metadata is included and provides database-specific information associated with the returned object. If the \"all\" server tag is specified, the command attempts to fetch the global parameter value associated with all servers. If the explicit server tag is specified, the command fetches the value associated with the given server. If the server-specific value does not exist, the ``remote-global-parameter4-get`` command fetches the value associated with all servers."
+        "The returned response contains a map with a global parameter name:value pair. The value may be a JSON string, integer, real, boolean, list of these scalar types or a map containing only one of these scalar types. The metadata is included and provides database-specific information associated with the returned object. If the \"all\" server tag is specified, the command attempts to fetch the global parameter value associated with all servers. If the explicit server tag is specified, the command fetches the value associated with the given server. If the server-specific value does not exist, the ``remote-global-parameter4-get`` command fetches the value associated with all servers."
     ],
     "resp-syntax": [
         "{",
index d5c44139f359fd14c48827abfad23e0a1a3561d3..7c178050cc73366f39d322655360bb39592ae299 100644 (file)
@@ -5,7 +5,7 @@
         "This command creates or updates one or more global parameters in the configuration database."
     ],
     "cmd-comment": [
-        "This command carries multiple global parameters with their values (including maps with scalar parameters). Care should be taken when specifying more than one parameter; in some cases, only a subset of the parameters may be successfully stored in the database and other parameters may fail to be stored. In such cases the ``remote-global-parameter4-get-all`` command may be useful to verify the contents of the database after the update. The ``server-tags`` list is mandatory and must contain exactly one server tag. Specifying an empty list, a value of ``null``, or multiple server tags will result in an error. The server tag \"all\" is allowed; it associates the specified parameters with all servers."
+        "This command carries multiple global parameters with their values (including lists or maps with scalar parameters). Care should be taken when specifying more than one parameter; in some cases, only a subset of the parameters may be successfully stored in the database and other parameters may fail to be stored. In such cases the ``remote-global-parameter4-get-all`` command may be useful to verify the contents of the database after the update. The ``server-tags`` list is mandatory and must contain exactly one server tag. Specifying an empty list, a value of ``null``, or multiple server tags will result in an error. The server tag \"all\" is allowed; it associates the specified parameters with all servers."
     ],
     "cmd-syntax": [
         "{",
index 5ddfa55203e9a615bc919afa8b798bea0ab42fd3..f025974383c1c649445cd59fd85989e2c0ead470 100644 (file)
@@ -21,7 +21,7 @@
     "hook": "cb_cmds",
     "name": "remote-global-parameter6-get-all",
     "resp-comment": [
-        "The returned response contains a list of maps. Each map contains a global parameter name:value pair. The value may be a JSON string, integer, real, boolean or a map containing only one of these types. The metadata is appended to each parameter and provides database-specific information associated with the returned objects. If the server tag \"all\" is included in the command, the response contains the global parameters shared among all servers. It excludes server-specific global parameters. If an explicit server tag is included in the command, the response contains all global parameters directly associated with the given server, and the global parameters associated with all servers when server-specific values are not present."
+        "The returned response contains a list of maps. Each map contains a global parameter name:value pair. The value may be a JSON string, integer, real, boolean, list of these scalar types or a map containing only one of these scalar types. The metadata is appended to each parameter and provides database-specific information associated with the returned objects. If the server tag \"all\" is included in the command, the response contains the global parameters shared among all servers. It excludes server-specific global parameters. If an explicit server tag is included in the command, the response contains all global parameters directly associated with the given server, and the global parameters associated with all servers when server-specific values are not present."
     ],
     "resp-syntax": [
         "{",
index c95dceef141375cfb65ce5b627b84c89912b7642..ba2768cff294645aef5432c8cb4c72b8243201ed 100644 (file)
@@ -22,7 +22,7 @@
     "hook": "cb_cmds",
     "name": "remote-global-parameter6-get",
     "resp-comment": [
-        "The returned response contains a map with a global parameter name:value pair. The value may be a JSON string, integer, real, boolean or a map containing only one of these types. The metadata is included and provides database-specific information associated with the returned object. If the \"all\" server tag is specified, the command attempts to fetch the global parameter value associated with all servers. If the explicit server tag is specified, the command fetches the value associated with the given server. If the server-specific value does not exist, the ``remote-global-parameter6-get`` fetches the value associated with all servers."
+        "The returned response contains a map with a global parameter name:value pair. The value may be a JSON string, integer, real, boolean, list of these scalar types or a map containing only one of these scalar types. The metadata is included and provides database-specific information associated with the returned object. If the \"all\" server tag is specified, the command attempts to fetch the global parameter value associated with all servers. If the explicit server tag is specified, the command fetches the value associated with the given server. If the server-specific value does not exist, the ``remote-global-parameter6-get`` fetches the value associated with all servers."
     ],
     "resp-syntax": [
         "{",
index fb547c4f2c9dfa1ca0be334ba289b2cf5e02322d..7eb78bc214dccc1995a83322f9f4e12887983fcc 100644 (file)
@@ -5,7 +5,7 @@
         "This command creates or updates one or more global parameters in the configuration database."
     ],
     "cmd-comment": [
-        "This command carries multiple global parameters with their values (including maps with scalar parameters). Care should be taken when specifying more than one parameter; in some cases, only a subset of the parameters may be successfully stored in the database and other parameters may fail to be stored. In such cases the ``remote-global-parameter6-get-all`` command may be useful to verify the contents of the database after the update. The ``server-tags`` list is mandatory and must contain exactly one server tag. Specifying an empty list, a value of ``null``, or multiple server tags will result in an error. The server tag \"all\" is allowed; it associates the specified parameters with all servers."
+        "This command carries multiple global parameters with their values (including lists or maps with scalar parameters). Care should be taken when specifying more than one parameter; in some cases, only a subset of the parameters may be successfully stored in the database and other parameters may fail to be stored. In such cases the ``remote-global-parameter6-get-all`` command may be useful to verify the contents of the database after the update. The ``server-tags`` list is mandatory and must contain exactly one server tag. Specifying an empty list, a value of ``null``, or multiple server tags will result in an error. The server tag \"all\" is allowed; it associates the specified parameters with all servers."
     ],
     "cmd-syntax": [
         "{",