From: Francis Dupont Date: Wed, 24 Oct 2018 14:01:45 +0000 (+0200) Subject: [65-libyang-config-translator] Added config translator code and tests X-Git-Tag: 65-libyang-finish-doc_base~8 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6b43936ea5791c27819d2368693a9381f59d595e;p=thirdparty%2Fkea.git [65-libyang-config-translator] Added config translator code and tests --- diff --git a/src/lib/yang/Makefile.am b/src/lib/yang/Makefile.am index 27a8fe0adf..7086577fa7 100644 --- a/src/lib/yang/Makefile.am +++ b/src/lib/yang/Makefile.am @@ -28,6 +28,7 @@ libkea_yang_la_SOURCES += translator_host.cc translator_host.h libkea_yang_la_SOURCES += translator_subnet.cc translator_subnet.h libkea_yang_la_SOURCES += translator_shared_network.cc libkea_yang_la_SOURCES += translator_shared_network.h +libkea_yang_la_SOURCES += translator_config.cc translator_config.h libkea_yang_la_SOURCES += yang_models.h libkea_yang_la_LIBADD = $(top_builddir)/src/lib/asiolink/libkea-asiolink.la @@ -52,6 +53,7 @@ libkea_yang_include_HEADERS = \ sysrepo_error.h \ translator.h \ translator_class.h \ + translator_config.h \ translator_control_socket.h \ translator_database.h \ translator_host.h \ diff --git a/src/lib/yang/tests/Makefile.am b/src/lib/yang/tests/Makefile.am index 4a8f091ece..047478b2ce 100644 --- a/src/lib/yang/tests/Makefile.am +++ b/src/lib/yang/tests/Makefile.am @@ -17,7 +17,7 @@ EXTRA_DIST = keatest-module.yang TESTS = if HAVE_GTEST TESTS += run_unittests -run_unittests_SOURCES = yang_configs.h +run_unittests_SOURCES = json_configs.h yang_configs.h run_unittests_SOURCES += adaptor_unittests.cc run_unittests_SOURCES += adaptor_option_unittests.cc run_unittests_SOURCES += adaptor_pool_unittests.cc @@ -38,6 +38,7 @@ run_unittests_SOURCES += translator_host_unittests.cc run_unittests_SOURCES += translator_subnet_unittests.cc run_unittests_SOURCES += translator_shared_network_unittests.cc run_unittests_SOURCES += translator_utils_unittests.cc +run_unittests_SOURCES += config_unittests.cc run_unittests_SOURCES += run_unittests.cc run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) diff --git a/src/lib/yang/tests/adaptor_config_unittests.cc b/src/lib/yang/tests/adaptor_config_unittests.cc index 6f1eb9017e..a4d873f377 100644 --- a/src/lib/yang/tests/adaptor_config_unittests.cc +++ b/src/lib/yang/tests/adaptor_config_unittests.cc @@ -37,7 +37,7 @@ void testFile(const std::string& fname, bool v6, ElementPtr& result) { string decommented = decommentJSONfile(fname); - cout << "Parsing file " << fname << " (" << decommented << ")" << endl; + //cout << "Parsing file " << fname << " (" << decommented << ")" << endl; EXPECT_NO_THROW(json = Element::fromJSONFile(decommented, true)); reference_json = moveComments(json); diff --git a/src/lib/yang/tests/config_unittests.cc b/src/lib/yang/tests/config_unittests.cc new file mode 100644 index 0000000000..ed5a18236a --- /dev/null +++ b/src/lib/yang/tests/config_unittests.cc @@ -0,0 +1,408 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace isc; +using namespace isc::data; +using namespace isc::yang; +using namespace isc::yang::test; + +namespace { + +/// @brief Return the difference between two strings +/// +/// Use the gtest >= 1.8.0 tool which builds the difference between +/// two vectors of lines. +/// +/// @param left left string +/// @param right right string +/// @return the unified diff between left and right +#ifdef HAVE_CREATE_UNIFIED_DIFF +std::string generateDiff(std::string left, std::string right) { + std::vector left_lines; + boost::split(left_lines, left, boost::is_any_of("\n")); + std::vector right_lines; + boost::split(right_lines, right, boost::is_any_of("\n")); + using namespace testing::internal; + return (edit_distance::CreateUnifiedDiff(left_lines, right_lines)); +} +#else +std::string generateDiff(std::string, std::string) { + return (""); +} +#endif + + +/// @brief Test Fixture class for Yang <-> JSON configs. +class ConfigTest : public ::testing::Test { +public: + + /// @brief Constructor. + ConfigTest() { + createSession(); + } + + /// @brief Virtual destructor. + virtual ~ConfigTest() { + session_.reset(); + connection_.reset(); + model_.clear(); + } + + /// @brief Set model. + /// + /// @param model The model name. + void setModel(const string model) { + model_ = model; + } + + /// @brief Create session. + void createSession() { + connection_.reset(new Connection("configs unittests")); + session_.reset(new Session(connection_, SR_DS_CANDIDATE)); + } + + /// @brief Reset session. + void resetSession() { + session_.reset(new Session(connection_, SR_DS_CANDIDATE)); + } + + /// @brief Load Yang. + /// + /// @param tree The Yang tree to load. + void load(const YRTree& tree) { + YangRepr repr(model_); + repr.set(tree, session_); + } + + /// @brief Load JSON. + /// + /// @param json The JSON tree to load. + void load(ConstElementPtr json) { + TranslatorConfig tc(session_, model_); + tc.setConfig(json); + } + + /// @brief Load JSON text. + /// + /// @param config The JSON tree to load in textual format. + void load(const string& config) { + ConstElementPtr json; + ASSERT_NO_THROW(json = Element::fromJSON(config)); + load(json); + } + + /// @brief Load JSON file. + /// + /// @param filename The name of the JSON file to load, + ConstElementPtr loadFile(const string& filename) { + string decommented = isc::test::decommentJSONfile(filename); + ConstElementPtr json = Element::fromJSONFile(decommented, true); + ::remove(decommented.c_str()); + load(json); + return (json); + } + + /// @brief Get Yang. + YRTree getYang() { + YangRepr repr(model_); + return (repr.get(session_)); + } + + /// @brief Get JSON. + ConstElementPtr getJSON() { + TranslatorConfig tc(session_, model_); + return (tc.getConfig()); + } + + /// @brief Get JSON text. + string getText() { + return (isc::data::prettyPrint(getJSON())); + } + + /// @brief Verify Yang. + /// + /// @param expected The expected Yang tree. + bool verify(const YRTree& expected) { + YangRepr repr(model_); + return (repr.verify(expected, session_, cerr)); + } + + /// @brief Verify JSON. + /// + /// @param expected The expected JSON tree. + bool verify(ConstElementPtr expected) { + TranslatorConfig tc(session_, model_); + ConstElementPtr content = tc.getConfig(); + if (isEquivalent(expected, content)) { + return (true); + } + string wanted = prettyPrint(expected); + string got = prettyPrint(content); + cerr << "Expected:\n" << wanted << "\n" + << "Actual:\n" << got +#ifdef HAVE_CREATE_UNIFIED_DIFF + << "\nDiff:\n" << generateDiff(wanted, got) +#endif + << "\n"; + return (false); + } + + /// @brief Verify JSON. + /// + /// @param expected The expected JSON tree in textual format. + bool verify(const string& config) { + ConstElementPtr expected; + expected= Element::fromJSON(config); + return (verify(expected)); + } + + /// @brief Validate. + /// + /// @note A tree must be loaded first. + /// + bool validate() { + YangRepr repr(model_); + return (repr.validate(session_, cerr)); + } + + /// @brief The model. + string model_; + + /// @brief The sysrepo connection. + S_Connection connection_; + + /// @brief The sysrepo session. + S_Session session_; +}; + +// Check empty config with ietf-dhcpv6-server model. +TEST_F(ConfigTest, emptyIetf6) { + // First set the model. + setModel(IETF_DHCPV6_SERVER); + + YRTree tree; + ASSERT_NO_THROW(load(tree)); + EXPECT_TRUE(verify(tree)); + + ConstElementPtr json = Element::fromJSON(emptyJson6); + EXPECT_TRUE(verify(json)); + ASSERT_NO_THROW(load(json)); + EXPECT_TRUE(verify(emptyJson6)); + EXPECT_TRUE(verify(tree)); +} + +// Check empty config with kea-dhcp4-server:config model. +TEST_F(ConfigTest, emptyKeaDhcp4) { + // First set the model. + setModel(KEA_DHCP4_SERVER); + + YRTree tree; + ASSERT_NO_THROW(load(tree)); + EXPECT_TRUE(verify(tree)); + + ConstElementPtr json = Element::fromJSON(emptyJson4); + EXPECT_TRUE(verify(json)); + ASSERT_NO_THROW(load(json)); + EXPECT_TRUE(verify(emptyJson4)); + EXPECT_TRUE(verify(tree)); +} + +// Check empty config with kea-dhcp6-server:config model. +TEST_F(ConfigTest, emptyKeaDhcp6) { + // First set the model. + setModel(KEA_DHCP6_SERVER); + + YRTree tree; + ASSERT_NO_THROW(load(tree)); + EXPECT_TRUE(verify(tree)); + + ConstElementPtr json = Element::fromJSON(emptyJson6); + EXPECT_TRUE(verify(json)); + ASSERT_NO_THROW(load(json)); + EXPECT_TRUE(verify(emptyJson6)); + EXPECT_TRUE(verify(tree)); +} + +// Check subnet with two pools with ietf-dhcpv6-server model. +TEST_F(ConfigTest, subnetTwoPoolsIetf6) { + // First set the model. + setModel(subnetTwoPoolsModelIetf6); + + ASSERT_NO_THROW(load(subnetTwoPoolsTreeIetf6)); + EXPECT_TRUE(verify(subnetTwoPoolsJson6)); + + resetSession(); + + ASSERT_NO_THROW(load(subnetTwoPoolsJson6)); + EXPECT_TRUE(verify(subnetTwoPoolsTreeIetf6)); + + cout << "validation is expected to fail: please ignore messages" << endl; + EXPECT_FALSE(validate()); +} + +// Check subnet with a pool and option data lists with +// kea-dhcp4-server:config model. +TEST_F(ConfigTest, subnetOptionsKeaDhcp4) { + // First set the model. + setModel(subnetOptionsModelKeaDhcp4); + + ASSERT_NO_THROW(load(subnetOptionsTreeKeaDhcp4)); + EXPECT_TRUE(verify(subnetOptionsJson4)); + + resetSession(); + + ASSERT_NO_THROW(load(subnetOptionsJson4)); + EXPECT_TRUE(verify(subnetOptionsTreeKeaDhcp4)); + + EXPECT_TRUE(validate()); +} + +// Check subnet with a pool and option data lists with +// kea-dhcp6-server:config model. +TEST_F(ConfigTest, subnetOptionsKeaDhcp6) { + // First set the model. + setModel(subnetOptionsModelKeaDhcp6); + + ASSERT_NO_THROW(load(subnetOptionsTreeKeaDhcp6)); + EXPECT_TRUE(verify(subnetOptionsJson6)); + + resetSession(); + + ASSERT_NO_THROW(load(subnetOptionsJson6)); + EXPECT_TRUE(verify(subnetOptionsTreeKeaDhcp6)); + + EXPECT_TRUE(validate()); +} + +// Check with timers. +TEST_F(ConfigTest, subnetTimersIetf6) { + // First set the model. + setModel(subnetTimersModel); + + ASSERT_NO_THROW(load(subnetTimersIetf6)); + EXPECT_TRUE(verify(subnetTimersJson6)); + + resetSession(); + + ASSERT_NO_THROW(load(subnetTimersJson6)); + EXPECT_TRUE(verify(subnetTimersIetf6)); +} + +// Check a ietf-dhcpv6-server configuration which validates. +TEST_F(ConfigTest, validateIetf6) { + // First set the model. + setModel(validModelIetf6); + + ASSERT_NO_THROW(load(validTreeIetf6)); + EXPECT_TRUE(verify(validTreeIetf6)); + + EXPECT_TRUE(validate()); +} + +// Check Kea4 example files. +TEST_F(ConfigTest, examples4) { + // First set the model. + setModel(KEA_DHCP4_SERVER); + + vector examples = { + "advanced.json", + "all-keys.json", + "backends.json", + "cassandra.json", + "classify.json", + "classify2.json", + "comments.json", + "dhcpv4-over-dhcpv6.json", + "hooks.json", + "leases-expiration.json", + "multiple-options.json", + "mysql-reservations.json", + "pgsql-reservations.json", + "reservations.json", + "several-subnets.json", + "shared-network.json", + "single-subnet.json", + "with-ddns.json" + }; + for (string file : examples) { + resetSession(); + string path = string(CFG_EXAMPLES) + "/kea4/" + file; + ConstElementPtr json; + ASSERT_NO_THROW(json = loadFile(path)); + json = isc::test::moveComments(json); + EXPECT_TRUE(verify(json)); + EXPECT_TRUE(validate()); + } +} + +// Check Kea6 example files. +TEST_F(ConfigTest, examples6) { + // First set the model. + setModel(KEA_DHCP6_SERVER); + + vector examples = { + "advanced.json", + "all-keys.json", + "backends.json", + "cassandra.json", + "classify.json", + "classify2.json", + "comments.json", + "dhcpv4-over-dhcpv6.json", + "duid.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", + "with-ddns.json" + }; + for (string file : examples) { + resetSession(); + string path = string(CFG_EXAMPLES) + "/kea6/" + file; + ConstElementPtr json; + ASSERT_NO_THROW(json = loadFile(path)); + json = isc::test::moveComments(json); + EXPECT_TRUE(verify(json)); + EXPECT_TRUE(validate()); + } +} + +// Check the example in the design document. +TEST_F(ConfigTest, designExample) { + // First set the model. + setModel(designExampleModel); + + ASSERT_NO_THROW(load(designExampleTree)); + EXPECT_TRUE(verify(designExampleJson)); + + resetSession(); + + ASSERT_NO_THROW(load(designExampleJson)); + EXPECT_TRUE(verify(designExampleTree)); +} + +}; // end of anonymous namespace diff --git a/src/lib/yang/tests/json_configs.h b/src/lib/yang/tests/json_configs.h new file mode 100644 index 0000000000..032aef1cb7 --- /dev/null +++ b/src/lib/yang/tests/json_configs.h @@ -0,0 +1,175 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef ISC_JSON_CONFIGS_H +#define ISC_JSON_CONFIGS_H + +#include + +namespace isc { +namespace yang { +namespace test { + +/// @brief Empty DHCPv4 config. +const std::string emptyJson4 = + "{\n" + " \"Dhcp4\": {\n" + " }\n" + "}"; + +/// @brief A DHCPv4 config with one subnet with two pools. +const std::string subnetTwoPoolsJson4 = + "{\n" + " \"Dhcp4\": {\n" + " \"subnet4\": [\n" + " {\n" + " \"id\": 111,\n" + " \"pools\": [\n" + " {\n" + " \"pool\": \"10.0.1.0/24\"\n" + " },\n" + " {\n" + " \"pool\": \"10.0.2.0/24\"\n" + " }\n" + " ],\n" + " \"subnet\": \"10.0.0.0/8\"\n" + " }\n" + " ]\n" + " }\n" + "}"; + +/// @brief A DHCPv6 config with one subnet with two pools and timers. +const std::string subnetTimersJson6 = + "{\n" + " \"Dhcp6\": {\n" + " \"subnet6\": [\n" + " {\n" + " \"id\": 111,\n" + " \"renew-timer\": 1000,\n" + " \"rebind-timer\": 2000,\n" + " \"pools\": [\n" + " {\n" + " \"pool\": \"2001:db8::1:0/112\"\n" + " },\n" + " {\n" + " \"pool\": \"2001:db8::2:0/112\"\n" + " }\n" + " ],\n" + " \"subnet\": \"2001:db8::/48\"\n" + " }\n" + " ]\n" + " }\n" + "}"; + +/// @brief A DHCPv4 subnet with one pool and option data lists. +const std::string subnetOptionsJson4 = + "{\n" + " \"Dhcp4\": {\n" + " \"subnet4\": [\n" + " {\n" + " \"id\": 111,\n" + " \"option-data\": [\n" + " {\n" + " \"code\": 100,\n" + " \"space\": \"dns\",\n" + " \"csv-format\": false,\n" + " \"data\": \"12121212\",\n" + " \"always-send\": false\n" + " }\n" + " ],\n" + " \"pools\": [\n" + " {\n" + " \"pool\": \"10.0.1.0/24\"\n" + " }\n" + " ],\n" + " \"subnet\": \"10.0.0.0/8\"\n" + " }\n" + " ]\n" + " }\n" + "}"; + +/// @brief Empty DHCPv6 config. +const std::string emptyJson6 = + "{\n" + " \"Dhcp6\": {\n" + " }\n" + "}"; + +/// @brief A DHCPv6 config with one subnet with one pool and option data lists. +const std::string subnetOptionsJson6 = + "{\n" + " \"Dhcp6\": {\n" + " \"subnet6\": [\n" + " {\n" + " \"id\": 111,\n" + " \"pools\": [\n" + " {\n" + " \"option-data\": [\n" + " {\n" + " \"code\": 100,\n" + " \"space\": \"dns\",\n" + " \"csv-format\": false,\n" + " \"data\": \"12121212\",\n" + " \"always-send\": false\n" + " }\n" + " ],\n" + " \"pool\": \"2001:db8::1:0/112\"\n" + " }\n" + " ],\n" + " \"subnet\": \"2001:db8::/48\"\n" + " }\n" + " ]\n" + " }\n" + "}"; + +/// @brief A DHCPv6 config with one subnet with two pools. +const std::string subnetTwoPoolsJson6 = + "{\n" + " \"Dhcp6\": {\n" + " \"subnet6\": [\n" + " {\n" + " \"id\": 111,\n" + " \"pools\": [\n" + " {\n" + " \"pool\": \"2001:db8::1:0/112\"\n" + " },\n" + " {\n" + " \"pool\": \"2001:db8::2:0/112\"\n" + " }\n" + " ],\n" + " \"subnet\": \"2001:db8::/48\"\n" + " }\n" + " ]\n" + " }\n" + "}"; + +/// @brief Example from the design document. +const std::string designExampleJson = + "{\n" + " \"Dhcp6\": {\n" + " \"subnet6\": [\n" + " {\n" + " \"id\": 1,\n" + " \"subnet\": \"2001:db8:20:b00::/56\",\n" + " \"user-context\": { \"description\": \"example\" },\n" + " \"pools\": [ ],\n" + " \"pd-pools\": [\n" + " {\n" + " \"prefix\": \"2001:db8:20:b00::\",\n" + " \"prefix-len\": 57\n" +// " \"delegated-len\": 57\n" + " }\n" + " ]\n" + " }\n" + " ]\n" + " }\n" + "}"; + +}; // end of namespace isc::yang::test +}; // end of namespace isc::yang +}; // end of namespace isc + +#endif // ISC_JSON_CONFIGS_H diff --git a/src/lib/yang/tests/translator_utils_unittests.cc b/src/lib/yang/tests/translator_utils_unittests.cc index 204120d005..6178cacb66 100644 --- a/src/lib/yang/tests/translator_utils_unittests.cc +++ b/src/lib/yang/tests/translator_utils_unittests.cc @@ -215,7 +215,7 @@ void sanityCheckConfig(const std::string& model, const YRTree& tree) { // defined in yang_configs.h are sane. TEST(YangReprTest, verifyConfigs) { for (auto x : test_configs) { - cout << "Testing tree for model " << x.first << endl; + //cout << "Testing tree for model " << x.first << endl; sanityCheckConfig(x.first, x.second); } } diff --git a/src/lib/yang/translator_config.cc b/src/lib/yang/translator_config.cc new file mode 100644 index 0000000000..04a7ae8c8e --- /dev/null +++ b/src/lib/yang/translator_config.cc @@ -0,0 +1,908 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include +#include +#include +#include + +using namespace std; +using namespace isc::data; + +namespace isc { +namespace yang { + +TranslatorConfig::TranslatorConfig(S_Session session, const string& model) + : TranslatorBasic(session), + TranslatorControlSocket(session, model), + TranslatorDatabase(session, model), + TranslatorDatabases(session, model), + TranslatorOptionData(session, model), + TranslatorOptionDataList(session, model), + TranslatorOptionDef(session, model), + TranslatorOptionDefList(session, model), + TranslatorClass(session, model), + TranslatorClasses(session, model), + TranslatorPool(session, model), + TranslatorPools(session, model), + TranslatorPdPool(session, model), + TranslatorPdPools(session, model), + TranslatorHost(session, model), + TranslatorHosts(session, model), + TranslatorSubnet(session, model), + TranslatorSubnets(session, model), + TranslatorSharedNetwork(session, model), + TranslatorSharedNetworks(session, model), + TranslatorLogger(session, model), + TranslatorLoggers(session, model), + model_(model) { +} + +TranslatorConfig::~TranslatorConfig() { +} + +ElementPtr +TranslatorConfig::getConfig() { + try { + if (model_ == IETF_DHCPV6_SERVER) { + return (getConfigIetf6()); + } else if (model_ == KEA_DHCP4_SERVER) { + return (getConfigKea4()); + } else if (model_ == KEA_DHCP6_SERVER) { + return (getConfigKea6()); + } + } catch (const sysrepo_exception& ex) { + isc_throw(SysrepoError, "sysrepo error getting config: " << ex.what()); + } + isc_throw(NotImplemented, + "getConfig not implemented for the model: " << model_); +} + +ElementPtr +TranslatorConfig::getConfigIetf6() { + ElementPtr result = Element::createMap(); + ElementPtr dhcp6 = Element::createMap(); + result->set("Dhcp6", dhcp6); + string xpath = "/" + model_ + ":server/server-config"; + ConstElementPtr ranges = getSubnets(xpath + "/network-ranges"); + if (ranges && (ranges->size() > 0)) { + dhcp6->set("subnet6", ranges); + } + // Skip everything else. + return (result); +} + +ElementPtr +TranslatorConfig::getConfigKea4() { + ElementPtr result = Element::createMap(); + result->set("Dhcp4", getServerKeaDhcp4()); + ConstElementPtr logging = getServerKeaLogging(); + if (logging && (logging->size() > 0)) { + result->set("Logging", logging); + } + return (result); +} + +ElementPtr +TranslatorConfig::getConfigKea6() { + ElementPtr result = Element::createMap(); + result->set("Dhcp6", getServerKeaDhcp6()); + ConstElementPtr logging = getServerKeaLogging(); + if (logging && (logging->size() > 0)) { + result->set("Logging", logging); + } + return (result); +} + +ElementPtr +TranslatorConfig::getServerKeaDhcpCommon(const string& xpath) { + ElementPtr result = Element::createMap(); + ConstElementPtr valid = getItem(xpath + "/valid-lifetime"); + if (valid) { + result->set("valid-lifetime", valid); + } + ConstElementPtr renew = getItem(xpath + "/renew-timer"); + if (renew) { + result->set("renew-timer", renew); + } + ConstElementPtr rebind = getItem(xpath + "/rebind-timer"); + if (rebind) { + result->set("rebind-timer", rebind); + } + ConstElementPtr period = getItem(xpath + "/decline-probation-period"); + if (period) { + result->set("decline-probation-period", period); + } + ConstElementPtr networks = getSharedNetworks(xpath + "/shared-networks"); + if (networks && (networks->size() > 0)) { + result->set("shared-networks", networks); + } + ConstElementPtr classes = getClasses(xpath + "/client-classes"); + if (classes && (classes->size() > 0)) { + result->set("client-classes", classes); + } + ConstElementPtr database = getDatabase(xpath + "/lease-database"); + if (database) { + result->set("lease-database", database); + } + ConstElementPtr databases = getDatabases(xpath + "/hosts-databases"); + if (databases && (databases->size() > 0)) { + result->set("hosts-databases", databases); + } + ConstElementPtr host_ids = + getItems(xpath + "/host-reservation-identifiers"); + if (host_ids) { + result->set("host-reservation-identifiers", host_ids); + } + ConstElementPtr defs = getOptionDefList(xpath + "/option-def-list"); + if (defs && (defs->size() > 0)) { + result->set("option-def", defs); + } + ConstElementPtr options = getOptionDataList(xpath + "/option-data-list"); + if (options && (options->size() > 0)) { + result->set("option-data", options); + } + S_Iter_Value iter = getIter(xpath + "/hooks-libraries/*"); + if (iter) { + ElementPtr hook_libs = Element::createList(); + for (;;) { + const string& lib = getNext(iter); + if (lib.empty()) { + break; + } + ElementPtr hook_lib = Element::createMap(); + ConstElementPtr name = getItem(lib + "/library"); + if (name) { + hook_lib->set("library", name); + ConstElementPtr params = getItem(lib + "/parameters"); + if (params) { + string parameters = params->stringValue(); + if (!parameters.empty()) { + hook_lib->set("parameters", + Element::fromJSON(parameters)); + } + } + hook_libs->add(hook_lib); + } + } + if (hook_libs->size() > 0) { + result->set("hooks-libraries", hook_libs); + } + } + ElementPtr expired = Element::createMap(); + ConstElementPtr reclaim = + getItem(xpath + "/expired-leases-processing/reclaim-timer-wait-time"); + if (reclaim) { + expired->set("reclaim-timer-wait-time", reclaim); + } + ConstElementPtr flush = + getItem(xpath + "/expired-leases-processing/flush-reclaimed-timer-wait-time"); + if (flush) { + expired->set("flush-reclaimed-timer-wait-time", flush); + } + ConstElementPtr hold = + getItem(xpath + "/expired-leases-processing/hold-reclaimed-time"); + if (hold) { + expired->set("hold-reclaimed-time", hold); + } + ConstElementPtr max_leases = + getItem(xpath + "/expired-leases-processing/max-reclaim-leases"); + if (max_leases) { + expired->set("max-reclaim-leases", max_leases); + } + ConstElementPtr max_time = + getItem(xpath + "/expired-leases-processing/max-reclaim-time"); + if (max_time) { + expired->set("max-reclaim-time", max_time); + } + ConstElementPtr unwarned = + getItem(xpath + "/expired-leases-processing/unwarned-reclaim-cycles"); + if (unwarned) { + expired->set("unwarned-reclaim-cycles", unwarned); + } + if (expired->size() > 0) { + result->set("expired-leases-processing", expired); + } + ConstElementPtr port = getItem(xpath + "/dhcp4o6-port"); + if (port) { + result->set("dhcp4o6-port", port); + } + ConstElementPtr socket = getControlSocket(xpath + "/control-socket"); + if (socket) { + result->set("control-socket", socket); + } + ElementPtr ddns = Element::createMap(); + ConstElementPtr enable = getItem(xpath + "/dhcp-ddns/enable-updates"); + if (enable) { + ddns->set("enable-updates", enable); + } + ConstElementPtr suffix = getItem(xpath + "/dhcp-ddns/qualifying-suffix"); + if (suffix) { + ddns->set("qualifying-suffix", suffix); + } + ConstElementPtr server_ip = getItem(xpath + "/dhcp-ddns/server-ip"); + if (server_ip) { + ddns->set("server-ip", server_ip); + } + ConstElementPtr server_port = getItem(xpath + "/dhcp-ddns/server-port"); + if (server_port) { + ddns->set("server-port", server_port); + } + ConstElementPtr sender_ip = getItem(xpath + "/dhcp-ddns/sender-ip"); + if (sender_ip) { + ddns->set("sender-ip", sender_ip); + } + ConstElementPtr sender_port = getItem(xpath + "/dhcp-ddns/sender-port"); + if (sender_port) { + ddns->set("sender-port", sender_port); + } + ConstElementPtr queue = getItem(xpath + "/dhcp-ddns/max-queue-size"); + if (queue) { + ddns->set("max-queue-size", queue); + } + ConstElementPtr protocol = getItem(xpath + "/dhcp-ddns/ncr-protocol"); + if (protocol) { + ddns->set("ncr-protocol", protocol); + } + ConstElementPtr format = getItem(xpath + "/dhcp-ddns/ncr-format"); + if (format) { + ddns->set("ncr-format", format); + } + ConstElementPtr always = getItem(xpath + "/dhcp-ddns/always-include-fqdn"); + if (always) { + ddns->set("always-include-fqdn", always); + } + ConstElementPtr no_up = getItem(xpath + "/dhcp-ddns/override-no-update"); + if (no_up) { + ddns->set("override-no-update", no_up); + } + ConstElementPtr client = + getItem(xpath + "/dhcp-ddns/override-client-update"); + if (client) { + ddns->set("override-client-update", client); + } + ConstElementPtr replace = + getItem(xpath + "/dhcp-ddns/replace-client-name"); + if (replace) { + ddns->set("replace-client-name", replace); + } + ConstElementPtr generated = getItem(xpath + "/dhcp-ddns/generated-prefix"); + if (generated) { + ddns->set("generated-prefix", generated); + } + ConstElementPtr char_set = getItem(xpath + "/dhcp-ddns/hostname-char-set"); + if (char_set) { + ddns->set("hostname-char-set", char_set); + } + ConstElementPtr char_repl = + getItem(xpath + "/dhcp-ddns/hostname-char-replacement"); + if (char_repl) { + ddns->set("hostname-char-replacement", char_repl); + } + ConstElementPtr context = getItem(xpath + "/dhcp-ddns/user-context"); + if (context) { + ddns->set("user-context", Element::fromJSON(context->stringValue())); + } + if (ddns->size() > 0) { + result->set("dhcp-ddns", ddns); + } + context = getItem(xpath + "/user-context"); + if (context) { + result->set("user-context", Element::fromJSON(context->stringValue())); + } + ConstElementPtr checks = getItem(xpath + "/sanity-checks/lease-checks"); + if (checks) { + ElementPtr sanity = Element::createMap(); + sanity->set("lease-checks", checks); + result->set("sanity-checks", sanity); + } + ConstElementPtr hosts = getHosts(xpath + "/reservations"); + if (hosts && (hosts->size() > 0)) { + result->set("reservations", hosts); + } + return (result); +} + +ElementPtr +TranslatorConfig::getServerKeaDhcp4() { + string xpath = "/kea-dhcp4-server:config"; + ElementPtr result = getServerKeaDhcpCommon(xpath); + ConstElementPtr subnets = getSubnets(xpath + "/subnet4"); + if (subnets && (subnets->size() > 0)) { + result->set("subnet4", subnets); + } + ElementPtr if_config = Element::createMap(); + ConstElementPtr ifs = getItems(xpath + "/interfaces-config/interfaces"); + if (ifs && (ifs->size() > 0)) { + if_config->set("interfaces", ifs); + } + ConstElementPtr ds_type = + getItem(xpath + "/interfaces-config/dhcp-socket-type"); + if (ds_type) { + if_config->set("dhcp-socket-type", ds_type); + } + ConstElementPtr out_if = + getItem(xpath + "/interfaces-config/outbound-interface"); + if (out_if) { + if_config->set("outbound-interface", out_if); + } + ConstElementPtr redetect = + getItem(xpath + "/interfaces-config/re-detect"); + if (redetect) { + if_config->set("re-detect", redetect); + } + ConstElementPtr context = + getItem(xpath + "/interfaces-config/user-context"); + if (context) { + if_config->set("user-context", + Element::fromJSON(context->stringValue())); + } + if (if_config->size() > 0) { + result->set("interfaces-config", if_config); + } + ConstElementPtr echo = getItem(xpath + "/echo-client-id"); + if (echo) { + result->set("echo-client-id", echo); + } + ConstElementPtr match = getItem(xpath + "/match-client-id"); + if (match) { + result->set("match-client-id", match); + } + ConstElementPtr next = getItem(xpath + "/next-server"); + if (next) { + result->set("next-server", next); + } + ConstElementPtr hostname = getItem(xpath + "/server-hostname"); + if (hostname) { + result->set("server-hostname", hostname); + } + ConstElementPtr boot = getItem(xpath + "/boot-file-name"); + if (boot) { + result->set("boot-file-name", boot); + } + return (result); +} + +ElementPtr +TranslatorConfig::getServerKeaDhcp6() { + string xpath = "/kea-dhcp6-server:config"; + ElementPtr result = getServerKeaDhcpCommon(xpath); + ConstElementPtr preferred = getItem(xpath + "/preferred-lifetime"); + if (preferred) { + result->set("preferred-lifetime", preferred); + } + ConstElementPtr subnets = getSubnets(xpath + "/subnet6"); + if (subnets && (subnets->size() > 0)) { + result->set("subnet6", subnets); + } + ElementPtr if_config = Element::createMap(); + ConstElementPtr ifs = getItems(xpath + "/interfaces-config/interfaces"); + if (ifs && (ifs->size() > 0)) { + if_config->set("interfaces", ifs); + } + ConstElementPtr redetect = + getItem(xpath + "/interfaces-config/re-detect"); + if (redetect) { + if_config->set("re-detect", redetect); + } + ConstElementPtr context = + getItem(xpath + "/interfaces-config/user-context"); + if (context) { + if_config->set("user-context", + Element::fromJSON(context->stringValue())); + } + if (if_config->size() > 0) { + result->set("interfaces-config", if_config); + } + ConstElementPtr relay = getItems(xpath + "/relay-supplied-options"); + if (relay) { + result->set("relay-supplied-options", relay); + } + ConstElementPtr macs = getItems(xpath + "/mac-sources"); + if (macs) { + result->set("mac-sources", macs); + } + ElementPtr server_id = Element::createMap(); + ConstElementPtr id_type = getItem(xpath + "/server-id/type"); + if (id_type) { + server_id->set("type", id_type); + } + ConstElementPtr id_id = getItem(xpath + "/server-id/identifier"); + if (id_id) { + server_id->set("identifier", id_id); + } + ConstElementPtr id_time = getItem(xpath + "/server-id/time"); + if (id_time) { + server_id->set("time", id_time); + } + ConstElementPtr id_htype = getItem(xpath + "/server-id/htype"); + if (id_htype) { + server_id->set("htype", id_htype); + } + ConstElementPtr id_ent_id = getItem(xpath + "/server-id/enterprise-id"); + if (id_ent_id) { + server_id->set("enterprise-id", id_ent_id); + } + ConstElementPtr id_persist = getItem(xpath + "/server-id/persist"); + if (id_persist) { + server_id->set("persist", id_persist); + } + context = getItem(xpath + "/server-id/user-context"); + if (context) { + server_id->set("user-context", + Element::fromJSON(context->stringValue())); + } + if (server_id->size() > 0) { + result->set("server-id", server_id); + } + return (result); +} + +ElementPtr +TranslatorConfig::getServerKeaLogging() { + string xpath = "/" + model_ + ":logging"; + ElementPtr result = Element::createMap(); + ConstElementPtr loggers = getLoggers(xpath + "/loggers"); + if (loggers && (loggers->size() > 0)) { + result->set("loggers", loggers); + } + return (result); +} + +void +TranslatorConfig::setConfig(ConstElementPtr elem) { + try { + if (model_ == IETF_DHCPV6_SERVER) { + if (elem) { + AdaptorConfig::preProcess6(elem); + setConfigIetf6(elem); + } else { + delConfigIetf6(); + } + } else if (model_ == KEA_DHCP4_SERVER) { + if (elem) { + AdaptorConfig::preProcess4(elem); + setConfigKea4(elem); + } else { + delConfigKea(); + } + } else if (model_ == KEA_DHCP6_SERVER) { + if (elem) { + AdaptorConfig::preProcess6(elem); + setConfigKea6(elem); + } else { + delConfigKea(); + } + } else { + isc_throw(NotImplemented, + "setConfig not implemented for the model: " << model_); + } + } catch (const sysrepo_exception& ex) { + isc_throw(SysrepoError, + "sysrepo error setting config '" << elem->str() + << "': " << ex.what()); + } +} + +void +TranslatorConfig::delConfigIetf6() { + delItem("/" + model_ + ":server"); +} + +void +TranslatorConfig::setConfigIetf6(ConstElementPtr elem) { + string xpath = "/" + model_ + ":server/server-config"; + ConstElementPtr dhcp6 = elem->get("Dhcp6"); + if (!dhcp6) { + isc_throw(BadValue, "no Dhcp6 entry in " << elem->str()); + } + ConstElementPtr ranges = dhcp6->get("subnet6"); + if (ranges && (ranges->size() > 0)) { + setSubnets(xpath + "/network-ranges", ranges); + } + // Skip everything else. +} + +void +TranslatorConfig::delConfigKea() { + delItem("/" + model_ + ":config"); + delItem("/" + model_ + ":logging"); +} + +void +TranslatorConfig::setConfigKea4(ConstElementPtr elem) { + ConstElementPtr dhcp = elem->get("Dhcp4"); + if (dhcp) { + setServerKeaDhcp4(dhcp); + } + ConstElementPtr logging = elem->get("Logging"); + if (logging) { + setServerKeaLogging(logging); + } +} + +void +TranslatorConfig::setConfigKea6(ConstElementPtr elem) { + ConstElementPtr dhcp = elem->get("Dhcp6"); + if (dhcp) { + setServerKeaDhcp6(dhcp); + } + ConstElementPtr logging = elem->get("Logging"); + if (logging) { + setServerKeaLogging(logging); + } +} + +void +TranslatorConfig::setServerKeaDhcpCommon(const string& xpath, + ConstElementPtr elem) { + ConstElementPtr valid = elem->get("valid-lifetime"); + if (valid) { + setItem(xpath + "/valid-lifetime", valid, SR_UINT32_T); + } + ConstElementPtr renew = elem->get("renew-timer"); + if (renew) { + setItem(xpath + "/renew-timer", renew, SR_UINT32_T); + } + ConstElementPtr rebind = elem->get("rebind-timer"); + if (rebind) { + setItem(xpath + "/rebind-timer", rebind, SR_UINT32_T); + } + ConstElementPtr period = elem->get("decline-probation-period"); + if (period) { + setItem(xpath + "/decline-probation-period", period, SR_UINT32_T); + } + ConstElementPtr networks = elem->get("shared-networks"); + if (networks) { + setSharedNetworks(xpath + "/shared-networks", networks); + } + ConstElementPtr classes = elem->get("client-classes"); + if (classes && (classes->size() > 0)) { + setClasses(xpath + "/client-classes", classes); + } + ConstElementPtr database = elem->get("lease-database"); + if (database) { + setDatabase(xpath + "/lease-database", database); + } + ConstElementPtr databases = elem->get("hosts-databases"); + if (databases) { + if (databases->size() > 0) { + setDatabases(xpath + "/hosts-databases", databases); + } + } else { + database = elem->get("hosts-database"); + if (database) { + ElementPtr list = Element::createList(); + list->add(copy(database)); + setDatabases(xpath + "/hosts-databases", list); + } + } + ConstElementPtr host_ids = elem->get("host-reservation-identifiers"); + if (host_ids) { + for (ConstElementPtr id : host_ids->listValue()) { + setItem(xpath + "/host-reservation-identifiers", id, SR_ENUM_T); + } + } + ConstElementPtr defs = elem->get("option-def"); + if (defs && (defs->size() > 0)) { + setOptionDefList(xpath + "/option-def-list", defs); + } + ConstElementPtr options = elem->get("option-data"); + if (options && (options->size() > 0)) { + setOptionDataList(xpath + "/option-data-list", options); + } + ConstElementPtr hook_libs = elem->get("hooks-libraries"); + if (hook_libs) { + for (ConstElementPtr lib : hook_libs->listValue()) { + ConstElementPtr name = lib->get("library"); + if (!name) { + continue; + } + ostringstream hook_lib; + hook_lib << xpath << "/hooks-libraries/hook-library[library='" + << name->stringValue() << "']"; + ConstElementPtr params = lib->get("parameters"); + if (params) { + hook_lib << "/parameters"; + setItem(hook_lib.str(), Element::create(params->str()), + SR_STRING_T); + } else { + ConstElementPtr list = Element::createList(); + setItem(hook_lib.str(), list, SR_LIST_T); + } + } + } + ConstElementPtr expired = elem->get("expired-leases-processing"); + if (expired) { + ConstElementPtr reclaim = expired->get("reclaim-timer-wait-time"); + if (reclaim) { + setItem(xpath + "/expired-leases-processing/reclaim-timer-wait-time", + reclaim, SR_UINT32_T); + } + ConstElementPtr flush = + expired->get("flush-reclaimed-timer-wait-time"); + if (flush) { + setItem(xpath + "/expired-leases-processing/flush-reclaimed-timer-wait-time", + flush, SR_UINT32_T); + } + ConstElementPtr hold = expired->get("hold-reclaimed-time"); + if (hold) { + setItem(xpath + "/expired-leases-processing/hold-reclaimed-time", + hold, SR_UINT32_T); + } + ConstElementPtr max_leases = expired->get("max-reclaim-leases"); + if (max_leases) { + setItem(xpath + "/expired-leases-processing/max-reclaim-leases", + max_leases, SR_UINT32_T); + } + ConstElementPtr max_time = expired->get("max-reclaim-time"); + if (max_time) { + setItem(xpath + "/expired-leases-processing/max-reclaim-time", + max_time, SR_UINT32_T); + } + ConstElementPtr unwarned = expired->get("unwarned-reclaim-cycles"); + if (unwarned) { + setItem(xpath + "/expired-leases-processing/unwarned-reclaim-cycles", + unwarned, SR_UINT32_T); + } + } + ConstElementPtr port = elem->get("dhcp4o6-port"); + if (port) { + setItem(xpath + "/dhcp4o6-port", port, SR_UINT16_T); + } + ConstElementPtr socket = elem->get("control-socket"); + if (socket) { + setControlSocket(xpath + "/control-socket", socket); + } + ConstElementPtr ddns = elem->get("dhcp-ddns"); + if (ddns) { + ConstElementPtr enable = ddns->get("enable-updates"); + if (enable) { + setItem(xpath + "/dhcp-ddns/enable-updates", enable, SR_BOOL_T); + } + ConstElementPtr suffix = ddns->get("qualifying-suffix"); + if (suffix) { + setItem(xpath + "/dhcp-ddns/qualifying-suffix", suffix, + SR_STRING_T); + } + ConstElementPtr server_ip = ddns->get("server-ip"); + if (server_ip) { + setItem(xpath + "/dhcp-ddns/server-ip", server_ip, SR_STRING_T); + } + ConstElementPtr server_port = ddns->get("server-port"); + if (server_port) { + setItem(xpath + "/dhcp-ddns/server-port", server_port, + SR_UINT16_T); + } + ConstElementPtr sender_ip = ddns->get("sender-ip"); + if (sender_ip) { + setItem(xpath + "/dhcp-ddns/sender-ip", sender_ip, SR_STRING_T); + } + ConstElementPtr sender_port = ddns->get("sender-port"); + if (sender_port) { + setItem(xpath + "/dhcp-ddns/sender-port", sender_port, + SR_UINT16_T); + } + ConstElementPtr queue = ddns->get("max-queue-size"); + if (queue) { + setItem(xpath + "/dhcp-ddns/max-queue-size", queue, SR_UINT32_T); + } + ConstElementPtr protocol = ddns->get("ncr-protocol"); + if (protocol) { + setItem(xpath + "/dhcp-ddns/ncr-protocol", protocol, SR_ENUM_T); + } + ConstElementPtr format = ddns->get("ncr-format"); + if (format) { + setItem(xpath + "/dhcp-ddns/ncr-format", format, SR_ENUM_T); + } + ConstElementPtr always = ddns->get("always-include-fqdn"); + if (always) { + setItem(xpath + "/dhcp-ddns/always-include-fqdn", always, + SR_BOOL_T); + } + ConstElementPtr no_up = ddns->get("override-no-update"); + if (no_up) { + setItem(xpath + "/dhcp-ddns/override-no-update", no_up, SR_BOOL_T); + } + ConstElementPtr client = ddns->get("override-client-update"); + if (client) { + setItem(xpath + "/dhcp-ddns/override-client-update", client, + SR_BOOL_T); + } + ConstElementPtr replace = ddns->get("replace-client-name"); + if (replace) { + setItem(xpath + "/dhcp-ddns/replace-client-name", replace, + SR_ENUM_T); + } + ConstElementPtr generated = ddns->get("generated-prefix"); + if (generated) { + setItem(xpath + "/dhcp-ddns/generated-prefix", generated, + SR_STRING_T); + } + ConstElementPtr char_set = ddns->get("hostname-char-set"); + if (char_set) { + setItem(xpath + "/dhcp-ddns/hostname-char-set", char_set, + SR_STRING_T); + } + ConstElementPtr char_repl = ddns->get("hostname-char-replacement"); + if (char_repl) { + setItem(xpath + "/dhcp-ddns/hostname-char-replacement", char_repl, + SR_STRING_T); + } + ConstElementPtr context = Adaptor::getContext(ddns); + if (context) { + ConstElementPtr repr = Element::create(context->str()); + setItem(xpath + "/dhcp-ddns/user-context", repr, SR_STRING_T); + } + } + ConstElementPtr context = Adaptor::getContext(elem); + if (context) { + ConstElementPtr repr = Element::create(context->str()); + setItem(xpath + "/user-context", repr, SR_STRING_T); + } + ConstElementPtr sanity = elem->get("sanity-checks"); + if (sanity) { + ConstElementPtr checks = sanity->get("lease-checks"); + if (checks) { + setItem(xpath + "/sanity-checks/lease-checks", checks, SR_ENUM_T); + } + } + ConstElementPtr hosts = elem->get("reservations"); + if (hosts && (hosts->size() > 0)) { + setHosts(xpath + "/reservations", hosts); + } +} + +void +TranslatorConfig::setServerKeaDhcp4(ConstElementPtr elem) { + string xpath = "/kea-dhcp4-server:config"; + setServerKeaDhcpCommon(xpath, elem); + ConstElementPtr subnets = elem->get("subnet4"); + if (subnets) { + setSubnets(xpath + "/subnet4", subnets); + } + ConstElementPtr if_config = elem->get("interfaces-config"); + if (if_config) { + ConstElementPtr ifs = if_config->get("interfaces"); + if (ifs && (ifs->size() > 0)) { + for (ConstElementPtr intf : ifs->listValue()) { + setItem(xpath + "/interfaces-config/interfaces", + intf, SR_STRING_T); + } + } + ConstElementPtr ds_type = if_config->get("dhcp-socket-type"); + if (ds_type) { + setItem(xpath + "/interfaces-config/dhcp-socket-type", + ds_type, SR_ENUM_T); + } + ConstElementPtr out_if = if_config->get("outbound-interface"); + if (out_if) { + setItem(xpath + "/interfaces-config/outbound-interface", + out_if, SR_ENUM_T); + } + ConstElementPtr redetect = if_config->get("re-detect"); + if (redetect) { + setItem(xpath + "/interfaces-config/re-detect", + redetect, SR_BOOL_T); + } + ConstElementPtr context = Adaptor::getContext(if_config); + if (context) { + setItem(xpath + "/interfaces-config/user-context", + Element::create(context->str()), SR_STRING_T); + } + } + ConstElementPtr echo = elem->get("echo-client-id"); + if (echo) { + setItem(xpath + "/echo-client-id", echo, SR_BOOL_T); + } + ConstElementPtr match = elem->get("match-client-id"); + if (match) { + setItem(xpath + "/match-client-id", match, SR_BOOL_T); + } + ConstElementPtr next = elem->get("next-server"); + if (next) { + setItem(xpath + "/next-server", next, SR_STRING_T); + } + ConstElementPtr hostname = elem->get("server-hostname"); + if (hostname) { + setItem(xpath + "/server-hostname", hostname, SR_STRING_T); + } + ConstElementPtr boot = elem->get("boot-file-name"); + if (boot) { + setItem(xpath + "/boot-file-name", boot, SR_STRING_T); + } +} + +void +TranslatorConfig::setServerKeaDhcp6(ConstElementPtr elem) { + string xpath = "/kea-dhcp6-server:config"; + setServerKeaDhcpCommon(xpath, elem); + ConstElementPtr preferred = elem->get("preferred-lifetime"); + if (preferred) { + setItem(xpath + "/preferred-lifetime", preferred, SR_UINT32_T); + } + ConstElementPtr subnets = elem->get("subnet6"); + if (subnets) { + setSubnets(xpath + "/subnet6", subnets); + } + ConstElementPtr if_config = elem->get("interfaces-config"); + if (if_config) { + ConstElementPtr ifs = if_config->get("interfaces"); + if (ifs && (ifs->size() > 0)) { + for (ConstElementPtr intf : ifs->listValue()) { + setItem(xpath + "/interfaces-config/interfaces", + intf, SR_STRING_T); + } + } + ConstElementPtr redetect = if_config->get("re-detect"); + if (redetect) { + setItem(xpath + "/interfaces-config/re-detect", + redetect, SR_BOOL_T); + } + ConstElementPtr context = Adaptor::getContext(if_config); + if (context) { + setItem(xpath + "/interfaces-config/user-context", + Element::create(context->str()), SR_STRING_T); + } + } + ConstElementPtr relay = elem->get("relay-supplied-options"); + if (relay) { + for (ConstElementPtr addr : relay->listValue()) { + setItem(xpath + "/relay-supplied-options", addr, SR_STRING_T); + } + } + ConstElementPtr macs = elem->get("mac-sources"); + if (macs) { + for (ConstElementPtr source : macs->listValue()) { + setItem(xpath + "/mac-sources", source, SR_STRING_T); + } + } + ConstElementPtr server_id = elem->get("server-id"); + if (server_id) { + ConstElementPtr id_type = server_id->get("type"); + if (id_type) { + setItem(xpath + "/server-id/type", id_type, SR_ENUM_T); + } + ConstElementPtr id_id = server_id->get("identifier"); + if (id_id) { + setItem(xpath + "/server-id/identifier", id_id, SR_STRING_T); + } + ConstElementPtr id_time = server_id->get("time"); + if (id_time) { + setItem(xpath + "/server-id/time", id_time, SR_UINT32_T); + } + ConstElementPtr id_htype = server_id->get("htype"); + if (id_htype) { + setItem(xpath + "/server-id/htype", id_htype, SR_UINT16_T); + } + ConstElementPtr id_ent_id = server_id->get("enterprise-id"); + if (id_ent_id) { + setItem(xpath + "/server-id/enterprise-id", id_ent_id, + SR_UINT32_T); + } + ConstElementPtr id_persist = server_id->get("persist"); + if (id_persist) { + setItem(xpath + "/server-id/persist", id_persist, SR_BOOL_T); + } + ConstElementPtr context = Adaptor::getContext(server_id); + if (context) { + ConstElementPtr repr = Element::create(context->str()); + setItem(xpath + "/server-id/user-context", repr, SR_STRING_T); + } + } +} + +void +TranslatorConfig::setServerKeaLogging(ConstElementPtr elem) { + string xpath = "/" + model_ + ":logging"; + ConstElementPtr loggers = elem->get("loggers"); + if (loggers) { + setLoggers(xpath + "/loggers", loggers); + } +} + +}; // end of namespace isc::yang +}; // end of namespace isc diff --git a/src/lib/yang/translator_config.h b/src/lib/yang/translator_config.h new file mode 100644 index 0000000000..b638d79f5d --- /dev/null +++ b/src/lib/yang/translator_config.h @@ -0,0 +1,308 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef ISC_TRANSLATOR_CONFIG_H +#define ISC_TRANSLATOR_CONFIG_H 1 + +#include +#include +#include +#include +#include +#include +#include + +namespace isc { +namespace yang { + +/// Configuration translation between YANG and JSON +/// +/// JSON syntax for kea-dhcp6-server is: +/// @code +/// "Dhcp6": { +/// "preferred-lifetime": , +/// "valid-lifetime": , +/// "renew-timer": , +/// "rebind-timer": , +/// "decline-probation-period": , +/// "subnet6": [ ], +/// , +/// "interfaces-config": { +/// "interfaces" [ ], +/// ... +/// }, +/// , +/// , +/// , +/// , +/// , +/// , +/// , +/// , +/// , +/// , +/// , +/// , +/// , +/// , +/// +/// }, +/// "Logging": +/// @endcode +/// +/// @todo: add kea-dhcp4 syntax and expand logging. +/// +/// YANG syntax for kea-dhcp6-server:config is: +/// @code +/// +--rw preferred-lifetime? uint32 +/// +--rw valid-lifetime? uint32 +/// +--rw renew-timer? uint32 +/// +--rw rebind-timer? uint32 +/// +--rw decline-probation-period? uint32 +/// +--rw subnet6 subnet6* +/// +--rw shared-networks shared-network* +/// +--rw interfaces-config +/// +--rw interfaces* string +/// +--rw re-detect? boolean +/// +--rw user-context? string +/// +--rw lease-database! +/// +--rw hosts-databases hosts-database* +/// +--rw relay-supplied-options* string +/// +--rw mac-sources* string +/// +--rw host-reservation-identifiers* enumeration +/// +--rw client-classes client-class* +/// +--rw option-def-list option-def* +/// +--rw option-data-list option-data* +/// +--rw hook-library* +/// +--rw library string +/// +--rw parameters? string +/// +--rw expired-leases-processing +/// +--rw server-id +/// +--rw dhcp4o6-port? uint16 +/// +--rw control-socket! +/// +--rw dhcp-ddns +/// +--rw echo-client-id? boolean +/// +--rw user-context? string +/// +--rw sanity-checks +/// +--rw lease-checks? enumeration +/// @endcode +/// +/// YANG syntax for kea-dhcp4-server:config is: +/// @code +/// +--rw valid-lifetime? uint32 +/// +--rw renew-timer? uint32 +/// +--rw rebind-timer? uint32 +/// +--rw decline-probation-period? uint32 +/// +--rw subnet4 subnet4* +/// +--rw shared-networks shared-network* +/// +--rw interfaces-config +/// +--rw interfaces* string +/// +--rw dhcp-socket-type? enumeration +/// +--rw outbound-interface? enumeration +/// +--rw re-detect? boolean +/// +--rw user-context? string +/// +--rw lease-database! +/// +--rw hosts-databases hosts-database* +/// +--rw host-reservation-identifiers* enumeration +/// +--rw client-classes client-class* +/// +--rw option-def-list option-def* +/// +--rw option-data-list option-data* +/// +--rw hook-library* +/// +--rw library string +/// +--rw parameters? string +/// +--rw expired-leases-processing +/// +--rw dhcp4o6-port? uint16 +/// +--rw control-socket! +/// +--rw dhcp-ddns +/// +--rw echo-client-id? boolean +/// +--rw match-client-id? boolean +/// +--rw next-server? inet:ipv4-address +/// +--rw server-hostname? string +/// +--rw boot-file-name? string +/// +--rw user-context? string +/// +--rw sanity-checks +/// +--rw lease-checks? enumeration +/// @endcode +/// +/// YANG syntax for kea-*:logging is: +/// @code +/// +--rw logging +/// +--rw loggers +/// @endcode +/// +/// @todo add example +/// +/// Inheritance graph between translators is: +/// +/// +-----------------------------------------+ +/// | | +/// +------------------------------+ | +/// | | | +/// +----------+-------------------+----------+ +/// | | | | +/// | | +---------+----------+ +/// | | | | | +/// config +- shared +- subnet +- pool --+- option -+ basic +/// | network (list) | (list) | data | +/// | (list) | | (list) | +/// | | | | +/// | +- pd ----+ | +/// | | pool | | +/// | | (list) | | +/// | | | | +/// +--------------------+- host --+ | +/// | (liat) | | +/// | | | +/// | +--------------------+----------+ +/// | | | +/// +- class -+- option ----------------------+ +/// | (list) | def | +/// | | (list) | +/// +---------+ | +/// | | +/// + control --------------------------------+ +/// | socket | +/// | | +/// +------------+ | +/// | | | +/// +- database -+- database -----------------+ +/// | list | +/// | | +/// +- logger --------------------------------+ +/// (list) +/// +/// 'XXX (list)' stands for 'XXX list --- XXX' which is a common motif +/// (only database shows direct dependencies on both the list and the element) +/// +/// @brief A translator class for converting the config between +/// YANG and JSON. +/// +/// Currently supports kea-dhcp[46]-server, kea-logging and partially +/// ietf-dhcpv6-server. +class TranslatorConfig : virtual public TranslatorControlSocket, + virtual public TranslatorDatabases, + virtual public TranslatorClasses, + virtual public TranslatorSharedNetworks, + virtual public TranslatorLoggers { +public: + + /// @brief Constructor. + /// + /// @param session Sysrepo session. + /// @param model Model name. + TranslatorConfig(S_Session session, const std::string& model); + + /// @brief Destructor. + virtual ~TranslatorConfig(); + + /// @brief Get and translate a pool from YANG to JSON. + /// + /// @return JSON representation of the config. + /// @throw SysrepoError when sysrepo raises an error. + isc::data::ElementPtr getConfig(); + + /// @brief Translate and set config from JSON to YANG. + /// + /// Null elem argument removes the config containers. + /// + /// @param elem The JSON element. + void setConfig(isc::data::ConstElementPtr elem); + +protected: + /// @brief getConfig for ietf-dhcpv6-server. + /// + /// @return JSON representation of the config. + /// @throw SysrepoError when sysrepo raises an error. + isc::data::ElementPtr getConfigIetf6(); + + /// @brief delConfig for ietf-dhcpv6-server. + void delConfigIetf6(); + + /// @brief setConfig for ietf-dhcpv6-server. + /// + /// @param elem The JSON element. + /// @throw BadValue on config without Dhcp6. + void setConfigIetf6(isc::data::ConstElementPtr elem); + + /// @brief getConfig for kea-dhcp4-server. + /// + /// @return JSON representation of the config. + /// @throw SysrepoError when sysrepo raises an error. + isc::data::ElementPtr getConfigKea4(); + + /// @brief getConfig for kea-dhcp6-server. + /// + /// @return JSON representation of the config. + /// @throw SysrepoError when sysrepo raises an error. + isc::data::ElementPtr getConfigKea6(); + + /// @brief getServer common part for kea-dhcp[46]:config. + /// + /// @param xpath The xpath of the server. + /// @return JSON representation of the server. + isc::data::ElementPtr getServerKeaDhcpCommon(const std::string& xpath); + + /// @brief getServer for kea-dhcp4-server:config. + /// + /// @return JSON representation of the config. + /// @throw SysrepoError when sysrepo raises an error. + isc::data::ElementPtr getServerKeaDhcp4(); + + /// @brief getServer for kea-dhcp6-server:config. + /// + /// @return JSON representation of the config. + /// @throw SysrepoError when sysrepo raises an error. + isc::data::ElementPtr getServerKeaDhcp6(); + + /// @brief getServer for kea-*:logging. + /// + /// @return JSON representation of the config. + /// @throw SysrepoError when sysrepo raises an error. + isc::data::ElementPtr getServerKeaLogging(); + + /// @brief delConfig for kea-dhcp[46]-server. + void delConfigKea(); + + /// @brief setConfig for kea-dhcp4-server. + /// + /// @param elem The JSON element. + void setConfigKea4(isc::data::ConstElementPtr elem); + + /// @brief setConfig for kea-dhcp6-server. + /// + /// @param elem The JSON element. + void setConfigKea6(isc::data::ConstElementPtr elem); + + /// @brief setServer common part for kea-dhcp[46]:config. + /// + /// @param xpath The xpath of the server. + /// @param elem The JSON element. + void setServerKeaDhcpCommon(const std::string& xpath, + isc::data::ConstElementPtr elem); + + /// @brief setServer for kea-dhcp4-server:config. + /// + /// @param elem The JSON element. + void setServerKeaDhcp4(isc::data::ConstElementPtr elem); + + /// @brief setServer for kea-dhcp6-server:config. + /// + /// @param elem The JSON element. + void setServerKeaDhcp6(isc::data::ConstElementPtr elem); + + /// @brief setServer for kea-*:logging. + /// + /// @param elem The JSON element. + void setServerKeaLogging(isc::data::ConstElementPtr elem); + + /// @brief The model. + std::string model_; +}; + +}; // end of namespace isc::yang +}; // end of namespace isc + +#endif // ISC_TRANSLATOR_CONFIG_H