From 00d2434ae3a47c82a2921767a5245f93807df0c1 Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Fri, 18 Jul 2025 00:03:07 +0300 Subject: [PATCH] [#3944] add support for CB global scalar lists --- doc/sphinx/arm/dhcp4-srv.rst | 10 ++++ doc/sphinx/arm/dhcp6-srv.rst | 10 ++++ src/bin/dhcp4/json_config_parser.cc | 2 +- .../dhcp4/tests/config_backend_unittest.cc | 8 +++ src/bin/dhcp6/json_config_parser.cc | 2 +- .../dhcp6/tests/config_backend_unittest.cc | 8 +++ src/bin/dhcp6/tests/parser_unittest.cc | 55 +++++++++---------- src/lib/dhcpsrv/cb_ctl_dhcp.h | 23 +++++--- src/lib/dhcpsrv/cb_ctl_dhcp4.cc | 4 +- src/lib/dhcpsrv/cb_ctl_dhcp6.cc | 4 +- src/lib/dhcpsrv/cfg_globals.cc | 1 + src/lib/dhcpsrv/cfg_globals.h | 1 + .../parsers/host_reservation_parser.cc | 14 ++--- .../dhcpsrv/parsers/host_reservation_parser.h | 12 +++- src/lib/dhcpsrv/parsers/simple_parser4.cc | 14 +++++ src/lib/dhcpsrv/parsers/simple_parser4.h | 3 + src/lib/dhcpsrv/parsers/simple_parser6.cc | 14 +++++ src/lib/dhcpsrv/parsers/simple_parser6.h | 3 + src/lib/dhcpsrv/srv_config.cc | 21 ++++++- src/lib/dhcpsrv/srv_config.h | 4 +- .../tests/host_reservation_parser_unittest.cc | 29 +++++----- src/lib/dhcpsrv/tests/srv_config_unittest.cc | 36 +++++++++--- .../testutils/generic_backend_unittest.cc | 24 +++++--- .../testutils/generic_backend_unittest.h | 16 ++++-- .../api/remote-global-parameter4-get-all.json | 2 +- .../api/remote-global-parameter4-get.json | 2 +- .../api/remote-global-parameter4-set.json | 2 +- .../api/remote-global-parameter6-get-all.json | 2 +- .../api/remote-global-parameter6-get.json | 2 +- .../api/remote-global-parameter6-set.json | 2 +- 30 files changed, 232 insertions(+), 98 deletions(-) diff --git a/doc/sphinx/arm/dhcp4-srv.rst b/doc/sphinx/arm/dhcp4-srv.rst index f24e063e82..9fb21f8f64 100644 --- a/doc/sphinx/arm/dhcp4-srv.rst +++ b/doc/sphinx/arm/dhcp4-srv.rst @@ -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 diff --git a/doc/sphinx/arm/dhcp6-srv.rst b/doc/sphinx/arm/dhcp6-srv.rst index d8e87e538d..7275043484 100644 --- a/doc/sphinx/arm/dhcp6-srv.rst +++ b/doc/sphinx/arm/dhcp6-srv.rst @@ -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 diff --git a/src/bin/dhcp4/json_config_parser.cc b/src/bin/dhcp4/json_config_parser.cc index ebab5530e5..8bee0e3585 100644 --- a/src/bin/dhcp4/json_config_parser.cc +++ b/src/bin/dhcp4/json_config_parser.cc @@ -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); } diff --git a/src/bin/dhcp4/tests/config_backend_unittest.cc b/src/bin/dhcp4/tests/config_backend_unittest.cc index 4b5f08fd55..dc8f7df2f7 100644 --- a/src/bin/dhcp4/tests/config_backend_unittest.cc +++ b/src/bin/dhcp4/tests/config_backend_unittest.cc @@ -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 diff --git a/src/bin/dhcp6/json_config_parser.cc b/src/bin/dhcp6/json_config_parser.cc index 1f9aec6760..4391155133 100644 --- a/src/bin/dhcp6/json_config_parser.cc +++ b/src/bin/dhcp6/json_config_parser.cc @@ -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); } diff --git a/src/bin/dhcp6/tests/config_backend_unittest.cc b/src/bin/dhcp6/tests/config_backend_unittest.cc index 690c6745a6..afc62db890 100644 --- a/src/bin/dhcp6/tests/config_backend_unittest.cc +++ b/src/bin/dhcp6/tests/config_backend_unittest.cc @@ -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 diff --git a/src/bin/dhcp6/tests/parser_unittest.cc b/src/bin/dhcp6/tests/parser_unittest.cc index a82ca5c149..1e7389c330 100644 --- a/src/bin/dhcp6/tests/parser_unittest.cc +++ b/src/bin/dhcp6/tests/parser_unittest.cc @@ -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 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 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(); 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(value); + if (global_lists.count(cb_global->getName())) { + mutable_value = data::Element::fromJSON(value->stringValue()); + } + data::ConstElementPtr global_value = boost::const_pointer_cast(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); } } } diff --git a/src/lib/dhcpsrv/cb_ctl_dhcp4.cc b/src/lib/dhcpsrv/cb_ctl_dhcp4.cc index a24502cc43..dadc5c3f5f 100644 --- a/src/lib/dhcpsrv/cb_ctl_dhcp4.cc +++ b/src/lib/dhcpsrv/cb_ctl_dhcp4.cc @@ -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; } } diff --git a/src/lib/dhcpsrv/cb_ctl_dhcp6.cc b/src/lib/dhcpsrv/cb_ctl_dhcp6.cc index 552c8553f2..6aa9b02631 100644 --- a/src/lib/dhcpsrv/cb_ctl_dhcp6.cc +++ b/src/lib/dhcpsrv/cb_ctl_dhcp6.cc @@ -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; } } diff --git a/src/lib/dhcpsrv/cfg_globals.cc b/src/lib/dhcpsrv/cfg_globals.cc index f9c334eff9..076d802174 100644 --- a/src/lib/dhcpsrv/cfg_globals.cc +++ b/src/lib/dhcpsrv/cfg_globals.cc @@ -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 }, diff --git a/src/lib/dhcpsrv/cfg_globals.h b/src/lib/dhcpsrv/cfg_globals.h index f0bccedef3..33c9388053 100644 --- a/src/lib/dhcpsrv/cfg_globals.h +++ b/src/lib/dhcpsrv/cfg_globals.h @@ -83,6 +83,7 @@ public: DDNS_TTL, DDNS_TTL_MIN, DDNS_TTL_MAX, + HOST_RESERVATION_IDENTIFIERS, // DHCPv4 specific parameters. ECHO_CLIENT_ID, diff --git a/src/lib/dhcpsrv/parsers/host_reservation_parser.cc b/src/lib/dhcpsrv/parsers/host_reservation_parser.cc index c7bb9ff6c2..0196aab993 100644 --- a/src/lib/dhcpsrv/parsers/host_reservation_parser.cc +++ b/src/lib/dhcpsrv/parsers/host_reservation_parser.cc @@ -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 diff --git a/src/lib/dhcpsrv/parsers/host_reservation_parser.h b/src/lib/dhcpsrv/parsers/host_reservation_parser.h index 2c2f260e5d..d4eacb7c32 100644 --- a/src/lib/dhcpsrv/parsers/host_reservation_parser.h +++ b/src/lib/dhcpsrv/parsers/host_reservation_parser.h @@ -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: diff --git a/src/lib/dhcpsrv/parsers/simple_parser4.cc b/src/lib/dhcpsrv/parsers/simple_parser4.cc index 473c06bf68..2974a82192 100644 --- a/src/lib/dhcpsrv/parsers/simple_parser4.cc +++ b/src/lib/dhcpsrv/parsers/simple_parser4.cc @@ -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, diff --git a/src/lib/dhcpsrv/parsers/simple_parser4.h b/src/lib/dhcpsrv/parsers/simple_parser4.h index e209ab4924..614fa319fe 100644 --- a/src/lib/dhcpsrv/parsers/simple_parser4.h +++ b/src/lib/dhcpsrv/parsers/simple_parser4.h @@ -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; diff --git a/src/lib/dhcpsrv/parsers/simple_parser6.cc b/src/lib/dhcpsrv/parsers/simple_parser6.cc index 87f82fcd8b..1fe8b36d2b 100644 --- a/src/lib/dhcpsrv/parsers/simple_parser6.cc +++ b/src/lib/dhcpsrv/parsers/simple_parser6.cc @@ -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, diff --git a/src/lib/dhcpsrv/parsers/simple_parser6.h b/src/lib/dhcpsrv/parsers/simple_parser6.h index 8eae3bfd1c..b7e504bb4c 100644 --- a/src/lib/dhcpsrv/parsers/simple_parser6.h +++ b/src/lib/dhcpsrv/parsers/simple_parser6.h @@ -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; diff --git a/src/lib/dhcpsrv/srv_config.cc b/src/lib/dhcpsrv/srv_config.cc index 43a57aabd0..36a814422d 100644 --- a/src/lib/dhcpsrv/srv_config.cc +++ b/src/lib/dhcpsrv/srv_config.cc @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -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) { diff --git a/src/lib/dhcpsrv/srv_config.h b/src/lib/dhcpsrv/srv_config.h index 9263aff502..b6f4944ac6 100644 --- a/src/lib/dhcpsrv/srv_config.h +++ b/src/lib/dhcpsrv/srv_config.h @@ -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_; diff --git a/src/lib/dhcpsrv/tests/host_reservation_parser_unittest.cc b/src/lib/dhcpsrv/tests/host_reservation_parser_unittest.cc index 0815ed82d2..5a793424d2 100644 --- a/src/lib/dhcpsrv/tests/host_reservation_parser_unittest.cc +++ b/src/lib/dhcpsrv/tests/host_reservation_parser_unittest.cc @@ -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 - 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(config); + testInvalidConfig(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(config); + testInvalidConfig(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(config); + testInvalidConfig(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(config); + testInvalidConfig(config, CfgMgr::instance().getStagingCfg()->getCfgHostOperations4()); } // Test that empty list of identifier types is not allowed. TEST_F(HostReservationIdsParserTest, dhcp4EmptyList) { std::string config = "[ ]"; - testInvalidConfig(config); + testInvalidConfig(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(config); + testInvalidConfig(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(config); + testInvalidConfig(config, CfgMgr::instance().getStagingCfg()->getCfgHostOperations6()); } // Test that empty list of identifier types is not allowed. TEST_F(HostReservationIdsParserTest, dhcp6EmptyList) { std::string config = "[ ]"; - testInvalidConfig(config); + testInvalidConfig(config, CfgMgr::instance().getStagingCfg()->getCfgHostOperations6()); } } // end of anonymous namespace diff --git a/src/lib/dhcpsrv/tests/srv_config_unittest.cc b/src/lib/dhcpsrv/tests/srv_config_unittest.cc index b73a6d43d4..962c60bf0e 100644 --- a/src/lib/dhcpsrv/tests/srv_config_unittest.cc +++ b/src/lib/dhcpsrv/tests/srv_config_unittest.cc @@ -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" diff --git a/src/lib/dhcpsrv/testutils/generic_backend_unittest.cc b/src/lib/dhcpsrv/testutils/generic_backend_unittest.cc index f66105b844..8ebb85ee82 100644 --- a/src/lib/dhcpsrv/testutils/generic_backend_unittest.cc +++ b/src/lib/dhcpsrv/testutils/generic_backend_unittest.cc @@ -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 diff --git a/src/lib/dhcpsrv/testutils/generic_backend_unittest.h b/src/lib/dhcpsrv/testutils/generic_backend_unittest.h index 8b18d958c3..94bf7cece1 100644 --- a/src/lib/dhcpsrv/testutils/generic_backend_unittest.h +++ b/src/lib/dhcpsrv/testutils/generic_backend_unittest.h @@ -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. /// diff --git a/src/share/api/remote-global-parameter4-get-all.json b/src/share/api/remote-global-parameter4-get-all.json index efb6a1984d..2901dacd04 100644 --- a/src/share/api/remote-global-parameter4-get-all.json +++ b/src/share/api/remote-global-parameter4-get-all.json @@ -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": [ "{", diff --git a/src/share/api/remote-global-parameter4-get.json b/src/share/api/remote-global-parameter4-get.json index 9fd98bbf20..051a703c4a 100644 --- a/src/share/api/remote-global-parameter4-get.json +++ b/src/share/api/remote-global-parameter4-get.json @@ -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": [ "{", diff --git a/src/share/api/remote-global-parameter4-set.json b/src/share/api/remote-global-parameter4-set.json index d5c44139f3..7c178050cc 100644 --- a/src/share/api/remote-global-parameter4-set.json +++ b/src/share/api/remote-global-parameter4-set.json @@ -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": [ "{", diff --git a/src/share/api/remote-global-parameter6-get-all.json b/src/share/api/remote-global-parameter6-get-all.json index 5ddfa55203..f025974383 100644 --- a/src/share/api/remote-global-parameter6-get-all.json +++ b/src/share/api/remote-global-parameter6-get-all.json @@ -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": [ "{", diff --git a/src/share/api/remote-global-parameter6-get.json b/src/share/api/remote-global-parameter6-get.json index c95dceef14..ba2768cff2 100644 --- a/src/share/api/remote-global-parameter6-get.json +++ b/src/share/api/remote-global-parameter6-get.json @@ -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": [ "{", diff --git a/src/share/api/remote-global-parameter6-set.json b/src/share/api/remote-global-parameter6-set.json index fb547c4f2c..7eb78bc214 100644 --- a/src/share/api/remote-global-parameter6-set.json +++ b/src/share/api/remote-global-parameter6-set.json @@ -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": [ "{", -- 2.47.2