]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[65-libyang-host] Brought host code from kea-yang
authorFrancis Dupont <fdupont@isc.org>
Tue, 25 Sep 2018 17:17:03 +0000 (19:17 +0200)
committerFrancis Dupont <fdupont@isc.org>
Wed, 26 Sep 2018 16:20:26 +0000 (12:20 -0400)
src/lib/yang/Makefile.am
src/lib/yang/tests/Makefile.am
src/lib/yang/tests/translator_host_unittests.cc [new file with mode: 0644]
src/lib/yang/translator_host.cc [new file with mode: 0644]
src/lib/yang/translator_host.h [new file with mode: 0644]

index 4761a7127d49bbb666a321eba6f92a21981fab43..e22d38855bbdf07dee33ac357ca7ded462022b78 100644 (file)
@@ -10,6 +10,7 @@ libkea_yang_la_SOURCES += sysrepo_error.h
 libkea_yang_la_SOURCES += translator.cc translator.h
 libkea_yang_la_SOURCES += translator_option_data.cc
 libkea_yang_la_SOURCES += translator_option_data.h
+libkea_yang_la_SOURCES += translator_host.cc translator_host.h
 
 libkea_yang_la_LIBADD =  $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
 libkea_yang_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la
@@ -27,6 +28,7 @@ libkea_yang_include_HEADERS = \
        adaptor.h \
        sysrepo_error.h \
        translator.h \
+       translator_host.h \
        translator_option_data.h
 
 EXTRA_DIST = yang.dox
index 5e593f1d191f119c3a178e5367ded58d427e43e4..7c3e5cd1cd36f66886a802173b8c9fc411203c84 100644 (file)
@@ -21,6 +21,7 @@ run_unittests_SOURCES  = adaptor_unittests.cc
 run_unittests_SOURCES += sysrepo_setup.h
 run_unittests_SOURCES += translator_unittests.cc
 run_unittests_SOURCES += translator_option_data_unittests.cc
+run_unittests_SOURCES += translator_host_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/translator_host_unittests.cc b/src/lib/yang/tests/translator_host_unittests.cc
new file mode 100644 (file)
index 0000000..5adce13
--- /dev/null
@@ -0,0 +1,181 @@
+// 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/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");
+
+    // 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");
+
+    // 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-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
diff --git a/src/lib/yang/translator_host.cc b/src/lib/yang/translator_host.cc
new file mode 100644 (file)
index 0000000..e13366b
--- /dev/null
@@ -0,0 +1,273 @@
+// 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 <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") ||
+            (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") {
+        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") {
+        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") ||
+            (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") {
+        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") {
+        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") ||
+            (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
diff --git a/src/lib/yang/translator_host.h b/src/lib/yang/translator_host.h
new file mode 100644 (file)
index 0000000..baf85d4
--- /dev/null
@@ -0,0 +1,198 @@
+// 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_HOST_H
+#define ISC_TRANSLATOR_HOST_H 1
+
+#include <yang/translator_option_data.h>
+#include <list>
+
+namespace isc {
+namespace yang {
+
+// Host reservation translation between YANG and JSON
+//
+// JSON syntax for kea-dhcp4 is:
+// @code
+// {
+//     "hw-address": <hardware address>,
+//     "duid": <duid>,
+//     "circuit-id": <circuit id>,
+//     "client-id": <client id>,
+//     "flex-id": <flex id>,
+//     "ip-address": <ipv4 reserved address>,
+//     "hostname": <hostname>,
+//     "next-server": "<next server>",
+//     "server-hostname": "<server hostname>",
+//     "boot-file-name": "<boot file name>",
+//     "client-classes": "<client class names>",
+//     "option-data": [ <list of option data> ],
+//     "user-context": { <json map> },
+//     "comment": "<comment>"
+// }
+// @endcode
+//
+// JSON syntax for kea-dhcp6 is:
+// @code
+// {
+//     "hw-address": <hardware address>,
+//     "duid": <duid>,
+//     "flex-id": <flex id>,
+//     "ip-addresses": <ipv6 reserved addresses>,
+//     "prefixes": <ipv6 reserved prefixes>,
+//     "hostname": <hostname>,
+//     "client-classes": "<client class names>",
+//     "option-data": [ <list of option data> ],
+//     "user-context": { <json map> },
+//     "comment": "<comment>"
+// }
+// @endcode
+//
+// YANG syntax for kea-dhcp[46] is with identifier-type and identifier
+// as the list keys:
+// @code
+// +--rw identifier-type          enumeration
+// +--rw identifier               string
+// +--rw hostname?                string
+// +--rw option-data-list         option-data*
+// +--rw client-classes*          string
+// +--rw user-context?            string
+// (DHCPv4 only)
+// +--rw ip-address?              inet:ipv4-address
+// +--rw next-server?             inet:ipv4-address
+// +--rw server-hostname?         string
+// +--rw boot-file-name?          string
+// (DHCPv6 only)
+// +--rw ip-addresses*            inet:ipv6-address
+// +--rw prefixes*                inet:ipv6-prefix
+// @endcode
+//
+// An example in JSON and YANG formats:
+// @code
+// [
+//     {
+//         "flex-id": "00:ff",
+//         "ip-address": "10.0.0.1",
+//         "hostname": "foo"
+//     }
+// ]
+// @endcode
+// @code
+//  /kea-dhcp4-server:config (container)
+//  /kea-dhcp4-server:config/subnet4 (container)
+//  /kea-dhcp4-server:config/subnet4/subnet4[id='111'] (list instance)
+//  /kea-dhcp4-server:config/subnet4/subnet4[id='111']/id = 111
+//  /kea-dhcp4-server:config/subnet4/subnet4[id='111']/subnet = 10.0.0.0/24
+//  /kea-dhcp4-server:config/subnet4/subnet4[id='111']/
+//     reservations (container)
+//  /kea-dhcp4-server:config/subnet4/subnet4[id='111']/reservations/
+//     host[identifier-type='flex-id'][identifier='00:ff'] (list instance)
+//  /kea-dhcp4-server:config/subnet4/subnet4[id='111']/reservations/
+//     host[identifier-type='flex-id'][identifier='00:ff']/
+//     identifier-type = flex-id
+//  /kea-dhcp4-server:config/subnet4/subnet4[id='111']/reservations/
+//     host[identifier-type='flex-id'][identifier='00:ff']/
+//     identifier = 00:ff
+//  /kea-dhcp4-server:config/subnet4/subnet4[id='111']/reservations/
+//     host[identifier-type='flex-id'][identifier='00:ff']/
+//     hostname = foo
+//  /kea-dhcp4-server:config/subnet4/subnet4[id='111']/reservations/
+//     host[identifier-type='flex-id'][identifier='00:ff']/
+//     ip-address = 10.0.0.1
+// @endcode
+
+// @brief A translator class for converting a host reservation between
+// YANG and JSON.
+//
+// Currently supports on kea-dhcp[46]-server, not yet ietf-dhcpv6-server.
+class TranslatorHost : virtual public TranslatorOptionDataList {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// @param session Sysrepo session.
+    /// @param model Model name.
+    TranslatorHost(S_Session session, const std::string& model);
+
+    /// @brief Destructor.
+    virtual ~TranslatorHost();
+
+    /// @brief Get and translate a host reservation from YANG to JSON.
+    ///
+    /// @param xpath The xpath of the host reservation.
+    /// @return JSON representation of the host reservation.
+    /// @throw SysrepoError when sysrepo raises an error.
+    isc::data::ElementPtr getHost(const std::string& xpath);
+
+    /// @brief Translate and set host reservation from JSON to YANG.
+    ///
+    /// @param xpath The xpath of the host reservation.
+    /// @param elem The JSON element.
+    void setHost(const std::string& xpath, isc::data::ConstElementPtr elem);
+
+protected:
+    /// @brief getHost for kea-dhcp[46].
+    ///
+    /// @param xpath The xpath of the host reservation.
+    /// @return JSON representation of the host reservation.
+    isc::data::ElementPtr getHostKea(const std::string& xpath);
+
+    /// @brief setHost for kea-dhcp[46].
+    ///
+    /// @param xpath The xpath of the host reservation.
+    /// @param elem The JSON element.
+    void setHostKea(const std::string& xpath, isc::data::ConstElementPtr elem);
+
+    /// @brief The model.
+    std::string model_;
+};
+
+// @brief A translator class for converting host reservations between
+// YANG and JSON.
+//
+// Currently supports on kea-dhcp[46]-server, not yet ietf-dhcpv6-server.
+//
+class TranslatorHosts : virtual public TranslatorHost {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// @param session Sysrepo session.
+    /// @param model Model name.
+    TranslatorHosts(S_Session session, const std::string& model);
+
+    /// @brief Destructor.
+    virtual ~TranslatorHosts();
+
+    /// @brief Get and translate host reservations from YANG to JSON.
+    ///
+    /// @param xpath The xpath of the host reservation list.
+    /// @throw SysrepoError when sysrepo raises an error.
+    isc::data::ElementPtr getHosts(const std::string& xpath);
+
+    /// @brief Translate and set (address) host reservations from JSON to YANG.
+    ///
+    /// @param xpath The xpath of the host reservation list.
+    /// @param elem The JSON element.
+    void setHosts(const std::string& xpath, isc::data::ConstElementPtr elem);
+
+protected:
+    /// @brief setHosts for kea-dhcp[46].
+    ///
+    /// @param xpath The xpath of the host reservation list.
+    /// @param elem The JSON element.
+    /// @throw BadValue on host reservation without known identifier type.
+    void setHostsKea(const std::string& xpath,
+                     isc::data::ConstElementPtr elem);
+
+    /// @brief The model.
+    std::string model_;
+};
+
+}; // end of namespace isc::yang
+}; // end of namespace isc
+
+#endif // ISC_TRANSLATOR_HOST_H