--- /dev/null
+// 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 <config.h>
+
+#include <testutils/io_utils.h>
+#include <testutils/user_context_utils.h>
+#include <yang/translator_config.h>
+#include <yang/yang_models.h>
+#include <yang/tests/yang_configs.h>
+#include <yang/tests/json_configs.h>
+#include <boost/algorithm/string.hpp>
+#include <gtest/gtest.h>
+#include <iostream>
+
+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<std::string> left_lines;
+ boost::split(left_lines, left, boost::is_any_of("\n"));
+ std::vector<std::string> 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<string> 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<string> 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
--- /dev/null
+// 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 <yang/translator_config.h>
+#include <yang/adaptor_config.h>
+#include <yang/yang_models.h>
+#include <sstream>
+
+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
--- /dev/null
+// 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 <yang/translator.h>
+#include <yang/translator_control_socket.h>
+#include <yang/translator_database.h>
+#include <yang/translator_class.h>
+#include <yang/translator_shared_network.h>
+#include <yang/translator_logger.h>
+#include <list>
+
+namespace isc {
+namespace yang {
+
+/// Configuration translation between YANG and JSON
+///
+/// JSON syntax for kea-dhcp6-server is:
+/// @code
+/// "Dhcp6": {
+/// "preferred-lifetime": <preferred lifetime>,
+/// "valid-lifetime": <valid lifetime>,
+/// "renew-timer": <renew timer>,
+/// "rebind-timer": <rebind timer>,
+/// "decline-probation-period": <decline probation period>,
+/// "subnet6": [ <list of subnet6> ],
+/// <shared-networks>,
+/// "interfaces-config": {
+/// "interfaces" [ <list of interface names / specs> ],
+/// ...
+/// },
+/// <databases>,
+/// <mac-sources>,
+/// <relay-supplied-options>,
+/// <host-reservation-identifiers>,
+/// <client-classes>,
+/// <option-def>,
+/// <option-data>,
+/// <hooks-libraries>,
+/// <expired-leases-processing>,
+/// <server-id>,
+/// <dhcp4o6-port>,
+/// <control-socket>,
+/// <dhcp-ddns>,
+/// <user-context>,
+/// <comment>
+/// },
+/// "Logging": <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! <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 <expired-leases-processing>
+/// +--rw server-id <server-id>
+/// +--rw dhcp4o6-port? uint16
+/// +--rw control-socket! <control-socket>
+/// +--rw dhcp-ddns <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! <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 <expired-leases-processing>
+/// +--rw dhcp4o6-port? uint16
+/// +--rw control-socket! <control-socket>
+/// +--rw dhcp-ddns <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