]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[65-libyang-generic] Imported generic stuff
authorFrancis Dupont <fdupont@isc.org>
Fri, 31 Aug 2018 13:57:13 +0000 (15:57 +0200)
committerFrancis Dupont <fdupont@isc.org>
Fri, 31 Aug 2018 13:57:13 +0000 (15:57 +0200)
12 files changed:
src/lib/yang/Makefile.am [new file with mode: 0644]
src/lib/yang/sysrepo_connection.cc [new file with mode: 0644]
src/lib/yang/sysrepo_connection.h [new file with mode: 0644]
src/lib/yang/sysrepo_error.h [new file with mode: 0644]
src/lib/yang/tests/Makefile.am [new file with mode: 0644]
src/lib/yang/tests/run_unittests.cc [new file with mode: 0644]
src/lib/yang/tests/translator_unittests.cc [new file with mode: 0644]
src/lib/yang/translator.cc [new file with mode: 0644]
src/lib/yang/translator.h [new file with mode: 0644]
src/lib/yang/watcher.cc [new file with mode: 0644]
src/lib/yang/watcher.h [new file with mode: 0644]
src/lib/yang/yang.dox [new file with mode: 0644]

diff --git a/src/lib/yang/Makefile.am b/src/lib/yang/Makefile.am
new file mode 100644 (file)
index 0000000..0e5df09
--- /dev/null
@@ -0,0 +1,33 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(SYSREPO_CPPFLAGS)
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+
+lib_LTLIBRARIES = libkea-yang.la
+libkea_yang_la_SOURCES = sysrepo_error.h
+libkea_yang_la_SOURCES += sysrepo_connection.cc sysrepo_connection.h
+libkea_yang_la_SOURCES += translator.cc translator.h
+libkea_yang_la_SOURCES += watcher.cc watcher.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
+libkea_yang_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la
+libkea_yang_la_LIBADD += $(top_builddir)/src/lib/util/threads/libkea-threads.la
+libkea_yang_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la
+libkea_yang_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+libkea_yang_la_LIBADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS) $(SYSREPO_LIBS)
+
+libkea_yang_la_LDFLAGS = -no-undefined -version-info 0:0:0
+
+# Specify the headers for copying into the installation directory tree.
+libkea_yang_includedir = $(pkgincludedir)/yang
+libkea_yang_include_HEADERS = \
+       sysrepo_connection.h \
+       sysrepo_error.h \
+       translator.h \
+       watcher.h
+
+EXTRA_DIST = yang.dox
+
+CLEANFILES = *.gcno *.gcda
diff --git a/src/lib/yang/sysrepo_connection.cc b/src/lib/yang/sysrepo_connection.cc
new file mode 100644 (file)
index 0000000..e2e859b
--- /dev/null
@@ -0,0 +1,44 @@
+// 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/sysrepo_connection.h>
+
+namespace isc {
+namespace yang {
+
+SysrepoConnection::SysrepoConnection() {
+}
+
+SysrepoConnection::~SysrepoConnection() {
+    if (session_) {
+      session_->discard_changes();
+      session_->unlock_datastore();
+      session_->session_stop();
+
+      // how to call disconnect?
+    }
+}
+
+void
+SysrepoConnection::connect() {
+
+    // Get a connection.
+    S_Connection conn(new Connection("kea-netconf"));
+    // Get a session.
+    session_.reset(new Session(conn, SR_DS_CANDIDATE));
+    // Get a from yang object.
+}
+
+void
+SysrepoConnection::commit() {
+  if (!session_) {
+    isc_throw(SysrepoConnectionError, "session not established");
+  }
+  session_->commit();
+}
+
+}
+}
diff --git a/src/lib/yang/sysrepo_connection.h b/src/lib/yang/sysrepo_connection.h
new file mode 100644 (file)
index 0000000..10dced1
--- /dev/null
@@ -0,0 +1,37 @@
+// 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 NETCONF_CONNECTION_H
+#define NETCONF_CONNECTION_H
+
+#include <exceptions/exceptions.h>
+#include <sysrepo-cpp/Session.h>
+
+namespace isc {
+namespace yang {
+
+class SysrepoConnectionError : public Exception {
+public:
+    SysrepoConnectionError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+class SysrepoConnection {
+public:
+    SysrepoConnection();
+    virtual ~SysrepoConnection();
+    void connect();
+
+    void commit();
+
+ private:
+    S_Session session_;
+};
+
+}  // sysrepo
+}  // isc
+
+#endif /* NETCONF_CONNECTION_H_ */
diff --git a/src/lib/yang/sysrepo_error.h b/src/lib/yang/sysrepo_error.h
new file mode 100644 (file)
index 0000000..65be9fc
--- /dev/null
@@ -0,0 +1,26 @@
+// 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_SYSREPO_ERROR_H
+#define ISC_SYSREPO_ERROR_H 1
+
+#include <exceptions/exceptions.h>
+
+namespace isc {
+namespace yang {
+
+/// @brief Sysrepo error.
+class SysrepoError : public isc::Exception {
+public:
+    SysrepoError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what)
+    {}
+};
+
+}; // end of namespace isc::yang
+}; // end of namespace isc
+
+#endif // ISC_SYSREPO_ERROR_H
diff --git a/src/lib/yang/tests/Makefile.am b/src/lib/yang/tests/Makefile.am
new file mode 100644 (file)
index 0000000..6e8ddef
--- /dev/null
@@ -0,0 +1,38 @@
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(SYSREPO_CPPFLAGS)
+AM_CPPFLAGS += -DCFG_EXAMPLES=\"$(abs_top_srcdir)/doc/examples\"
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS_ENVIRONMENT = \
+       $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES  = translator_unittests.cc
+run_unittests_SOURCES += run_unittests.cc
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+
+run_unittests_LDADD =  $(top_builddir)/src/lib/yang/testutils/libyangtest.la
+run_unittests_LDADD += $(top_builddir)/src/lib/yang/libkea-yang.la
+run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libkea-testutils.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
+run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/threads/libkea-threads.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+run_unittests_LDADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS)
+run_unittests_LDADD += $(SYSREPO_LIBS) $(GTEST_LDADD)
+
+endif
+
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/yang/tests/run_unittests.cc b/src/lib/yang/tests/run_unittests.cc
new file mode 100644 (file)
index 0000000..c0847ca
--- /dev/null
@@ -0,0 +1,20 @@
+// 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 <gtest/gtest.h>
+#include <util/unittests/run_all.h>
+#include <log/logger_support.h>
+
+int
+main(int argc, char* argv[]) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    isc::log::initLogger();
+
+    return (isc::util::unittests::run_all());
+}
diff --git a/src/lib/yang/tests/translator_unittests.cc b/src/lib/yang/tests/translator_unittests.cc
new file mode 100644 (file)
index 0000000..49ab57d
--- /dev/null
@@ -0,0 +1,681 @@
+// 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.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 constructor.
+TEST(TranslatorBasicTest, constructor) {
+    // Get a connection.
+    S_Connection conn(new Connection("translator unittests"));
+    // Get a session.
+    S_Session sess(new Session(conn, SR_DS_CANDIDATE));
+    // Get a translator object.
+    boost::scoped_ptr<TranslatorBasic> t_obj;
+    EXPECT_NO_THROW(t_obj.reset(new TranslatorBasic(sess)));
+}
+
+// Test basic yang value to JSON using the static method.
+TEST(TranslatorBasicTest, valueFrom) {
+    S_Val s_val;
+    ConstElementPtr elem;
+
+    // Null.
+    EXPECT_THROW(TranslatorBasic::value(s_val), BadValue);
+
+    // No easy and direct way to build a container or a list...
+
+    // String.
+    string str("foo");
+    s_val.reset(new Val(str.c_str(), SR_STRING_T));
+    EXPECT_NO_THROW(elem = TranslatorBasic::value(s_val));
+    ASSERT_TRUE(elem);
+    ASSERT_EQ(Element::string, elem->getType());
+    EXPECT_EQ(str, elem->stringValue());
+    elem.reset();
+
+    // Bool.
+    s_val.reset(new Val(false, SR_BOOL_T));
+    EXPECT_NO_THROW(elem = TranslatorBasic::value(s_val));
+    ASSERT_TRUE(elem);
+    ASSERT_EQ(Element::boolean, elem->getType());
+    EXPECT_FALSE(elem->boolValue());
+    elem.reset();
+
+    // Unsigned 8 bit integer.
+    uint8_t u8(123);
+    s_val.reset(new Val(u8, SR_UINT8_T));
+    EXPECT_NO_THROW(elem = TranslatorBasic::value(s_val));
+    ASSERT_TRUE(elem);
+    ASSERT_EQ(Element::integer, elem->getType());
+    EXPECT_EQ(u8, elem->intValue());
+    elem.reset();
+
+    // Unsigned 16 bit integer.
+    uint16_t u16(12345);
+    s_val.reset(new Val(u16, SR_UINT16_T));
+    EXPECT_NO_THROW(elem = TranslatorBasic::value(s_val));
+    ASSERT_TRUE(elem);
+    ASSERT_EQ(Element::integer, elem->getType());
+    EXPECT_EQ(u16, elem->intValue());
+    elem.reset();
+
+    // Unsigned 32 bit integer.
+    uint32_t u32(123456789);
+    s_val.reset(new Val(u32, SR_UINT32_T));
+    EXPECT_NO_THROW(elem = TranslatorBasic::value(s_val));
+    ASSERT_TRUE(elem);
+    ASSERT_EQ(Element::integer, elem->getType());
+    EXPECT_EQ(u32, elem->intValue());
+    elem.reset();
+
+    // Signed 8 bit integer.
+    int8_t s8(-123);
+    s_val.reset(new Val(s8, SR_INT8_T));
+    EXPECT_NO_THROW(elem = TranslatorBasic::value(s_val));
+    ASSERT_TRUE(elem);
+    ASSERT_EQ(Element::integer, elem->getType());
+    EXPECT_EQ(s8, elem->intValue());
+    elem.reset();
+
+    // Signed 16 bit integer.
+    int16_t s16(-12345);
+    s_val.reset(new Val(s16, SR_INT16_T));
+    EXPECT_NO_THROW(elem = TranslatorBasic::value(s_val));
+    ASSERT_TRUE(elem);
+    ASSERT_EQ(Element::integer, elem->getType());
+    EXPECT_EQ(s16, elem->intValue());
+    elem.reset();
+
+    // Signed 32 bit integer.
+    int32_t s32(-123456789);
+    s_val.reset(new Val(s32, SR_INT32_T));
+    EXPECT_NO_THROW(elem = TranslatorBasic::value(s_val));
+    ASSERT_TRUE(elem);
+    ASSERT_EQ(Element::integer, elem->getType());
+    EXPECT_EQ(s32, elem->intValue());
+    elem.reset();
+
+    // Identity reference.
+    s_val.reset(new Val(str.c_str(), SR_IDENTITYREF_T));
+    EXPECT_NO_THROW(elem = TranslatorBasic::value(s_val));
+    ASSERT_TRUE(elem);
+    ASSERT_EQ(Element::string, elem->getType());
+    EXPECT_EQ(str, elem->stringValue());
+    elem.reset();
+
+    // Enumeration item.
+    s_val.reset(new Val(str.c_str(), SR_ENUM_T));
+    EXPECT_NO_THROW(elem = TranslatorBasic::value(s_val));
+    ASSERT_TRUE(elem);
+    ASSERT_EQ(Element::string, elem->getType());
+    EXPECT_EQ(str, elem->stringValue());
+    elem.reset();
+
+    // Binary.
+    string binary("Zm9vYmFy");
+    s_val.reset(new Val(binary.c_str(), SR_BINARY_T));
+    EXPECT_NO_THROW(elem = TranslatorBasic::value(s_val));
+    ASSERT_TRUE(elem);
+    ASSERT_EQ(Element::string, elem->getType());
+    EXPECT_EQ("foobar", elem->stringValue());
+    elem.reset();
+
+    // Unknown / unsupported.
+    double d64(.1234);
+    s_val.reset(new Val(d64));
+    EXPECT_THROW(TranslatorBasic::value(s_val), NotImplemented);
+}
+
+// Test basic yang value to JSON using sysrepo test models.
+TEST(TranslatorBasicTest, getItem) {
+    // Get a translator object to play with.
+    S_Connection conn(new Connection("translator unittests"));
+    S_Session sess(new Session(conn, SR_DS_CANDIDATE));
+    boost::scoped_ptr<TranslatorBasic> t_obj;
+    ASSERT_NO_THROW(t_obj.reset(new TranslatorBasic(sess)));
+
+    // Container.
+    string xpath = "/example-module:container/list";
+    S_Val s_val;
+    EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
+    ConstElementPtr elem;
+    EXPECT_NO_THROW(elem = t_obj->getItem("/example-module:container"));
+    EXPECT_FALSE(elem);
+    elem.reset();
+
+    // List.
+    EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
+    ASSERT_TRUE(elem);
+    ASSERT_EQ(Element::list, elem->getType());
+    EXPECT_EQ(0, elem->size());
+    elem.reset();
+
+    // String.
+    xpath = "/test-module:main/string";
+    s_val.reset(new Val("str", SR_STRING_T));
+    EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
+    EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
+    ASSERT_TRUE(elem);
+    ASSERT_EQ(Element::string, elem->getType());
+    EXPECT_EQ("str", elem->stringValue());
+    elem.reset();
+
+    // Bool.
+    xpath = "/test-module:main/boolean";
+    s_val.reset(new Val(true, SR_BOOL_T));
+    EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
+    EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
+    ASSERT_TRUE(elem);
+    ASSERT_EQ(Element::boolean, elem->getType());
+    EXPECT_TRUE(elem->boolValue());
+    elem.reset();
+
+    // Unsigned 8 bit integer.
+    xpath = "/test-module:main/ui8";
+    uint8_t u8(8);
+    s_val.reset(new Val(u8, SR_UINT8_T));
+    EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
+    EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
+    ASSERT_TRUE(elem);
+    ASSERT_EQ(Element::integer, elem->getType());
+    EXPECT_EQ(8, elem->intValue());
+    elem.reset();
+
+    // Unsigned 16 bit integer.
+    xpath = "/test-module:main/ui16";
+    uint16_t u16(16);
+    s_val.reset(new Val(u16, SR_UINT16_T));
+    EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
+    EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
+    ASSERT_TRUE(elem);
+    ASSERT_EQ(Element::integer, elem->getType());
+    EXPECT_EQ(16, elem->intValue());
+    elem.reset();
+
+    // Unsigned 32 bit integer.
+    xpath = "/test-module:main/ui32";
+    uint32_t u32(32);
+    s_val.reset(new Val(u32, SR_UINT32_T));
+    EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
+    EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
+    ASSERT_TRUE(elem);
+    ASSERT_EQ(Element::integer, elem->getType());
+    EXPECT_EQ(32, elem->intValue());
+    elem.reset();
+
+    // Signed 8 bit integer.
+    xpath = "/test-module:main/i8";
+    int8_t s8(8);
+    s_val.reset(new Val(s8, SR_INT8_T));
+    EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
+    EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
+    ASSERT_TRUE(elem);
+    ASSERT_EQ(Element::integer, elem->getType());
+    EXPECT_EQ(8, elem->intValue());
+    elem.reset();
+
+    // Signed 16 bit integer.
+    xpath = "/test-module:main/i16";
+    int16_t s16(16);
+    s_val.reset(new Val(s16, SR_INT16_T));
+    EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
+    EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
+    ASSERT_TRUE(elem);
+    ASSERT_EQ(Element::integer, elem->getType());
+    EXPECT_EQ(16, elem->intValue());
+    elem.reset();
+
+    // Signed 32 bit integer.
+    xpath = "/test-module:main/i32";
+    int32_t s32(32);
+    s_val.reset(new Val(s32, SR_INT32_T));
+    EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
+    EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
+    ASSERT_TRUE(elem);
+    ASSERT_EQ(Element::integer, elem->getType());
+    EXPECT_EQ(32, elem->intValue());
+    elem.reset();
+
+    // Identity reference.
+    xpath = "/test-module:main/id_ref";
+    s_val.reset(new Val("id_1", SR_IDENTITYREF_T));
+    EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
+    EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
+    ASSERT_TRUE(elem);
+    ASSERT_EQ(Element::string, elem->getType());
+    EXPECT_EQ("id_1", elem->stringValue());
+    elem.reset();
+
+    // Enumeration item.
+    xpath = "/test-module:main/enum";
+    s_val.reset(new Val("maybe", SR_ENUM_T));
+    EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
+    EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
+    ASSERT_TRUE(elem);
+    ASSERT_EQ(Element::string, elem->getType());
+    EXPECT_EQ("maybe", elem->stringValue());
+    elem.reset();
+
+    // Binary.
+    xpath = "/test-module:main/raw";
+    s_val.reset(new Val("Zm9vYmFy", SR_BINARY_T));
+    EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
+    EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
+    ASSERT_TRUE(elem);
+    ASSERT_EQ(Element::string, elem->getType());
+    EXPECT_EQ("foobar", elem->stringValue());
+    elem.reset();
+
+    // Leaf-list: not yet exist.
+    xpath = "/test-module:main/numbers";
+    EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
+    EXPECT_FALSE(elem);
+    elem.reset();
+
+    // No easy way to create it empty.
+
+    // Leaf-list: 1, 2 and 3.
+    u8 = 1;
+    s_val.reset(new Val(u8, SR_UINT8_T));
+    EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
+    u8 = 2;
+    s_val.reset(new Val(u8, SR_UINT8_T));
+    EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
+    u8 = 3;
+    s_val.reset(new Val(u8, SR_UINT8_T));
+    EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
+    EXPECT_NO_THROW(elem = t_obj->getItems(xpath));
+    ASSERT_TRUE(elem);
+    ASSERT_EQ(Element::list, elem->getType());
+    EXPECT_EQ(3, elem->size());
+    EXPECT_EQ("[ 1, 2, 3 ]", elem->str());
+    elem.reset();
+
+    // Unknown / unsupported.
+    xpath = "/test-module:main/dec64";
+    s_val.reset(new Val(9.85));
+    EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
+    EXPECT_THROW(elem = t_obj->getItem(xpath), NotImplemented);
+    elem.reset();
+
+    // Not found.
+    xpath = "/test-module:main/no_such_string";
+    EXPECT_NO_THROW(sess->delete_item(xpath.c_str()));
+    EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
+    EXPECT_FALSE(elem);
+    elem.reset();
+
+    // Check error.
+    xpath = "null";
+    try {
+        elem = t_obj->getItem(xpath);
+        ADD_FAILURE() << "expected exception";
+    } catch (const SysrepoError& ex) {
+        EXPECT_EQ("sysrepo error getting item at 'null': Invalid argument",
+                  string(ex.what()));
+    } catch (const std::exception& ex) {
+        ADD_FAILURE() << "unexpected exception with: " << ex.what();
+    }
+}
+
+// Test JSON to basic yang value using the static method.
+TEST(TranslatorBasicTest, valueTo) {
+
+    // Null.
+    ConstElementPtr elem;
+    EXPECT_THROW(TranslatorBasic::value(elem, SR_STRING_T), BadValue);
+
+    // Container.
+    elem = Element::createMap();
+    EXPECT_THROW(TranslatorBasic::value(elem, SR_CONTAINER_T), NotImplemented);
+    EXPECT_THROW(TranslatorBasic::value(elem, SR_CONTAINER_PRESENCE_T), NotImplemented);
+
+    // List.
+    elem = Element::createList();
+    S_Val s_val;
+    EXPECT_NO_THROW(s_val = TranslatorBasic::value(elem, SR_LIST_T));
+    EXPECT_FALSE(s_val);
+    s_val.reset();
+
+    // String.
+    string str("foo");
+    elem = Element::create(str);
+    EXPECT_NO_THROW(s_val = TranslatorBasic::value(elem, SR_STRING_T));
+    ASSERT_TRUE(s_val);
+    ASSERT_EQ(SR_STRING_T, s_val->type());
+    EXPECT_EQ(str, string(s_val->data()->get_string()));
+    s_val.reset();
+
+    // Bool.
+    elem = Element::create(false);
+    EXPECT_NO_THROW(s_val = TranslatorBasic::value(elem, SR_BOOL_T));
+    ASSERT_TRUE(s_val);
+    ASSERT_EQ(SR_BOOL_T, s_val->type());
+    EXPECT_FALSE(s_val->data()->get_bool());
+    s_val.reset();
+
+    // Unsigned 8 bit integer.
+    elem = Element::create(123);
+    EXPECT_NO_THROW(s_val = TranslatorBasic::value(elem, SR_UINT8_T));
+    ASSERT_TRUE(s_val);
+    ASSERT_EQ(SR_UINT8_T, s_val->type());
+    EXPECT_EQ(123, s_val->data()->get_uint8());
+    elem.reset();
+
+    // Unsigned 16 bit integer.
+    elem = Element::create(12345);
+    EXPECT_NO_THROW(s_val = TranslatorBasic::value(elem, SR_UINT16_T));
+    ASSERT_TRUE(s_val);
+    ASSERT_EQ(SR_UINT16_T, s_val->type());
+    EXPECT_EQ(12345, s_val->data()->get_uint16());
+    elem.reset();
+
+    // Unsigned 32 bit integer.
+    elem = Element::create(123456789);
+    EXPECT_NO_THROW(s_val = TranslatorBasic::value(elem, SR_UINT32_T));
+    ASSERT_TRUE(s_val);
+    ASSERT_EQ(SR_UINT32_T, s_val->type());
+    EXPECT_EQ(123456789, s_val->data()->get_uint32());
+    elem.reset();
+
+    // Signed 8 bit integer.
+    elem = Element::create(-123);
+    EXPECT_NO_THROW(s_val = TranslatorBasic::value(elem, SR_INT8_T));
+    ASSERT_TRUE(s_val);
+    ASSERT_EQ(SR_INT8_T, s_val->type());
+    EXPECT_EQ(-123, s_val->data()->get_int8());
+    elem.reset();
+
+    // Signed 16 bit integer.
+    elem = Element::create(-12345);
+    EXPECT_NO_THROW(s_val = TranslatorBasic::value(elem, SR_INT16_T));
+    ASSERT_TRUE(s_val);
+    ASSERT_EQ(SR_INT16_T, s_val->type());
+    EXPECT_EQ(-12345, s_val->data()->get_int16());
+    elem.reset();
+
+    // Signed 32 bit integer.
+    elem = Element::create(-123456789);
+    EXPECT_NO_THROW(s_val = TranslatorBasic::value(elem, SR_INT32_T));
+    ASSERT_TRUE(s_val);
+    ASSERT_EQ(SR_INT32_T, s_val->type());
+    EXPECT_EQ(-123456789, s_val->data()->get_int32());
+    elem.reset();
+
+    // Identity reference.
+    elem = Element::create(str);
+    EXPECT_NO_THROW(s_val = TranslatorBasic::value(elem, SR_IDENTITYREF_T));
+    ASSERT_TRUE(s_val);
+    ASSERT_EQ(SR_IDENTITYREF_T, s_val->type());
+    EXPECT_EQ(str, string(s_val->data()->get_identityref()));
+    s_val.reset();
+
+    // Enumeration item.
+    EXPECT_NO_THROW(s_val = TranslatorBasic::value(elem, SR_ENUM_T));
+    ASSERT_TRUE(s_val);
+    EXPECT_EQ(str, string(s_val->data()->get_enum()));
+    s_val.reset();
+    
+    // Binary.
+    elem = Element::create(string("foobar"));
+    EXPECT_NO_THROW(s_val = TranslatorBasic::value(elem, SR_BINARY_T));
+    ASSERT_TRUE(s_val);
+    EXPECT_EQ("Zm9vYmFy", string(s_val->data()->get_binary()));
+    s_val.reset();
+
+    // Unknown / unsupported.
+    elem = Element::create(.1234);
+    EXPECT_THROW(TranslatorBasic::value(elem, SR_DECIMAL64_T), NotImplemented);
+}
+
+// Test JSON to  basic yang value using sysrepo test models.
+TEST(TranslatorBasicTest, setItem) {
+    // Get a translator object to play with.
+    S_Connection conn(new Connection("translator unittests"));
+    S_Session sess(new Session(conn, SR_DS_CANDIDATE));
+    boost::scoped_ptr<TranslatorBasic> t_obj;
+    ASSERT_NO_THROW(t_obj.reset(new TranslatorBasic(sess)));
+
+    // Container.
+    string xpath = "/example-module:container";
+    ConstElementPtr elem = Element::createMap();
+    EXPECT_THROW(t_obj->setItem(xpath, elem, SR_CONTAINER_T), NotImplemented);
+    EXPECT_THROW(t_obj->setItem(xpath, elem, SR_CONTAINER_PRESENCE_T),
+                 NotImplemented);
+
+    // List.
+    xpath = "/example-module:container/list";
+    elem = Element::createList();
+    EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_LIST_T));
+    S_Val s_val;
+    EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
+    ASSERT_TRUE(s_val);
+    ASSERT_EQ(SR_LIST_T, s_val->type());
+    s_val.reset();
+
+    // String.
+    xpath = "/test-module:main/string";
+    elem = Element::create(string("str"));
+    EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_STRING_T));
+    EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
+    ASSERT_TRUE(s_val);
+    ASSERT_EQ(SR_STRING_T, s_val->type());
+    EXPECT_EQ("str", string(s_val->data()->get_string()));
+    s_val.reset();
+
+    // Bool.
+    xpath = "/test-module:main/boolean";
+    elem = Element::create(true);
+    EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_BOOL_T));
+    EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
+    ASSERT_TRUE(s_val);
+    ASSERT_EQ(SR_BOOL_T, s_val->type());
+    EXPECT_TRUE(s_val->data()->get_bool());
+    s_val.reset();
+
+    // Unsigned 8 bit integer.
+    xpath = "/test-module:main/ui8";
+    elem = Element::create(8);
+    EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_UINT8_T));
+    EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
+    ASSERT_TRUE(s_val);
+    ASSERT_EQ(SR_UINT8_T, s_val->type());
+    EXPECT_EQ(8, s_val->data()->get_uint8());
+    s_val.reset();
+
+    // Unsigned 16 bit integer.
+    xpath = "/test-module:main/ui16";
+    elem = Element::create(16);
+    EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_UINT16_T));
+    EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
+    ASSERT_TRUE(s_val);
+    ASSERT_EQ(SR_UINT16_T, s_val->type());
+    EXPECT_EQ(16, s_val->data()->get_uint16());
+    s_val.reset();
+
+    // Unsigned 32 bit integer.
+    xpath = "/test-module:main/ui32";
+    elem = Element::create(32);
+    EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_UINT32_T));
+    EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
+    ASSERT_TRUE(s_val);
+    ASSERT_EQ(SR_UINT32_T, s_val->type());
+    EXPECT_EQ(32, s_val->data()->get_uint32());
+    s_val.reset();
+
+    // Signed 8 bit integer.
+    xpath = "/test-module:main/i8";
+    elem = Element::create(8);
+    EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_INT8_T));
+    EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
+    ASSERT_TRUE(s_val);
+    ASSERT_EQ(SR_INT8_T, s_val->type());
+    EXPECT_EQ(8, s_val->data()->get_int8());
+    s_val.reset();
+
+    // Signed 16 bit integer.
+    xpath = "/test-module:main/i16";
+    elem = Element::create(16);
+    EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_INT16_T));
+    EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
+    ASSERT_TRUE(s_val);
+    ASSERT_EQ(SR_INT16_T, s_val->type());
+    EXPECT_EQ(16, s_val->data()->get_int16());
+    s_val.reset();
+
+    // Signed 32 bit integer.
+    xpath = "/test-module:main/i32";
+    elem = Element::create(32);
+    EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_INT32_T));
+    EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
+    ASSERT_TRUE(s_val);
+    ASSERT_EQ(SR_INT32_T, s_val->type());
+    EXPECT_EQ(32, s_val->data()->get_int32());
+    s_val.reset();
+
+    // Identity reference.
+    xpath = "/test-module:main/id_ref";
+    elem = Element::create(string("id_1"));
+    EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_IDENTITYREF_T));
+    EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
+    ASSERT_TRUE(s_val);
+    ASSERT_EQ(SR_IDENTITYREF_T, s_val->type());
+    EXPECT_EQ("id_1", string(s_val->data()->get_identityref()));
+    s_val.reset();
+
+    // Enumeration item.
+    xpath = "/test-module:main/enum";
+    elem = Element::create(string("maybe"));
+    EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_ENUM_T));
+    EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
+    ASSERT_TRUE(s_val);
+    ASSERT_EQ(SR_ENUM_T, s_val->type());
+    EXPECT_EQ("maybe", string(s_val->data()->get_enum()));
+    s_val.reset();
+
+    // Binary.
+    xpath = "/test-module:main/raw";
+    elem = Element::create(string("foobar"));
+    EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_BINARY_T));
+    EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
+    ASSERT_TRUE(s_val);
+    ASSERT_EQ(SR_BINARY_T, s_val->type());
+    EXPECT_EQ("Zm9vYmFy", string(s_val->data()->get_binary()));
+    s_val.reset();
+
+    // Leaf-list.
+    xpath = "/test-module:main/numbers";
+    S_Vals s_vals;
+    EXPECT_NO_THROW(s_vals = sess->get_items(xpath.c_str()));
+    EXPECT_FALSE(s_vals);
+    s_vals.reset();
+
+    // Fill it.
+    elem = Element::create(1);
+    EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_UINT8_T));
+    elem = Element::create(2);
+    EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_UINT8_T));
+    elem = Element::create(3);
+    EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_UINT8_T));
+    EXPECT_NO_THROW(s_vals = sess->get_items(xpath.c_str()));
+    ASSERT_TRUE(s_vals);
+    EXPECT_EQ(3, s_vals->val_cnt());
+    s_vals.reset();
+
+    // Clean it.
+    EXPECT_NO_THROW(t_obj->delItem(xpath));
+    EXPECT_NO_THROW(s_vals = sess->get_items(xpath.c_str()));
+    EXPECT_FALSE(s_vals);
+    s_vals.reset();
+
+    // Unknown / unsupported.
+    xpath = "/test-module:main/dec64";
+    elem = Element::create(9.85);
+    EXPECT_THROW(t_obj->setItem(xpath, elem, SR_DECIMAL64_T), NotImplemented);
+
+    // Bad xpath.
+    xpath = "/test-module:main/no_such_string";
+    elem = Element::create(string("str"));
+    try {
+        t_obj->setItem(xpath, elem, SR_STRING_T);
+        ADD_FAILURE() << "expected exception";
+    } catch (const SysrepoError& ex) {
+        string expected = "sysrepo error setting item '\"str\"' at '" +
+            xpath + "': Request contains unknown element";
+        EXPECT_EQ(expected, string(ex.what()));
+    } catch (const std::exception& ex) {
+        ADD_FAILURE() << "unexpected exception with: " << ex.what();
+    }
+
+    // Bad type.
+    xpath = "/test-module:main/string";
+    elem = Element::create(true);
+    try {
+        t_obj->setItem(xpath, elem, SR_BOOL_T);
+        ADD_FAILURE() << "expected exception";
+    } catch (const SysrepoError& ex) {
+        string expected = "sysrepo error setting item 'true' at '" +
+            xpath + "': Invalid argument";
+        EXPECT_EQ(expected, string(ex.what()));
+    } catch (const std::exception& ex) {
+        ADD_FAILURE() << "unexpected exception with: " << ex.what();
+    }
+
+    // Delete (twice).
+    xpath = "/test-module:main/string";
+    EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
+    EXPECT_TRUE(s_val);
+    s_val.reset();
+    EXPECT_NO_THROW(t_obj->delItem(xpath));
+    EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
+    EXPECT_FALSE(s_val);
+    EXPECT_NO_THROW(t_obj->delItem(xpath));
+}
+
+// Test yang list iteration.
+TEST(TranslatorBasicTest, list) {
+    // Get a translator object to play with.
+    S_Connection conn(new Connection("translator unittests"));
+    S_Session sess(new Session(conn, SR_DS_CANDIDATE));
+    boost::scoped_ptr<TranslatorBasic> t_obj;
+    ASSERT_NO_THROW(t_obj.reset(new TranslatorBasic(sess)));
+
+    // Empty list.
+    S_Iter_Value iter;
+    EXPECT_NO_THROW(iter = t_obj->getIter("/example-module:container/list"));
+    ASSERT_TRUE(iter);
+    string xpath;
+    EXPECT_NO_THROW(xpath = t_obj->getNext(iter));
+    EXPECT_TRUE(xpath.empty());
+
+    // Retried with a filled list.
+    xpath = "/example-module:container/list[key1='key1'][key2='key2']/leaf";
+    S_Val s_val(new Val("Leaf value"));
+    EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
+    EXPECT_NO_THROW(iter = t_obj->getIter("/example-module:container/list"));
+    ASSERT_TRUE(iter);
+    EXPECT_NO_THROW(xpath = t_obj->getNext(iter));
+    EXPECT_EQ("/example-module:container/list[key1='key1'][key2='key2']", xpath);
+    EXPECT_NO_THROW(xpath = t_obj->getNext(iter));
+    EXPECT_TRUE(xpath.empty());
+
+    // Not found: same than empty because sr_get_items_iter() translates
+    // SR_ERR_NOT_FOUND by SR_ERR_OK...
+}
+
+}; // end of anonymous namespace
diff --git a/src/lib/yang/translator.cc b/src/lib/yang/translator.cc
new file mode 100644 (file)
index 0000000..6d4199a
--- /dev/null
@@ -0,0 +1,276 @@
+// 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.h>
+#include <util/encode/base64.h>
+#include <cstring>
+
+using namespace std;
+using namespace isc::data;
+using namespace isc::util::encode;
+
+namespace {
+
+string encode64(const string& input) {
+    vector<uint8_t> binary;
+    binary.resize(input.size());
+    memmove(&binary[0], input.c_str(), binary.size());
+    return (encodeBase64(binary));
+}
+
+string decode64(const string& input) {
+    vector<uint8_t> binary;
+    decodeBase64(input, binary);
+    string result;
+    result.resize(binary.size());
+    memmove(&result[0], &binary[0], result.size());
+    return (result);
+}
+
+} // end of anonymous namespace
+
+namespace isc {
+namespace yang {
+
+TranslatorBasic::TranslatorBasic(S_Session session) : session_(session) {
+}
+
+TranslatorBasic::~TranslatorBasic() {
+}
+
+ElementPtr
+TranslatorBasic::value(S_Val s_val) {
+    if (!s_val) {
+        isc_throw(BadValue, "value called with null");
+    }
+    switch (s_val->type()) {
+    case SR_CONTAINER_T:
+    case SR_CONTAINER_PRESENCE_T:
+        // Internal node.
+        return (ElementPtr());
+
+    case SR_LIST_T:
+        return (Element::createList());
+
+    case SR_STRING_T:
+        return (Element::create(string(s_val->data()->get_string())));
+
+    case SR_BOOL_T:
+        return (Element::create(s_val->data()->get_bool() ? true : false));
+
+    case SR_UINT8_T:
+        return (Element::create(static_cast<long long>(s_val->data()->get_uint8())));
+    case SR_UINT16_T:
+        return (Element::create(static_cast<long long>(s_val->data()->get_uint16())));
+
+    case SR_UINT32_T:
+        return (Element::create(static_cast<long long>(s_val->data()->get_uint32())));
+
+    case SR_INT8_T:
+        return (Element::create(static_cast<long long>(s_val->data()->get_int8())));
+
+    case SR_INT16_T:
+        return (Element::create(static_cast<long long>(s_val->data()->get_int16())));
+
+    case SR_INT32_T:
+        return (Element::create(static_cast<long long>(s_val->data()->get_int32())));
+
+    case SR_IDENTITYREF_T:
+        return (Element::create(string(s_val->data()->get_identityref())));
+
+    case SR_ENUM_T:
+        return (Element::create(string(s_val->data()->get_enum())));
+
+    case SR_BINARY_T:
+        return (Element::create(decode64(s_val->data()->get_binary())));
+
+    default:
+        isc_throw(NotImplemented,
+                  "value called with unupported type: " << s_val->type());
+    }
+}
+
+ElementPtr
+TranslatorBasic::getItem(const string& xpath) {
+    S_Val s_val;
+    try {
+        s_val = session_->get_item(xpath.c_str());
+    } catch (const sysrepo_exception& ex) {
+        isc_throw(SysrepoError,
+                  "sysrepo error getting item at '" << xpath
+                  << "': " << ex.what());
+    }
+    if (!s_val) {
+        return (ElementPtr());
+    }
+    return (value(s_val));
+}
+
+ElementPtr
+TranslatorBasic::getItems(const string& xpath) {
+    S_Vals s_vals;
+    try {
+        s_vals = session_->get_items(xpath.c_str());
+        if (!s_vals) {
+            return (ElementPtr());
+        }
+        ElementPtr result = Element::createList();
+        for (size_t i = 0; i < s_vals->val_cnt(); ++i) {
+            S_Val s_val = s_vals->val(i);
+            result->add(value(s_val));
+        }
+        return (result);
+    } catch (const sysrepo_exception& ex) {
+        isc_throw(SysrepoError,
+                  "sysrepo error getting item at '" << xpath
+                  << "': " << ex.what());
+    }
+}
+
+S_Val
+TranslatorBasic::value(ConstElementPtr elem, sr_type_t type) {
+    if (!elem) {
+        isc_throw(BadValue, "value called with null");
+    }
+    S_Val s_val;
+    switch (type) {
+    case SR_CONTAINER_T:
+    case SR_CONTAINER_PRESENCE_T:
+        isc_throw(NotImplemented, "value called for a container");
+
+    case SR_LIST_T:
+        if (elem->getType() != Element::list) {
+            isc_throw(BadValue, "value for a list called with not a list");
+        }
+        // Return null.
+        break;
+
+    case SR_STRING_T:
+    case SR_IDENTITYREF_T:
+    case SR_ENUM_T:
+        if (elem->getType() != Element::string) {
+            isc_throw(BadValue, "value for a string called with not a string");
+        }
+        s_val.reset(new Val(elem->stringValue().c_str(), type));
+        break;
+
+    case SR_BOOL_T:
+        if (elem->getType() != Element::boolean) {
+            isc_throw(BadValue, "value for a boolean called with not a boolean");
+        }
+        s_val.reset(new Val(elem->boolValue(), type));
+        break;
+
+    case SR_UINT8_T:
+        if (elem->getType() != Element::integer) {
+            isc_throw(BadValue, "value for an integer called with not an integer");
+        }
+        s_val.reset(new Val(static_cast<uint8_t>(elem->intValue()), type));
+        break;
+
+    case SR_UINT16_T:
+        if (elem->getType() != Element::integer) {
+            isc_throw(BadValue, "value for an integer called with not an integer");
+        }
+        s_val.reset(new Val(static_cast<uint16_t>(elem->intValue()), type));
+        break;
+
+    case SR_UINT32_T:
+        if (elem->getType() != Element::integer) {
+            isc_throw(BadValue, "value for an integer called with not an integer");
+        }
+        s_val.reset(new Val(static_cast<uint32_t>(elem->intValue()), type));
+        break;
+
+    case SR_INT8_T:
+        if (elem->getType() != Element::integer) {
+            isc_throw(BadValue, "value for an integer called with not an integer");
+        }
+        s_val.reset(new Val(static_cast<int8_t>(elem->intValue()), type));
+        break;
+
+    case SR_INT16_T:
+        if (elem->getType() != Element::integer) {
+            isc_throw(BadValue, "value for an integer called with not an integer");
+        }
+        s_val.reset(new Val(static_cast<int16_t>(elem->intValue()), type));
+        break;
+
+    case SR_INT32_T:
+        if (elem->getType() != Element::integer) {
+            isc_throw(BadValue, "value for an integer called with not an integer");
+        }
+        s_val.reset(new Val(static_cast<int32_t>(elem->intValue()), type));
+        break;
+
+    case SR_BINARY_T:
+        if (elem->getType() != Element::string) {
+            isc_throw(BadValue, "value for a base64 called with not a string");
+        }
+        s_val.reset(new Val(encode64(elem->stringValue()).c_str(), type));
+        break;
+
+    default:
+        isc_throw(NotImplemented,
+                  "value called with unupported type: " << type);
+    }
+
+    return (s_val);
+}
+
+void
+TranslatorBasic::setItem(const string& xpath, ConstElementPtr elem,
+                         sr_type_t type) {
+    S_Val s_val = value(elem, type);
+    if (!s_val && (type != SR_LIST_T)) {
+        return;
+    }
+    try {
+        session_->set_item(xpath.c_str(), s_val);
+    } catch (const sysrepo_exception& ex) {
+        isc_throw(SysrepoError,
+                  "sysrepo error setting item '" << elem->str()
+                  << "' at '" << xpath << "': " << ex.what());
+    }
+}
+
+void
+TranslatorBasic::delItem(const std::string& xpath) {
+    try {
+        session_->delete_item(xpath.c_str());
+    } catch (const sysrepo_exception& ex) {
+        isc_throw(SysrepoError,
+                  "sysrepo error deleting item at '"
+                  << xpath << "': " << ex.what());
+    }
+}
+
+
+S_Iter_Value
+TranslatorBasic::getIter(const std::string& xpath) {
+    return (session_->get_items_iter(xpath.c_str()));
+}
+
+string
+TranslatorBasic::getNext(S_Iter_Value iter) {
+    if (!iter) {
+        isc_throw(BadValue, "getNext called with null");
+    }
+    S_Val s_val;
+    try {
+        s_val = session_->get_item_next(iter);
+    } catch (const sysrepo_exception&) {
+        // Should not happen according to the doc but still happen?
+        return ("");
+    }
+    if (!s_val) {
+        return ("");
+    }
+    return (s_val->xpath());
+}
+
+}; // end of namespace isc::yang
+}; // end of namespace isc
diff --git a/src/lib/yang/translator.h b/src/lib/yang/translator.h
new file mode 100644 (file)
index 0000000..d1c8449
--- /dev/null
@@ -0,0 +1,99 @@
+// 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_H
+#define ISC_TRANSLATOR_H 1
+
+#include <cc/data.h>
+#include <yang/sysrepo_error.h>
+
+#include <sysrepo-cpp/Session.h>
+
+namespace isc {
+namespace yang {
+
+/// @brief Between Yang and JSON translator class for basic values.
+class TranslatorBasic {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// @param session Sysrepo session.
+    TranslatorBasic(S_Session session);
+
+    /// @brief Destructor.
+    virtual ~TranslatorBasic();
+
+    /// @brief Translate basic value from Yang to JSON.
+    ///
+    /// @note Please don't use this outside tests.
+    ///
+    /// @param s_val The value.
+    /// @return The Element representing the sysrepo value.
+    /// @throw NotImplemented when the value type is not supported.
+    static isc::data::ElementPtr value(S_Val s_val);
+
+    /// @brief Get and translate basic value from Yang to JSON.
+    ///
+    /// @note Should be const as it is read only...
+    ///
+    /// @param xpath The xpath of the basic value.
+    /// @return The Element representing the item at xpath or null when not found.
+    /// @throw SysrepoError when sysrepo raises an error.
+    /// @throw NotImplemented when the value type is not supported.
+    isc::data::ElementPtr getItem(const std::string& xpath);
+
+    /// @brief Get and translate a list of basic values from Yang to JSON.
+    ///
+    /// @param xpath The xpath of the list of basic values.
+    /// @return The ListElement representing the leaf-list at xpath or
+    /// null when not found.
+    isc::data::ElementPtr getItems(const std::string& xpath);
+
+    /// @brief Translate basic value from JSON to Yang.
+    ///
+    /// @note Please don't use this outside tests.
+    ///
+    /// @param elem The JSON element.
+    /// @param type The sysrepo type.
+    static S_Val value(isc::data::ConstElementPtr elem, sr_type_t type);
+
+    /// @brief Translate and set basic value from JSON to Yang.
+    ///
+    /// @param xpath The xpath of the basic value.
+    /// @param elem The JSON element.
+    /// @param type The sysrepo type.
+    void setItem(const std::string& xpath, isc::data::ConstElementPtr elem,
+                 sr_type_t type);
+
+    /// @brief Delete basic value from Yang.
+    ///
+    /// @param xpath The xpath of the basic value.
+    void delItem(const std::string& xpath);
+
+    /// List iterator methods keeping the session private.
+
+    /// @brief Get iterator over a Yang list.
+    ///
+    /// @param xpath The xpath of the list.
+    /// @return An S_Iter_Value pointer. Null is the list does not exist.
+    S_Iter_Value getIter(const std::string& xpath);
+
+    /// @brief Get xpath of the next Yang list item.
+    ///
+    /// @param iter The iterator.
+    /// @return The xpath of the next element. Null when at the end of the list.
+    std::string getNext(S_Iter_Value iter);
+
+protected:
+    /// @brief The sysrepo session.
+    S_Session session_;
+};
+
+}; // end of namespace isc::yang
+}; // end of namespace isc
+
+#endif // ISC_TRANSLATOR_H
diff --git a/src/lib/yang/watcher.cc b/src/lib/yang/watcher.cc
new file mode 100644 (file)
index 0000000..e4b816e
--- /dev/null
@@ -0,0 +1,35 @@
+// 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/watcher.h>
+
+using namespace std;
+using namespace isc::data;
+
+namespace isc {
+namespace yang {
+
+Watcher::Watcher(SysrepoConnection &connection, const string &xpath)
+    : xpath_(xpath), netconf_data_(0), connection_(connection) {
+};
+
+Watcher::~Watcher() {
+}
+
+string
+Watcher::getXPath() {
+    return (xpath_);
+}
+
+ElementPtr
+Watcher::getJSON() {
+    return (json_);
+}
+
+}  // namespace netconf
+}  // namespace isc
diff --git a/src/lib/yang/watcher.h b/src/lib/yang/watcher.h
new file mode 100644 (file)
index 0000000..7e86b1e
--- /dev/null
@@ -0,0 +1,59 @@
+// 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_WATCHER_H
+#define ISC_WATCHER_H
+
+#include <config.h>
+
+#include <boost/shared_ptr.hpp>
+#include <cc/data.h>
+#include <yang/sysrepo_connection.h>
+
+namespace isc {
+namespace yang {
+
+/// @brief This represents a base class for all watchers.
+///
+/// Isc_Watcher is an object that receives callback notification
+/// from sysrepo (in YANG format) and converts it to appropriate
+/// JSON that can be sent over control channel and understood by Kea.
+class Watcher {
+public:
+    // Constructor (requires xpath to install a callback)
+    Watcher(SysrepoConnection &connection, const std::string &xpath);
+
+    virtual ~Watcher();
+
+    virtual std::string getXPath();
+
+    // This method will be called when the callback returns.
+    // Need to figure out the type used.
+    void setYangData(void *data);
+
+    // This method translates Netconf data to JSON format
+    // understood by Kea.
+    virtual void translate() = 0;
+
+    // Once setYangData is called,
+    isc::data::ElementPtr getJSON();
+
+protected:
+    std::string xpath_;
+
+    void *netconf_data_;
+
+    isc::data::ElementPtr json_;
+
+    SysrepoConnection &connection_;
+};
+
+typedef boost::shared_ptr<Watcher> WatcherPtr;
+
+}  // namespace isc::yang
+}  // namespace isc
+
+#endif /* ISC_WATCHER_H */
diff --git a/src/lib/yang/yang.dox b/src/lib/yang/yang.dox
new file mode 100644 (file)
index 0000000..10da950
--- /dev/null
@@ -0,0 +1,46 @@
+// 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/.
+
+/**
+ @page libyang libkea-yang - Kea Yang Utilities Library
+
+All translators take a seesion in constructors and derive from
+the basic / base class and recursively of translators for
+embedded parts.
+
+@section yangTranslator between Yang and JSON Translator
+
+@c isc::yang::TranslatorBasic provides some methods:
+ - getItem() gets and translates basic value from Yang to JSON
+ - getItems() gets and translates a leaf-list from Yang to JSON
+   (for a list please use an iterator)
+ - setItem() translates and sets a basic value from JSON to Yang
+ - delItem() deletes a value
+ - getIter() gets an iterator over a Yang list
+ - getNext() returns the xpath of the next item
+
+@section yangTranslatorPool between Yang and JSON Translator for pool
+
+@c isc::yang::TranslatorPool is the standard example of a translator
+for a structured value. Its constructor takes a model name: the code
+implements some variants to accommodate the model with shared code
+moved into a common private routine.
+
+@c isc::yang::TranslatorPools deals with a list of pools. The getPools
+method iterates over thr lidt in both ways. Loot at examples in unit
+tests to understand how can be filled.
+
+Note pools show two shortcomings in IETF models:
+ - option sets make to track changes nearly impossible: the only easy
+  code is to translate the whole configuration.
+ - prefix and start - end forms of pool ranges are both mandatory.
+
+@section yangTranslatorSubnet between Yang and JSON Translator for subnet
+
+The new thing here is the use of adaptors to move timers from subnets
+to pools and back.
+
+*/