From 8051d3497e3fd6fbad7d68e7a4bdb161e35e6607 Mon Sep 17 00:00:00 2001 From: Francis Dupont Date: Fri, 31 Aug 2018 15:57:13 +0200 Subject: [PATCH] [65-libyang-generic] Imported generic stuff --- src/lib/yang/Makefile.am | 33 + src/lib/yang/sysrepo_connection.cc | 44 ++ src/lib/yang/sysrepo_connection.h | 37 ++ src/lib/yang/sysrepo_error.h | 26 + src/lib/yang/tests/Makefile.am | 38 ++ src/lib/yang/tests/run_unittests.cc | 20 + src/lib/yang/tests/translator_unittests.cc | 681 +++++++++++++++++++++ src/lib/yang/translator.cc | 276 +++++++++ src/lib/yang/translator.h | 99 +++ src/lib/yang/watcher.cc | 35 ++ src/lib/yang/watcher.h | 59 ++ src/lib/yang/yang.dox | 46 ++ 12 files changed, 1394 insertions(+) create mode 100644 src/lib/yang/Makefile.am create mode 100644 src/lib/yang/sysrepo_connection.cc create mode 100644 src/lib/yang/sysrepo_connection.h create mode 100644 src/lib/yang/sysrepo_error.h create mode 100644 src/lib/yang/tests/Makefile.am create mode 100644 src/lib/yang/tests/run_unittests.cc create mode 100644 src/lib/yang/tests/translator_unittests.cc create mode 100644 src/lib/yang/translator.cc create mode 100644 src/lib/yang/translator.h create mode 100644 src/lib/yang/watcher.cc create mode 100644 src/lib/yang/watcher.h create mode 100644 src/lib/yang/yang.dox diff --git a/src/lib/yang/Makefile.am b/src/lib/yang/Makefile.am new file mode 100644 index 000000000..0e5df0950 --- /dev/null +++ b/src/lib/yang/Makefile.am @@ -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 index 000000000..e2e859bdf --- /dev/null +++ b/src/lib/yang/sysrepo_connection.cc @@ -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 + +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 index 000000000..10dced10f --- /dev/null +++ b/src/lib/yang/sysrepo_connection.h @@ -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 +#include + +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 index 000000000..65be9fcd5 --- /dev/null +++ b/src/lib/yang/sysrepo_error.h @@ -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 + +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 index 000000000..6e8ddef06 --- /dev/null +++ b/src/lib/yang/tests/Makefile.am @@ -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 index 000000000..c0847cad1 --- /dev/null +++ b/src/lib/yang/tests/run_unittests.cc @@ -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 + +#include +#include +#include + +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 index 000000000..49ab57d65 --- /dev/null +++ b/src/lib/yang/tests/translator_unittests.cc @@ -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 + +#include + +#include +#include + +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 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 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 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 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 index 000000000..6d4199af2 --- /dev/null +++ b/src/lib/yang/translator.cc @@ -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 +#include +#include + +using namespace std; +using namespace isc::data; +using namespace isc::util::encode; + +namespace { + +string encode64(const string& input) { + vector binary; + binary.resize(input.size()); + memmove(&binary[0], input.c_str(), binary.size()); + return (encodeBase64(binary)); +} + +string decode64(const string& input) { + vector 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(s_val->data()->get_uint8()))); + case SR_UINT16_T: + return (Element::create(static_cast(s_val->data()->get_uint16()))); + + case SR_UINT32_T: + return (Element::create(static_cast(s_val->data()->get_uint32()))); + + case SR_INT8_T: + return (Element::create(static_cast(s_val->data()->get_int8()))); + + case SR_INT16_T: + return (Element::create(static_cast(s_val->data()->get_int16()))); + + case SR_INT32_T: + return (Element::create(static_cast(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(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(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(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(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(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(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 index 000000000..d1c8449e9 --- /dev/null +++ b/src/lib/yang/translator.h @@ -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 +#include + +#include + +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 index 000000000..e4b816eae --- /dev/null +++ b/src/lib/yang/watcher.cc @@ -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 + +#include + +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 index 000000000..7e86b1e89 --- /dev/null +++ b/src/lib/yang/watcher.h @@ -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 + +#include +#include +#include + +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 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 index 000000000..10da9504b --- /dev/null +++ b/src/lib/yang/yang.dox @@ -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. + +*/ -- GitLab