]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[65-libyang-database] Filled files, waiting for adaptor merge
authorFrancis Dupont <fdupont@isc.org>
Thu, 13 Sep 2018 13:46:54 +0000 (15:46 +0200)
committerFrancis Dupont <fdupont@isc.org>
Fri, 14 Sep 2018 14:18:05 +0000 (16:18 +0200)
src/lib/yang/Makefile.am
src/lib/yang/tests/Makefile.am
src/lib/yang/tests/translator_database_unittests.cc [new file with mode: 0644]
src/lib/yang/translator_database.cc [new file with mode: 0644]
src/lib/yang/translator_database.h [new file with mode: 0644]

index 01b3a633afbfb8df9ff014a2abcdd6323c8c2823..a9830c3deb9dd19f929184a22492d10dbdd746a5 100644 (file)
@@ -7,6 +7,7 @@ AM_CXXFLAGS = $(KEA_CXXFLAGS)
 lib_LTLIBRARIES = libkea-yang.la
 libkea_yang_la_SOURCES = sysrepo_error.h
 libkea_yang_la_SOURCES += translator.cc translator.h
+libkea_yang_la_SOURCES += translator_database.cc translator_database.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
index f54de8916496de5858c5fadc2a26c0872e7490d0..471f984fb86d80115da4f723d4ffa8cf98dcbf59 100644 (file)
@@ -18,6 +18,7 @@ TESTS =
 if HAVE_GTEST
 TESTS += run_unittests
 run_unittests_SOURCES  = translator_unittests.cc
+run_unittests_SOURCES += translator_database_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_database_unittests.cc b/src/lib/yang/tests/translator_database_unittests.cc
new file mode 100644 (file)
index 0000000..9001d4d
--- /dev/null
@@ -0,0 +1,347 @@
+// 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_database.h>
+
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::data;
+using namespace isc::yang;
+
+namespace {
+
+// Test get empty database using the ad hoc model.
+TEST(TranslatorDatabaseTest, getEmpty) {
+    // Get a translator database object to play with.
+    S_Connection conn(new Connection("translator database access unittests"));
+    S_Session sess(new Session(conn, SR_DS_CANDIDATE));
+    boost::scoped_ptr<TranslatorDatabase> td_obj;
+
+    // Use the ad hoc model.
+    const string& model = "kea-dhcp4";
+    EXPECT_NO_THROW(td_obj.reset(new TranslatorDatabase(sess, model)));
+
+    // Get empty.
+    const string& xpath = "/kea-dhcp4:config/lease-database";
+    ConstElementPtr database;
+    EXPECT_NO_THROW(database = td_obj->getDatabase(xpath));
+    EXPECT_FALSE(database);
+}
+
+// Test get a database using the ad hoc model.
+TEST(TranslatorDatabaseTest, get) {
+    // Get a translator database object to play with.
+    S_Connection conn(new Connection("translator database access unittests"));
+    S_Session sess(new Session(conn, SR_DS_CANDIDATE));
+    boost::scoped_ptr<TranslatorDatabase> td_obj;
+
+    // Use the ad hoc model.
+    const string& model = "kea-dhcp4";
+    EXPECT_NO_THROW(td_obj.reset(new TranslatorDatabase(sess, model)));
+
+    // Set a value.
+    const string& xpath = "/kea-dhcp4:config/lease-database";
+    const string& xtype = xpath + "/database-type";
+    const string& xinterval = xpath + "/lfc-interval";
+    S_Val s_type(new Val("memfile"));
+    EXPECT_NO_THROW(sess->set_item(xtype.c_str(), s_type));
+    uint32_t li = 3600;
+    S_Val s_interval(new Val(li, SR_UINT32_T));
+    EXPECT_NO_THROW(sess->set_item(xinterval.c_str(), s_interval));
+
+    // Get empty.
+    ConstElementPtr database;
+    EXPECT_NO_THROW(database = td_obj->getDatabase(xpath));
+    ASSERT_TRUE(database);
+    EXPECT_EQ(2, database->size());
+    ConstElementPtr type = database->get("type");
+    ASSERT_TRUE(type);
+    ASSERT_EQ(Element::string, type->getType());
+    EXPECT_EQ("memfile", type->stringValue());
+    ConstElementPtr interval = database->get("lfc-interval");
+    ASSERT_TRUE(interval);
+    ASSERT_EQ(Element::integer, interval->getType());
+    EXPECT_EQ(li, interval->intValue());
+}
+
+// Test set a database using the ad hoc model.
+TEST(TranslatorDatabaseTest, set) {
+    // Get a translator database object to play with.
+    S_Connection conn(new Connection("translator database access unittests"));
+    S_Session sess(new Session(conn, SR_DS_CANDIDATE));
+    boost::scoped_ptr<TranslatorDatabase> td_obj;
+
+    // Use the ad hoc model.
+    const string& model = "kea-dhcp4";
+    EXPECT_NO_THROW(td_obj.reset(new TranslatorDatabase(sess, model)));
+
+    // Set a value.
+    const string& xpath = "/kea-dhcp4:config/lease-database";
+    ElementPtr database = Element::createMap();
+    database->set("type", Element::create(string("memfile")));
+    database->set("lfc-interval", Element::create(3600));
+    ASSERT_NO_THROW(td_obj->setDatabase(xpath, database));
+
+    // Get it back.
+    ConstElementPtr got;
+    EXPECT_NO_THROW(got = td_obj->getDatabase(xpath));
+    ASSERT_TRUE(got);
+    ASSERT_EQ(Element::map, got->getType());
+    EXPECT_EQ(2, got->size());
+    ConstElementPtr type = got->get("type");
+    ASSERT_TRUE(type);
+    ASSERT_EQ(Element::string, type->getType());
+    EXPECT_EQ("memfile", type->stringValue());
+    ConstElementPtr interval = database->get("lfc-interval");
+    ASSERT_TRUE(interval);
+    ASSERT_EQ(Element::integer, interval->getType());
+    EXPECT_EQ(3600, interval->intValue());
+}
+
+// Test set empty database using the ad hoc model.
+TEST(TranslatorDatabaseTest, setEmpty) {
+    // Get a translator database object to play with.
+    S_Connection conn(new Connection("translator database access unittests"));
+    S_Session sess(new Session(conn, SR_DS_CANDIDATE));
+    boost::scoped_ptr<TranslatorDatabase> td_obj;
+
+    // Use the ad hoc model.
+    const string& model = "kea-dhcp4";
+    EXPECT_NO_THROW(td_obj.reset(new TranslatorDatabase(sess, model)));
+
+    // Set a value.
+    const string& xpath = "/kea-dhcp4:config/lease-database";
+    const string& xtype = xpath + "/database-type";
+    const string& xinterval = xpath + "/lfc-interval";
+    S_Val s_type(new Val("memfile"));
+    EXPECT_NO_THROW(sess->set_item(xtype.c_str(), s_type));
+    uint32_t li = 3600;
+    S_Val s_interval(new Val(li, SR_UINT32_T));
+    EXPECT_NO_THROW(sess->set_item(xinterval.c_str(), s_interval));
+
+    // Reset to empty.
+    ASSERT_NO_THROW(td_obj->setDatabase(xpath, ConstElementPtr()));
+
+    // Get it back.
+    ConstElementPtr database;
+    EXPECT_NO_THROW(database = td_obj->getDatabase(xpath));
+    EXPECT_FALSE(database);
+}
+
+// Test get empty databases.
+TEST(TranslatorDatabasesTest, getEmpty) {
+    // Get a translator databases object to play with.
+    S_Connection conn(new Connection("translator database accesses unittests"));
+    S_Session sess(new Session(conn, SR_DS_CANDIDATE));
+    boost::scoped_ptr<TranslatorDatabases> tds_obj;
+
+    // Use the ad hoc model.
+    const string& model = "kea-dhcp6";
+    EXPECT_NO_THROW(tds_obj.reset(new TranslatorDatabases(sess, model)));
+
+    // Get empty.
+    const string& xpath = "/kea-dhcp6:config/hosts-databases";
+    ConstElementPtr databases;
+    EXPECT_NO_THROW(databases = tds_obj->getDatabases(xpath));
+    EXPECT_FALSE(databases);
+}
+
+// Test get databases.
+TEST(TranslatorDatabasesTest, get) {
+    // Get a translator databases object to play with.
+    S_Connection conn(new Connection("translator database accesses unittests"));
+    S_Session sess(new Session(conn, SR_DS_CANDIDATE));
+    boost::scoped_ptr<TranslatorDatabases> tds_obj;
+
+    // Use the ad hoc model.
+    const string& model = "kea-dhcp4";
+    EXPECT_NO_THROW(tds_obj.reset(new TranslatorDatabases(sess, model)));
+
+    // Set a value.
+    const string& xpath = "/kea-dhcp4:config/hosts-databases";
+    const string& xdatabase =
+        xpath + "/hosts-database[database-type='mysql']";
+    const string& xname = xdatabase + "/name";
+    const string& xuser = xdatabase + "/user";
+    const string& xpassword = xdatabase + "/password";
+    const string& xhost = xdatabase + "/host";
+    const string& xport = xdatabase + "/port";
+    S_Val s_name(new Val("kea"));
+    EXPECT_NO_THROW(sess->set_item(xname.c_str(), s_name));
+    S_Val s_user(new Val("kea"));
+    EXPECT_NO_THROW(sess->set_item(xuser.c_str(), s_user));
+    S_Val s_password(new Val("kea"));
+    EXPECT_NO_THROW(sess->set_item(xpassword.c_str(), s_password));
+    S_Val s_host(new Val("localhost"));
+    EXPECT_NO_THROW(sess->set_item(xhost.c_str(), s_host));
+    uint16_t mport = 3306;
+    S_Val s_port(new Val(mport, SR_UINT16_T));
+    EXPECT_NO_THROW(sess->set_item(xport.c_str(), s_port));
+
+    // Get empty.
+    ConstElementPtr databases;
+    EXPECT_NO_THROW(databases = tds_obj->getDatabases(xpath));
+    ASSERT_TRUE(databases);
+    ASSERT_EQ(1, databases->size());
+    ConstElementPtr database = databases->get(0);
+    ASSERT_TRUE(database);
+    EXPECT_EQ(6, database->size());
+    ConstElementPtr type = database->get("type");
+    ASSERT_TRUE(type);
+    ASSERT_EQ(Element::string, type->getType());
+    EXPECT_EQ("mysql", type->stringValue());
+    ConstElementPtr name = database->get("name");
+    ASSERT_TRUE(name);
+    ASSERT_EQ(Element::string, name->getType());
+    EXPECT_EQ("kea", name->stringValue());
+    ConstElementPtr user = database->get("user");
+    ASSERT_TRUE(user);
+    ASSERT_EQ(Element::string, user->getType());
+    EXPECT_EQ("kea", user->stringValue());
+    ConstElementPtr password = database->get("password");
+    ASSERT_TRUE(password);
+    ASSERT_EQ(Element::string, password->getType());
+    EXPECT_EQ("kea", password->stringValue());
+    ConstElementPtr host = database->get("host");
+    ASSERT_TRUE(host);
+    ASSERT_EQ(Element::string, host->getType());
+    EXPECT_EQ("localhost", host->stringValue());
+    ConstElementPtr port = database->get("port");
+    ASSERT_TRUE(port);
+    ASSERT_EQ(Element::integer, port->getType());
+    EXPECT_EQ(mport, port->intValue());
+}
+
+// Test set databases.
+TEST(TranslatorDatabasesTest, set) {
+    // Get a translator databases object to play with.
+    S_Connection conn(new Connection("translator database accesses unittests"));
+    S_Session sess(new Session(conn, SR_DS_CANDIDATE));
+    boost::scoped_ptr<TranslatorDatabases> tds_obj;
+
+    // Use the ad hoc model.
+    const string& model = "kea-dhcp6";
+    EXPECT_NO_THROW(tds_obj.reset(new TranslatorDatabases(sess, model)));
+
+    // Set a value.
+    const string& xpath = "/kea-dhcp6:config/hosts-databases";
+    ElementPtr database = Element::createMap();
+    database->set("type", Element::create(string("memfile")));
+    database->set("lfc-interval", Element::create(3600));
+    ElementPtr databases = Element::createList();
+    databases->add(database);
+    ASSERT_NO_THROW(tds_obj->setDatabases(xpath, databases));
+
+    // Get it back.
+    ConstElementPtr gots;
+    EXPECT_NO_THROW(gots = tds_obj->getDatabases(xpath));
+    ASSERT_TRUE(gots);
+    ASSERT_EQ(Element::list, gots->getType());
+    ASSERT_EQ(1, gots->size());
+    ConstElementPtr got = gots->get(0);
+    ASSERT_TRUE(got);
+    ASSERT_EQ(Element::map, got->getType());
+    EXPECT_EQ(2, got->size());
+    ConstElementPtr type = got->get("type");
+    ASSERT_TRUE(type);
+    ASSERT_EQ(Element::string, type->getType());
+    EXPECT_EQ("memfile", type->stringValue());
+    ConstElementPtr interval = database->get("lfc-interval");
+    ASSERT_TRUE(interval);
+    ASSERT_EQ(Element::integer, interval->getType());
+    EXPECT_EQ(3600, interval->intValue());
+}
+
+// Test set empty database.
+TEST(TranslatorDatabasesTest, setEmpty) {
+    // Get a translator databases object to play with.
+    S_Connection conn(new Connection("translator database accesses unittests"));
+    S_Session sess(new Session(conn, SR_DS_CANDIDATE));
+    boost::scoped_ptr<TranslatorDatabases> tds_obj;
+    boost::scoped_ptr<TranslatorDatabase> td_obj;
+
+    // Use the ad hoc model.
+    const string& model = "kea-dhcp4";
+    EXPECT_NO_THROW(tds_obj.reset(new TranslatorDatabases(sess, model)));
+    EXPECT_NO_THROW(td_obj.reset(new TranslatorDatabase(sess, model)));
+
+    // Set a value.
+    const string& xpath = "/kea-dhcp4:config/hosts-databases";
+    const string& xdatabase =
+        xpath + "/hosts-database[database-type='mysql']";
+    const string& xname = xdatabase + "/name";
+    const string& xuser = xdatabase + "/user";
+    const string& xpassword = xdatabase + "/password";
+    const string& xhost = xdatabase + "/host";
+    const string& xport = xdatabase + "/port";
+    S_Val s_name(new Val("kea"));
+    EXPECT_NO_THROW(sess->set_item(xname.c_str(), s_name));
+    S_Val s_user(new Val("kea"));
+    EXPECT_NO_THROW(sess->set_item(xuser.c_str(), s_user));
+    S_Val s_password(new Val("kea"));
+    EXPECT_NO_THROW(sess->set_item(xpassword.c_str(), s_password));
+    S_Val s_host(new Val("localhost"));
+    EXPECT_NO_THROW(sess->set_item(xhost.c_str(), s_host));
+    uint16_t mport = 3306;
+    S_Val s_port(new Val(mport, SR_UINT16_T));
+    EXPECT_NO_THROW(sess->set_item(xport.c_str(), s_port));
+
+    // Reset to empty.
+    ASSERT_NO_THROW(td_obj->setDatabase(xdatabase, ConstElementPtr()));
+
+    // Get empty.
+    ConstElementPtr databases;
+    EXPECT_NO_THROW(databases = tds_obj->getDatabases(xpath));
+    EXPECT_FALSE(databases);
+}
+
+// Test set empty databases.
+TEST(TranslatorDatabasesTest, setEmpties) {
+    // Get a translator databases object to play with.
+    S_Connection conn(new Connection("translator database accesses unittests"));
+    S_Session sess(new Session(conn, SR_DS_CANDIDATE));
+    boost::scoped_ptr<TranslatorDatabases> tds_obj;
+
+    // Use the ad hoc model.
+    const string& model = "kea-dhcp4";
+    EXPECT_NO_THROW(tds_obj.reset(new TranslatorDatabases(sess, model)));
+
+    // Set a value.
+    const string& xpath = "/kea-dhcp4:config/hosts-databases";
+    const string& xdatabase =
+        xpath + "/hosts-database[database-type='mysql']";
+    const string& xname = xdatabase + "/name";
+    const string& xuser = xdatabase + "/user";
+    const string& xpassword = xdatabase + "/password";
+    const string& xhost = xdatabase + "/host";
+    const string& xport = xdatabase + "/port";
+    S_Val s_name(new Val("kea"));
+    EXPECT_NO_THROW(sess->set_item(xname.c_str(), s_name));
+    S_Val s_user(new Val("kea"));
+    EXPECT_NO_THROW(sess->set_item(xuser.c_str(), s_user));
+    S_Val s_password(new Val("kea"));
+    EXPECT_NO_THROW(sess->set_item(xpassword.c_str(), s_password));
+    S_Val s_host(new Val("localhost"));
+    EXPECT_NO_THROW(sess->set_item(xhost.c_str(), s_host));
+    uint16_t mport = 3306;
+    S_Val s_port(new Val(mport, SR_UINT16_T));
+    EXPECT_NO_THROW(sess->set_item(xport.c_str(), s_port));
+
+    // Reset to empty.
+    ASSERT_NO_THROW(tds_obj->setDatabases(xdatabase, ConstElementPtr()));
+
+    // Get empty.
+    ConstElementPtr databases;
+    EXPECT_NO_THROW(databases = tds_obj->getDatabases(xpath));
+    EXPECT_FALSE(databases);
+}
+
+}; // end of anonymous namespace
diff --git a/src/lib/yang/translator_database.cc b/src/lib/yang/translator_database.cc
new file mode 100644 (file)
index 0000000..102c328
--- /dev/null
@@ -0,0 +1,306 @@
+// 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_database.h>
+#include <yang/adaptor.h>
+#include <sstream>
+
+using namespace std;
+using namespace isc::data;
+
+namespace isc {
+namespace yang {
+
+TranslatorDatabase::TranslatorDatabase(S_Session session, const string& model)
+    : TranslatorBasic(session), model_(model) {
+}
+
+TranslatorDatabase::~TranslatorDatabase() {
+}
+
+ElementPtr
+TranslatorDatabase::getDatabase(const string& xpath) {
+    try {
+        if ((model_ == "kea-dhcp4") || (model_ == "kea-dhcp6")) {
+            return (getDatabaseKea(xpath));
+        }
+    } catch (const sysrepo_exception& ex) {
+        isc_throw(SysrepoError,
+                  "sysrepo error getting database access at '" << xpath
+                  << "': " << ex.what());
+    }
+    isc_throw(NotImplemented,
+              "getDatabase not implemented for the model: " << model_);
+}
+
+ElementPtr
+TranslatorDatabase::getDatabaseKea(const string& xpath) {
+    ConstElementPtr type = getItem(xpath + "/database-type");
+    if (!type) {
+        return (ElementPtr());
+    }
+    ElementPtr result = Element::createMap();
+    result->set("type", type);
+    ConstElementPtr user = getItem(xpath + "/user");
+    if (user) {
+        result->set("user", user);
+    }
+    ConstElementPtr password = getItem(xpath + "/password");
+    if (password) {
+        result->set("password", password);
+    }
+    ConstElementPtr host = getItem(xpath + "/host");
+    if (host) {
+        result->set("host", host);
+    }
+    ConstElementPtr name = getItem(xpath + "/name");
+    if (name) {
+        result->set("name", name);
+    }
+    ConstElementPtr persist = getItem(xpath + "/persist");
+    if (persist) {
+        result->set("persist", persist);
+    }
+    ConstElementPtr port = getItem(xpath + "/port");
+    if (port) {
+        result->set("port", port);
+    }
+    ConstElementPtr lfc_interval = getItem(xpath + "/lfc-interval");
+    if (lfc_interval) {
+        result->set("lfc-interval", lfc_interval);
+    }
+    ConstElementPtr readonly = getItem(xpath + "/readonly");
+    if (readonly) {
+        result->set("readonly", readonly);
+    }
+    ConstElementPtr connect_timeout = getItem(xpath + "/connect-timeout");
+    if (connect_timeout) {
+        result->set("connect-timeout", connect_timeout);
+    }
+    ConstElementPtr contact_points = getItem(xpath + "/contact-points");
+    if (contact_points) {
+        result->set("contact-points", contact_points);
+    }
+    ConstElementPtr keyspace = getItem(xpath + "/keyspace");
+    if (keyspace) {
+        result->set("keyspace", keyspace);
+    }
+    ConstElementPtr max_reconnect = getItem(xpath + "/max-reconnect-tries");
+    if (max_reconnect) {
+        result->set("max-reconnect-tries", max_reconnect);
+    }
+    ConstElementPtr reconnect_time = getItem(xpath + "/reconnect-wait-time");
+    if (reconnect_time) {
+        result->set("reconnect-wait-time", reconnect_time);
+    }
+    ConstElementPtr request_timeout = getItem(xpath + "/request-timeout");
+    if (request_timeout) {
+        result->set("request-timeout", request_timeout);
+    }
+    ConstElementPtr keepalive = getItem(xpath + "/tcp-keepalive");
+    if (keepalive) {
+        result->set("tcp-keepalive", keepalive);
+    }
+    ConstElementPtr nodelay = getItem(xpath + "/tcp-nodelay");
+    if (nodelay) {
+        result->set("tcp-nodelay", nodelay);
+    }
+    ConstElementPtr context = getItem(xpath + "/user-context");
+    if (context) {
+        result->set("user-context", Element::fromJSON(context->stringValue()));
+    }
+    return (result);
+}
+
+void
+TranslatorDatabase::setDatabase(const string& xpath,
+                                ConstElementPtr elem,
+                                bool skip) {
+    try {
+        if ((model_ == "kea-dhcp4") || (model_ == "kea-dhcp6")) {
+            setDatabaseKea(xpath, elem, skip);
+        } else {
+            isc_throw(NotImplemented,
+                      "setDatabase not implemented for the model: " << model_);
+        }
+    } catch (const sysrepo_exception& ex) {
+        isc_throw(SysrepoError,
+                  "sysrepo error setting database access '" << elem->str()
+                  << "' at '" << xpath << "': " << ex.what());
+    }
+}
+
+void
+TranslatorDatabase::setDatabaseKea(const string& xpath,
+                                   ConstElementPtr elem,
+                                   bool skip) {
+    if (!elem) {
+        delItem(xpath);
+        return;
+    }
+    if (!skip) {
+        ConstElementPtr type = elem->get("type");
+        if (!type) {
+            isc_throw(BadValue, "setDatabase missing database type");
+        }
+        setItem(xpath + "/database-type", type, SR_STRING_T);
+    }
+    ConstElementPtr user = elem->get("user");
+    if (user) {
+        setItem(xpath + "/user", user, SR_STRING_T);
+    }
+    ConstElementPtr password = elem->get("password");
+    if (password) {
+        setItem(xpath + "/password", password, SR_STRING_T);
+    }
+    ConstElementPtr host = elem->get("host");
+    if (host) {
+        setItem(xpath + "/host", host, SR_STRING_T);
+    }
+    ConstElementPtr name = elem->get("name");
+    if (name) {
+        setItem(xpath + "/name", name, SR_STRING_T);
+    }
+    ConstElementPtr persist = elem->get("persist");
+    if (persist) {
+        setItem(xpath + "/persist", persist, SR_BOOL_T);
+    }
+    ConstElementPtr port = elem->get("port");
+    if (port) {
+        setItem(xpath + "/port", port, SR_UINT16_T);
+    }
+    ConstElementPtr lfc_interval = elem->get("lfc-interval");
+    if (lfc_interval) {
+        setItem(xpath + "/lfc-interval", lfc_interval, SR_UINT32_T);
+    }
+    ConstElementPtr readonly = elem->get("readonly");
+    if (readonly) {
+        setItem(xpath + "/readonly", readonly, SR_BOOL_T);
+    }
+    ConstElementPtr connect_timeout = elem->get("connect-timeout");
+    if (connect_timeout) {
+        setItem(xpath + "/connect-timeout", connect_timeout, SR_UINT32_T);
+    }
+    ConstElementPtr contact_points = elem->get("contact-points");
+    if (contact_points) {
+        setItem(xpath + "/contact-points", contact_points, SR_STRING_T);
+    }
+    ConstElementPtr keyspace = elem->get("keyspace");
+    if (keyspace) {
+        setItem(xpath + "/keyspace", keyspace, SR_STRING_T);
+    }
+    ConstElementPtr max_reconnect = elem->get("max-reconnect-tries");
+    if (max_reconnect) {
+        setItem(xpath + "/max-reconnect-tries", max_reconnect, SR_UINT32_T);
+    }
+    ConstElementPtr reconnect_wait = elem->get("reconnect-wait-time");
+    if (reconnect_wait) {
+        setItem(xpath + "/reconnect-wait-time", reconnect_wait, SR_UINT32_T);
+    }
+    ConstElementPtr request_timeout = elem->get("request-timeout");
+    if (request_timeout) {
+        setItem(xpath + "/request-timeout", request_timeout, SR_UINT32_T);
+    }
+    ConstElementPtr keepalive = elem->get("tcp-keepalive");
+    if (keepalive) {
+        setItem(xpath + "/tcp-keepalive", keepalive, SR_UINT32_T);
+    }
+    ConstElementPtr nodelay = elem->get("tcp-nodelay");
+    if (nodelay) {
+        setItem(xpath + "/tcp-nodelay", nodelay, SR_BOOL_T);
+    }
+    ConstElementPtr context = Adaptor::getContext(elem);
+    if (context) {
+        setItem(xpath + "/user-context", Element::create(context->str()),
+                SR_STRING_T);
+    }
+}
+
+TranslatorDatabases::TranslatorDatabases(S_Session session,
+                                         const string& model)
+    : TranslatorBasic(session),
+      TranslatorDatabase(session, model),
+      model_(model) {
+}
+
+TranslatorDatabases::~TranslatorDatabases() {
+}
+
+ConstElementPtr
+TranslatorDatabases::getDatabases(const string& xpath) {
+    try {
+        if ((model_ == "kea-dhcp4") || (model_ == "kea-dhcp6")) {
+            return (getDatabasesKea(xpath));
+        }
+    } catch (const sysrepo_exception& ex) {
+        isc_throw(SysrepoError,
+                  "sysrepo error getting database accesses at '" << xpath
+                  << "': " << ex.what());
+    }
+    isc_throw(NotImplemented,
+              "getDatabases not implemented for the model: " << model_);
+}
+
+ElementPtr
+TranslatorDatabases::getDatabasesKea(const string& xpath) {
+    S_Iter_Value iter = getIter(xpath + "/*");
+    if (!iter) {
+        return (ElementPtr());
+    }
+    ElementPtr result = Element::createList();
+    for (;;) {
+        const string& database = getNext(iter);
+        if (database.empty()) {
+            break;
+        }
+        result->add(getDatabase(database));
+    }
+    if (result->size() > 0) {
+        return (result);
+    } else {
+        return (ElementPtr());
+    }
+}
+
+void
+TranslatorDatabases::setDatabases(const string& xpath, ConstElementPtr elem) {
+    try {
+        if ((model_ == "kea-dhcp4") || (model_ == "kea-dhcp6")) {
+            setDatabasesKea(xpath, elem);
+        } else {
+            isc_throw(NotImplemented,
+                      "setDatabases not implemented for the model: "
+                      << model_);
+        }
+    } catch (const sysrepo_exception& ex) {
+        isc_throw(SysrepoError,
+                  "sysrepo error setting database accesses '" << elem->str()
+                  << "' at '" << xpath << "': " << ex.what());
+    }
+}
+
+void
+TranslatorDatabases::setDatabasesKea(const string& xpath,
+                                     ConstElementPtr elem) {
+    if (!elem) {
+        delItem(xpath + "/hosts-database");
+        delItem(xpath);
+        return;
+    }
+    for (size_t i = 0; i < elem->size(); ++i) {
+        ConstElementPtr database = elem->get(i);
+        if (!database->contains("type")) {
+            isc_throw(BadValue, "database without type: " << database->str());
+        }
+        string type = database->get("type")->stringValue();
+        ostringstream key;
+        key << xpath << "/hosts-database[database-type='" << type << "']";
+        setDatabase(key.str(), database, true);
+    }
+}
+
+}; // end of namespace isc::yang
+}; // end of namespace isc
diff --git a/src/lib/yang/translator_database.h b/src/lib/yang/translator_database.h
new file mode 100644 (file)
index 0000000..7caaf81
--- /dev/null
@@ -0,0 +1,166 @@
+// 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_DATABASE_H
+#define ISC_TRANSLATOR_DATABASE_H 1
+
+#include <yang/translator.h>
+#include <list>
+
+namespace isc {
+namespace yang {
+
+// @brief Between Yang and JSON translator class for a database access.
+class TranslatorDatabase : virtual public TranslatorBasic {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// @param session Sysrepo session.
+    /// @param model Model name.
+    TranslatorDatabase(S_Session session, const std::string& model);
+
+    /// @brief Destructor.
+    virtual ~TranslatorDatabase();
+
+    /// @brief Get and translate a database access from Yang to JSON.
+    ///
+    /// JSON syntax for all Kea servers with database access is:
+    /// @code
+    /// {
+    ///     "type": <type>, // required
+    ///     "user": <user>,
+    ///     "password": <password>,
+    ///     "host": <host>,
+    ///     "name": <name>,
+    ///     "persist": <persist flag>,
+    ///     "port": <port>,
+    ///     "lfc-interval": <lfc interval>,
+    ///     "readonly": <readonly flag>,
+    ///     "connect-timeout": <connect timeout>,
+    ///     "contact-points": <contact points>,
+    ///     "keyspace": <keyspace>,
+    ///     "max-reconnect-tries": <maximum reconnect tries>,
+    ///     "reconnect-wait-time": <reconnect wait time>,
+    ///     "request-timeout": <request timeout>,
+    ///     "tcp-keepalive": <TCP keepalive>,
+    ///     "tcp-nodelay": <TCP nodelay flag>,
+    ///     "user-context": { <json map> },
+    ///     "comment": <comment>
+    /// }
+    /// @endcode
+    ///
+    /// @param xpath The xpath of the database.
+    /// @return JSON representation of the database.
+    /// @throw SysrepoError when sysrepo raises an error.
+    isc::data::ElementPtr getDatabase(const std::string& xpath);
+
+    /// @brief Translate and set database access from JSON to Yang.
+    ///
+    /// @param xpath The xpath of the database access.
+    /// @param elem The JSON element.
+    /// @param skip The skip type field flag.
+    void setDatabase(const std::string& xpath,
+                     isc::data::ConstElementPtr elem,
+                     bool skip = false);
+
+protected:
+    /// @brief getDatabase JSON for kea-dhcp[46].
+    ///
+    /// @param xpath The xpath of the database.
+    /// @return JSON representation of the database.
+    /// @throw SysrepoError when sysrepo raises an error.
+    isc::data::ElementPtr getDatabaseKea(const std::string& xpath);
+
+    /// @brief setDatabase for kea-dhcp[46].
+    ///
+    /// Yang syntax for kea-dhcp[46] is:
+    /// @code
+    ///  +--rw database                container
+    ///    |
+    ///    +--rw datanase=type?        string
+    ///    +--rw user?                 string
+    ///    +--rw password?             string
+    ///    +--rw host?                 string
+    ///    +--rw name?                 string
+    ///    +--rw persist?              boolean
+    ///    +--rw port?                 uint16
+    ///    +--rw lfc-interval?         uint32
+    ///    +--rw readonly?             boolean
+    ///    +--rw connect-timeout?      uint32
+    ///    +--rw contact-points?       string
+    ///    +--rw keyspace?             string
+    ///    +--rw max-reconnect-tries?  uint32
+    ///    +--rw reconnect-wait-time?  uint32
+    ///    +--rw request-timeout?      uint32
+    ///    +--rw tcp-keepalive?        uint32
+    ///    +--rw tcp-nodelay?          boolean
+    ///    +--rw user-context?         string
+    /// @endcode
+    ///
+    /// @param xpath The xpath of the database access.
+    /// @param elem The JSON element.
+    /// @param skip The skip type field flag.
+    void setDatabaseKea(const std::string& xpath,
+                        isc::data::ConstElementPtr elem,
+                        bool skip);
+
+    /// @brief The model.
+    std::string model_;
+};
+
+// @brief Between Yang and JSON translator class for database accesses.
+class TranslatorDatabases : virtual public TranslatorDatabase {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// @param session Sysrepo session.
+    /// @param model Model name.
+    TranslatorDatabases(S_Session session, const std::string& model);
+
+    /// @brief Destructor.
+    virtual ~TranslatorDatabases();
+
+    /// @brief Get and translate database accesses from Yang to JSON.
+    ///
+    /// @param xpath The xpath of databases.
+    /// @return JSON representation of databases.
+    /// @throw SysrepoError when sysrepo raises an error.
+    isc::data::ConstElementPtr getDatabases(const std::string& xpath);
+
+    /// @brief Translate and set database accesses from JSON to Yang.
+    ///
+    /// @param xpath The xpath of databases.
+    /// @param elem The JSON element.
+    void setDatabases(const std::string& xpath,
+                      isc::data::ConstElementPtr elem);
+
+protected:
+    /// @brief getDatabases JSON for kea-dhcp[46].
+    ///
+    /// @param xpath The xpath of databases.
+    /// @return JSON representation of  databases.
+    /// @throw SysrepoError when sysrepo raises an error.
+    isc::data::ElementPtr getDatabasesKea(const std::string& xpath);
+
+    /// @brief setDatabases for kea-dhcp[46].
+    ///
+    /// Key is the database-type.
+    ///
+    /// @param xpath The xpath of databases.
+    /// @param elem The JSON element.
+    void setDatabasesKea(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_DATABASE_H