--- /dev/null
- useModel("kea-dhcp6-server");
+ // 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 <yang/translator_host.h>
++#include <yang/yang_models.h>
+ #include <yang/tests/sysrepo_setup.h>
+
+ #include <gtest/gtest.h>
+ #include <sstream>
+
+ using namespace std;
+ using namespace isc;
+ using namespace isc::data;
+ using namespace isc::yang;
+ using namespace isc::yang::test;
+
+ namespace {
+
+ /// @brief Translator name.
+ extern char const host_reservations[] = "host reservations";
+
+ /// @brief Test fixture class for @ref TranslatorHosts.
+ class TranslatorHostsTest :
+ public GenericTranslatorTest<host_reservations, TranslatorHosts> {
+ public:
+
+ /// Constructor.
+ TranslatorHostsTest() { }
+
+ /// Destructor (does nothing).
+ virtual ~TranslatorHostsTest() { }
+ };
+
+ // This test verifies that an empty host reservation list can be properly
+ // translated from YANG to JSON.
+ TEST_F(TranslatorHostsTest, getEmpty) {
- useModel("kea-dhcp6-server");
++ useModel(KEA_DHCP6_SERVER);
+
+ // Get the host reservation list and checks it is empty.
+ const string& xpath =
+ "/kea-dhcp6-server:config/subnet6/subnet6[id='111']/reservations";
+ ConstElementPtr hosts;
+ EXPECT_NO_THROW(hosts = t_obj_->getHosts(xpath));
+ ASSERT_TRUE(hosts);
+ ASSERT_EQ(Element::list, hosts->getType());
+ EXPECT_EQ(0, hosts->size());
+ }
+
+ // This test verifies that one host reservation can be properly
+ // translated from YANG to JSON.
+ TEST_F(TranslatorHostsTest, get) {
- useModel("kea-dhcp6-server");
++ useModel(KEA_DHCP6_SERVER);
+
+ // Create the subnet 2001:db8::/48 #111.
+ const string& subnet =
+ "/kea-dhcp6-server:config/subnet6/subnet6[id='111']";
+ S_Val v_subnet(new Val("2001:db8::/48", SR_STRING_T));
+ const string& xsubnet = subnet + "/subnet";
+ EXPECT_NO_THROW(sess_->set_item(xsubnet.c_str(), v_subnet));
+
+ // Create the host reservation for 2001:db8::1.
+ const string& xpath = subnet + "/reservations";
+ ostringstream shost;
+ shost << xpath + "/host[identifier-type='hw-address']"
+ << "[identifier='00:01:02:03:04:05']";
+ const string& xaddr = shost.str() + "/ip-addresses";
+ S_Val s_addr(new Val("2001:db8::1"));
+ EXPECT_NO_THROW(sess_->set_item(xaddr.c_str(), s_addr));
+
+ // Get the host.
+ ConstElementPtr host;
+ EXPECT_NO_THROW(host = t_obj_->getHost(shost.str()));
+ ASSERT_TRUE(host);
+ ElementPtr expected = Element::createMap();
+ ElementPtr addresses = Element::createList();
+ addresses->add(Element::create(string("2001:db8::1")));
+ expected->set("hw-address", Element::create(string("00:01:02:03:04:05")));
+ expected->set("ip-addresses", addresses);
+ EXPECT_TRUE(expected->equals(*host));
+
+ // Get the host reservation list and checks the host reservation is in it.
+ ConstElementPtr hosts;
+ EXPECT_NO_THROW(hosts = t_obj_->getHosts(xpath));
+ ASSERT_TRUE(hosts);
+ ASSERT_EQ(Element::list, hosts->getType());
+ ASSERT_EQ(1, hosts->size());
+ EXPECT_TRUE(host->equals(*hosts->get(0)));
+ }
+
+ // This test verifies that an empty host reservation list can be properly
+ // translated from JSON to YANG.
+ TEST_F(TranslatorHostsTest, setEmpty) {
- useModel("kea-dhcp4-server");
++ useModel(KEA_DHCP6_SERVER);
+
+ // Create the subnet 2001:db8::/48 #111.
+ const string& subnet =
+ "/kea-dhcp6-server:config/subnet6/subnet6[id='111']";
+ S_Val v_subnet(new Val("2001:db8::/48", SR_STRING_T));
+ const string& xsubnet = subnet + "/subnet";
+ EXPECT_NO_THROW(sess_->set_item(xsubnet.c_str(), v_subnet));
+
+ // Set empty list.
+ const string& xpath = subnet + "/reservations";
+ ConstElementPtr hosts = Element::createList();
+ EXPECT_NO_THROW(t_obj_->setHosts(xpath, hosts));
+
+ // Get it back.
+ hosts.reset();
+ EXPECT_NO_THROW(hosts = t_obj_->getHosts(xpath));
+ ASSERT_TRUE(hosts);
+ ASSERT_EQ(Element::list, hosts->getType());
+ EXPECT_EQ(0, hosts->size());
+ }
+
+ // This test verifies that one host reservation can be properly
+ // translated from JSON to YANG.
+ TEST_F(TranslatorHostsTest, set) {
++ useModel(KEA_DHCP4_SERVER);
+
+ // Create the subnet 10.0.0.0/14 #111.
+ const string& subnet =
+ "/kea-dhcp4-server:config/subnet4/subnet4[id='111']";
+ S_Val v_subnet(new Val("10.0.0.0/24", SR_STRING_T));
+ const string& xsubnet = subnet + "/subnet";
+ EXPECT_NO_THROW(sess_->set_item(xsubnet.c_str(), v_subnet));
+
+ // Set one host.
+ const string& xpath = subnet + "/reservations";
+ ElementPtr hosts = Element::createList();
+ ElementPtr host = Element::createMap();
+ host->set("flex-id", Element::create(string("00:ff")));
+ host->set("ip-address", Element::create(string("10.0.0.1")));
+ host->set("hostname", Element::create(string("foo")));
+ hosts->add(host);
+ EXPECT_NO_THROW(t_obj_->setHosts(xpath, hosts));
+
+ // Get it back.
+ hosts.reset();
+ EXPECT_NO_THROW(hosts = t_obj_->getHosts(xpath));
+ ASSERT_TRUE(hosts);
+ ASSERT_EQ(Element::list, hosts->getType());
+ ASSERT_EQ(1, hosts->size());
+ EXPECT_TRUE(host->equals(*hosts->get(0)));
+
+ // Check the tree representation.
+ S_Tree tree;
+ EXPECT_NO_THROW(tree = sess_->get_subtree("/kea-dhcp4-server:config"));
+ ASSERT_TRUE(tree);
+ string expected =
+ "kea-dhcp4-server:config (container)\n"
+ " |\n"
+ " -- subnet4 (container)\n"
+ " |\n"
+ " -- subnet4 (list instance)\n"
+ " |\n"
+ " -- id = 111\n"
+ " |\n"
+ " -- subnet = 10.0.0.0/24\n"
+ " |\n"
+ " -- reservations (container)\n"
+ " |\n"
+ " -- host (list instance)\n"
+ " |\n"
+ " -- identifier-type = flex-id\n"
+ " |\n"
+ " -- identifier = 00:ff\n"
+ " |\n"
+ " -- hostname = foo\n"
+ " |\n"
+ " -- ip-address = 10.0.0.1\n";
+ EXPECT_EQ(expected, tree->to_string(100));
+
+ // Check it validates.
+ EXPECT_NO_THROW(sess_->validate());
+ }
+
+ }; // end of anonymous namespace
--- /dev/null
- if ((model_ == "kea-dhcp4-server") ||
- (model_ == "kea-dhcp6-server")) {
+ // 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_host.h>
+ #include <yang/adaptor.h>
++#include <yang/yang_models.h>
+ #include <sstream>
+
+ using namespace std;
+ using namespace isc::data;
+
+ namespace isc {
+ namespace yang {
+
+ TranslatorHost::TranslatorHost(S_Session session, const string& model)
+ : TranslatorBasic(session),
+ TranslatorOptionData(session, model),
+ TranslatorOptionDataList(session, model),
+ model_(model) {
+ }
+
+ TranslatorHost::~TranslatorHost() {
+ }
+
+ ElementPtr
+ TranslatorHost::getHost(const string& xpath) {
+ try {
- if (model_ == "kea-dhcp4-server") {
++ if ((model_ == KEA_DHCP4_SERVER) ||
++ (model_ == KEA_DHCP6_SERVER)) {
+ return (getHostKea(xpath));
+ }
+ } catch (const sysrepo_exception& ex) {
+ isc_throw(SysrepoError,
+ "sysrepo error getting host reservation at '" << xpath
+ << "': " << ex.what());
+ }
+ isc_throw(NotImplemented,
+ "getHost not implemented for the model: " << model_);
+ }
+
+ ElementPtr
+ TranslatorHost::getHostKea(const string& xpath) {
+ ConstElementPtr id_type = getItem(xpath + "/identifier-type");
+ ConstElementPtr id = getItem(xpath + "/identifier");
+ if (!id_type || !id) {
+ isc_throw(Unexpected, "getHostKea requires both identifier and "
+ "identifier-type");
+ }
+ ElementPtr result = Element::createMap();
+ result->set(id_type->stringValue(), id);
+ ConstElementPtr hostname = getItem(xpath + "/hostname");
+ if (hostname) {
+ result->set("hostname", hostname);
+ }
- if (model_ == "kea-dhcp4-server") {
++ if (model_ == KEA_DHCP4_SERVER) {
+ ConstElementPtr address = getItem(xpath + "/ip-address");
+ if (address) {
+ result->set("ip-address", address);
+ }
+ } else {
+ ConstElementPtr addresses = getItems(xpath + "/ip-addresses");
+ if (addresses && (addresses->size() > 0)) {
+ result->set("ip-addresses", addresses);
+ }
+ ConstElementPtr prefixes = getItems(xpath + "/prefixes");
+ if (prefixes && (prefixes->size() > 0)) {
+ result->set("prefixes", prefixes);
+ }
+ }
+ ConstElementPtr options = getOptionDataList(xpath + "/option-data-list");
+ if (options && (options->size() > 0)) {
+ result->set("option-data", options);
+ }
+ ConstElementPtr classes = getItems(xpath + "/client-classes");
+ if (classes) {
+ result->set("client-classes", classes);
+ }
- if ((model_ == "kea-dhcp4-server") ||
- (model_ == "kea-dhcp6-server")) {
++ if (model_ == KEA_DHCP4_SERVER) {
+ 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);
+ }
+ }
+ ConstElementPtr context = getItem(xpath + "/user-context");
+ if (context) {
+ result->set("user-context", Element::fromJSON(context->stringValue()));
+ }
+ return (result);
+ }
+
+ void
+ TranslatorHost::setHost(const string& xpath, ConstElementPtr elem) {
+ try {
- if (model_ == "kea-dhcp4-server") {
++ if ((model_ == KEA_DHCP4_SERVER) ||
++ (model_ == KEA_DHCP6_SERVER)) {
+ setHostKea(xpath, elem);
+ } else {
+ isc_throw(NotImplemented,
+ "setHost not implemented for the model: " << model_);
+ }
+ } catch (const sysrepo_exception& ex) {
+ isc_throw(SysrepoError,
+ "sysrepo error setting host reservation '" << elem->str()
+ << "' at '" << xpath << "': " << ex.what());
+ }
+ }
+
+ void
+ TranslatorHost::setHostKea(const string& xpath, ConstElementPtr elem) {
+ ConstElementPtr hostname = elem->get("hostname");
+ // Skip identifier and identifier type as they are keys.
+ if (hostname) {
+ setItem(xpath + "/hostname", hostname, SR_STRING_T);
+ }
- if (model_ == "kea-dhcp4-server") {
++ if (model_ == KEA_DHCP4_SERVER) {
+ ConstElementPtr address = elem->get("ip-address");
+ if (address) {
+ setItem(xpath + "/ip-address", address, SR_STRING_T);
+ }
+ } else {
+ ConstElementPtr addresses = elem->get("ip-addresses");
+ if (addresses && (addresses->size() > 0)) {
+ for (ConstElementPtr address : addresses->listValue()) {
+ setItem(xpath + "/ip-addresses", address, SR_STRING_T);
+ }
+ }
+ ConstElementPtr prefixes = elem->get("prefixes");
+ if (prefixes && (prefixes->size() > 0)) {
+ for (ConstElementPtr prefix : prefixes->listValue()) {
+ setItem(xpath + "/prefixes", prefix, SR_STRING_T);
+ }
+ }
+ }
+ ConstElementPtr options = elem->get("option-data");
+ if (options && (options->size() > 0)) {
+ setOptionDataList(xpath + "/option-data-list", options);
+ }
+ ConstElementPtr classes = elem->get("client-classes");
+ if (classes && (classes->size() > 0)) {
+ for (ConstElementPtr cclass : classes->listValue()) {
+ setItem(xpath + "/client-classes", cclass, SR_STRING_T);
+ }
+ }
- if ((model_ == "kea-dhcp4-server") ||
- (model_ == "kea-dhcp6-server")) {
++ if (model_ == KEA_DHCP4_SERVER) {
+ 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);
+ }
+ }
+ ConstElementPtr context = Adaptor::getContext(elem);
+ if (context) {
+ setItem(xpath + "/user-context", Element::create(context->str()),
+ SR_STRING_T);
+ }
+ }
+
+ TranslatorHosts::TranslatorHosts(S_Session session, const string& model)
+ : TranslatorBasic(session),
+ TranslatorOptionData(session, model),
+ TranslatorOptionDataList(session, model),
+ TranslatorHost(session, model),
+ model_(model) {
+ }
+
+ TranslatorHosts::~TranslatorHosts() {
+ }
+
+ ElementPtr
+ TranslatorHosts::getHosts(const string& xpath) {
+ try {
+ ElementPtr result = Element::createList();
+ S_Iter_Value iter = getIter(xpath + "/*");
+ if (!iter) {
+ // Can't happen.
+ isc_throw(Unexpected, "getHosts can't get iterator: " << xpath);
+ }
+ for (;;) {
+ const string& host = getNext(iter);
+ if (host.empty()) {
+ break;
+ }
+ result->add(getHost(host));
+ }
+ return (result);
+ } catch (const sysrepo_exception& ex) {
+ isc_throw(SysrepoError,
+ "sysrepo error getting host reservations at '" << xpath
+ << "': " << ex.what());
+ }
+ }
+
+ void
+ TranslatorHosts::setHosts(const string& xpath, ConstElementPtr elem) {
+ try {
- if (model_ == "kea-dhcp4-server") {
++ if ((model_ == KEA_DHCP4_SERVER) ||
++ (model_ == KEA_DHCP6_SERVER)) {
+ setHostsKea(xpath, elem);
+ } else {
+ isc_throw(NotImplemented,
+ "setHosts not implemented for the model: " << model_);
+ }
+ } catch (const sysrepo_exception& ex) {
+ isc_throw(SysrepoError,
+ "sysrepo error setting host reservations '" << elem->str()
+ << "' at '" << xpath << "': " << ex.what());
+ }
+ }
+
+ void
+ TranslatorHosts::setHostsKea(const string& xpath, ConstElementPtr elem) {
+ for (size_t i = 0; i < elem->size(); ++i) {
+ string id_type = "unknown";
+ ConstElementPtr host = elem->get(i);
+ ConstElementPtr id = host->get("hw-address");
+ if (id) {
+ id_type = "hw-address";
+ goto found;
+ }
+ id = host->get("duid");
+ if (id) {
+ id_type = "duid";
+ goto found;
+ }
++ if (model_ == KEA_DHCP4_SERVER) {
+ id = host->get("circuit-id");
+ if (id) {
+ id_type = "circuit-id";
+ goto found;
+ }
+ id = host->get("client-id");
+ if (id) {
+ id_type = "client-id";
+ goto found;
+ }
+ }
+ id = host->get("flex-id");
+ if (id) {
+ id_type = "flex-id";
+ goto found;
+ }
+
+ found:
+ if (id_type == "unknown") {
+ isc_throw(BadValue, "getHosts: can't find the identifier type in "
+ << host->str());
+ }
+ ostringstream key;
+ key << xpath << "/host[identifier-type='" << id_type
+ << "'][identifier='" << id->stringValue() << "']";
+ setHost(key.str(), host);
+ }
+ }
+
+ }; // end of namespace isc::yang
+ }; // end of namespace isc