diff --git a/src/lib/yang/Makefile.am b/src/lib/yang/Makefile.am index 01b3a633afbfb8df9ff014a2abcdd6323c8c2823..5022b1724d0aee693cd550acd88d6c916ad1fd2f 100644 --- a/src/lib/yang/Makefile.am +++ b/src/lib/yang/Makefile.am @@ -7,6 +7,7 @@ AM_CXXFLAGS = $(KEA_CXXFLAGS) lib_LTLIBRARIES = libkea-yang.la libkea_yang_la_SOURCES = sysrepo_error.h libkea_yang_la_SOURCES += translator.cc translator.h +libkea_yang_la_SOURCES += translator_logger.cc translator_logger.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 diff --git a/src/lib/yang/tests/Makefile.am b/src/lib/yang/tests/Makefile.am index f54de8916496de5858c5fadc2a26c0872e7490d0..22f4d22a6a63f467de54907636581518fc40072d 100644 --- a/src/lib/yang/tests/Makefile.am +++ b/src/lib/yang/tests/Makefile.am @@ -18,6 +18,7 @@ TESTS = if HAVE_GTEST TESTS += run_unittests run_unittests_SOURCES = translator_unittests.cc +run_unittests_SOURCES += translator_logger_unittests.cc run_unittests_SOURCES += run_unittests.cc run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) diff --git a/src/lib/yang/tests/translator_logger_unittests.cc b/src/lib/yang/tests/translator_logger_unittests.cc new file mode 100644 index 0000000000000000000000000000000000000000..44a6bb3eb55e3a636f2665e405c2ddce850656a9 --- /dev/null +++ b/src/lib/yang/tests/translator_logger_unittests.cc @@ -0,0 +1,177 @@ +// 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 get empty loggers. +TEST(TranslatorLoggersTest, getEmpty) { + // Get a translator loggers object to play with. + S_Connection conn(new Connection("translator loggeres unittests")); + S_Session sess(new Session(conn, SR_DS_CANDIDATE)); + boost::scoped_ptr tls_obj; + + // Use the ad hoc model. + const string& model = "kea-dhcp4"; + EXPECT_NO_THROW(tls_obj.reset(new TranslatorLoggers(sess, model))); + + // Get empty. + const string& xpath = "/kea-dhcp4:logging/loggers"; + ConstElementPtr loggers; + EXPECT_NO_THROW(loggers = tls_obj->getLoggers(xpath)); + ASSERT_TRUE(loggers); + EXPECT_EQ(0, loggers->size()); +} + +// Test get loggers. +TEST(TranslatorLoggersTest, get) { + // Get a translator loggers object to play with. + S_Connection conn(new Connection("translator loggeres unittests")); + S_Session sess(new Session(conn, SR_DS_CANDIDATE)); + boost::scoped_ptr tls_obj; + + // Use the ad hoc model. + const string& model = "kea-dhcp6"; + EXPECT_NO_THROW(tls_obj.reset(new TranslatorLoggers(sess, model))); + + // Set a value. + const string& xpath = "/kea-dhcp6:logging/loggers"; + const string& xlogger = xpath + "/logger[name='foo']"; + const string& xseverity = xlogger + "/severity"; + const string& xoption = xlogger + "/output-options/option[output='/bar']"; + const string& xmaxver = xoption + "/maxver"; + S_Val s_severity(new Val("WARN", SR_ENUM_T)); + EXPECT_NO_THROW(sess->set_item(xseverity.c_str(), s_severity)); + uint32_t max_ver = 10; + S_Val s_maxver(new Val(max_ver, SR_UINT32_T)); + EXPECT_NO_THROW(sess->set_item(xmaxver.c_str(), s_maxver)); + + // Get empty. + ConstElementPtr loggers; + EXPECT_NO_THROW(loggers = tls_obj->getLoggers(xpath)); + ASSERT_TRUE(loggers); + ASSERT_EQ(1, loggers->size()); + ConstElementPtr logger = loggers->get(0); + ASSERT_TRUE(logger); + EXPECT_EQ(3, logger->size()); + ConstElementPtr name = logger->get("name"); + ASSERT_TRUE(name); + ASSERT_EQ(Element::string, name->getType()); + EXPECT_EQ("foo", name->stringValue()); + ConstElementPtr severity = logger->get("severity"); + ASSERT_TRUE(severity); + ASSERT_EQ(Element::string, severity->getType()); + EXPECT_EQ("WARN", severity->stringValue()); + ConstElementPtr options = logger->get("output_options"); + ASSERT_TRUE(options); + ASSERT_EQ(1, options->size()); + ConstElementPtr option = options->get(0); + ASSERT_TRUE(option); + EXPECT_EQ(2, option->size()); + ConstElementPtr output = option->get("output"); + ASSERT_TRUE(output); + ASSERT_EQ(Element::string, output->getType()); + EXPECT_EQ("/bar", output->stringValue()); + ConstElementPtr maxver = option->get("maxver"); + ASSERT_TRUE(maxver); + ASSERT_EQ(Element::integer, maxver->getType()); + EXPECT_EQ(max_ver, maxver->intValue()); +} + +// Test set loggers. +TEST(TranslatorLoggersTest, set) { + // Get a translator loggers object to play with. + S_Connection conn(new Connection("translator loggeres unittests")); + S_Session sess(new Session(conn, SR_DS_CANDIDATE)); + boost::scoped_ptr tls_obj; + + // Use the ad hoc model. + const string& model = "kea-dhcp4"; + EXPECT_NO_THROW(tls_obj.reset(new TranslatorLoggers(sess, model))); + + // Set a value. + const string& xpath = "/kea-dhcp4:logging/loggers"; + ElementPtr option = Element::createMap(); + option->set("output", Element::create(string("/bar"))); + option->set("maxver", Element::create(10)); + ElementPtr options = Element::createList(); + options->add(option); + ElementPtr logger = Element::createMap(); + logger->set("name", Element::create(string("foo"))); + logger->set("severity", Element::create(string("WARN"))); + logger->set("output_options", options); + ElementPtr loggers = Element::createList(); + loggers->add(logger); + ASSERT_NO_THROW(tls_obj->setLoggers(xpath, loggers)); + + // Get it back. + ConstElementPtr gots; + EXPECT_NO_THROW(gots = tls_obj->getLoggers(xpath)); + ASSERT_TRUE(gots); + ASSERT_EQ(1, gots->size()); + ConstElementPtr got = gots->get(0); + ASSERT_TRUE(got); + EXPECT_EQ(3, got->size()); + ConstElementPtr name = got->get("name"); + ASSERT_TRUE(name); + ASSERT_EQ(Element::string, name->getType()); + EXPECT_EQ("foo", name->stringValue()); + ConstElementPtr severity = logger->get("severity"); + ASSERT_TRUE(severity); + ASSERT_EQ(Element::string, severity->getType()); + EXPECT_EQ("WARN", severity->stringValue()); + ConstElementPtr got_os = logger->get("output_options"); + ASSERT_TRUE(got_os); + ASSERT_EQ(1, got_os->size()); + ConstElementPtr got_o = got_os->get(0); + ASSERT_TRUE(got_o); + EXPECT_EQ(2, got_o->size()); + ConstElementPtr output = got_o->get("output"); + ASSERT_TRUE(output); + ASSERT_EQ(Element::string, output->getType()); + EXPECT_EQ("/bar", output->stringValue()); + ConstElementPtr maxver = got_o->get("maxver"); + ASSERT_TRUE(maxver); + ASSERT_EQ(Element::integer, maxver->getType()); + EXPECT_EQ(10, maxver->intValue()); + + // Check the tree representation. + S_Tree tree; + EXPECT_NO_THROW(tree = sess->get_subtree("/kea-dhcp4:logging")); + ASSERT_TRUE(tree); + string expected = + "kea-dhcp4:logging (container)\n" + " |\n" + " -- loggers (container)\n" + " |\n" + " -- logger (list instance)\n" + " |\n" + " -- name = foo\n" + " |\n" + " -- output-options (container)\n" + " | |\n" + " | -- option (list instance)\n" + " | |\n" + " | -- output = /bar\n" + " | |\n" + " | -- maxver = 10\n" + " |\n" + " -- severity = WARN\n"; + EXPECT_EQ(expected, tree->to_string(100)); +} + +}; // end of anonymous namespace diff --git a/src/lib/yang/translator_logger.cc b/src/lib/yang/translator_logger.cc new file mode 100644 index 0000000000000000000000000000000000000000..496e442ceeb72facf756556d9a589ddb90ce2f97 --- /dev/null +++ b/src/lib/yang/translator_logger.cc @@ -0,0 +1,259 @@ +// 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; + +namespace isc { +namespace yang { + +TranslatorLogger::TranslatorLogger(S_Session session, const string& model) + : TranslatorBasic(session), model_(model) { +} + +TranslatorLogger::~TranslatorLogger() { +} + +ElementPtr +TranslatorLogger::getLogger(const string& xpath) { + try { + if ((model_ == "kea-dhcp4") || (model_ == "kea-dhcp6") || + (model_ == "kea-dhcpddns") || (model_ == "kea-control-agent")) { + return (getLoggerKea(xpath)); + } + } catch (const sysrepo_exception& ex) { + isc_throw(SysrepoError, + "sysrepo error getting logger at '" << xpath + << "': " << ex.what()); + } + isc_throw(NotImplemented, + "getLogger not implemented for the model: " << model_); +} + +ElementPtr +TranslatorLogger::getLoggerKea(const string& xpath) { + ConstElementPtr name = getItem(xpath + "/name"); + if (!name) { + return (ElementPtr()); + } + ElementPtr result = Element::createMap(); + result->set("name", name); + ConstElementPtr options = getOutputOptions(xpath + "/output-options"); + if (options && (options->size() > 0)) { + result->set("output_options", options); + } + ConstElementPtr severity = getItem(xpath + "/severity"); + if (severity) { + result->set("severity", severity); + } + ConstElementPtr debuglevel = getItem(xpath + "/debuglevel"); + if (debuglevel) { + result->set("debuglevel", debuglevel); + } + ConstElementPtr context = getItem(xpath + "/user-context"); + if (context) { + result->set("user-context", Element::fromJSON(context->stringValue())); + } + return (result); +} + +ElementPtr +TranslatorLogger::getOutputOption(const string& xpath) { + ConstElementPtr output = getItem(xpath + "/output"); + if (!output) { + return (ElementPtr()); + } + ElementPtr result = Element::createMap(); + result->set("output", output); + ConstElementPtr maxver = getItem(xpath + "/maxver"); + if (maxver) { + result->set("maxver", maxver); + } + ConstElementPtr maxsize = getItem(xpath + "/maxsize"); + if (maxsize) { + result->set("maxsize", maxsize); + } + ConstElementPtr flush = getItem(xpath + "/flush"); + if (flush) { + result->set("flush", flush); + } + return (result); +} + +ElementPtr +TranslatorLogger::getOutputOptions(const string& xpath) { + S_Iter_Value iter = getIter(xpath + "/*"); + if (!iter) { + return (ElementPtr()); + } + ElementPtr result = Element::createList(); + for (;;) { + const string& option = getNext(iter); + if (option.empty()) { + break; + } + result->add(getOutputOption(option)); + } + return (result); +} + +void +TranslatorLogger::setLogger(const string& xpath, ConstElementPtr elem) { + try { + if ((model_ == "kea-dhcp4") || (model_ == "kea-dhcp6") || + (model_ == "kea-dhcpddns") || (model_ == "kea-control-agent")) { + setLoggerKea(xpath, elem); + } else { + isc_throw(NotImplemented, + "setLogger not implemented for the model: " << model_); + } + } catch (const sysrepo_exception& ex) { + isc_throw(SysrepoError, + "sysrepo error setting logger '" << elem->str() + << "' at '" << xpath << "': " << ex.what()); + } +} + +void +TranslatorLogger::setLoggerKea(const string& xpath, ConstElementPtr elem) { + ConstElementPtr options = elem->get("output_options"); + if (options && (options->size() > 0)) { + setOutputOptions(xpath + "/output-options", options); + } + ConstElementPtr debuglevel = elem->get("debuglevel"); + if (debuglevel) { + setItem(xpath + "/debuglevel", debuglevel, SR_UINT8_T); + } + ConstElementPtr severity = elem->get("severity"); + if (severity) { + setItem(xpath + "/severity", severity, SR_ENUM_T); + } + ConstElementPtr context = Adaptor::getContext(elem); + if (context) { + setItem(xpath + "/user-context", Element::create(context->str()), + SR_STRING_T); + } +} + +void +TranslatorLogger::setOutputOption(const string& xpath, ConstElementPtr elem) { + bool created = false; + ConstElementPtr maxver = elem->get("maxver"); + if (maxver) { + setItem(xpath + "/maxver", maxver, SR_UINT32_T); + created = true; + } + ConstElementPtr maxsize = elem->get("maxsize"); + if (maxsize) { + setItem(xpath + "/maxsize", maxsize, SR_UINT32_T); + created = true; + } + ConstElementPtr flush = elem->get("flush"); + if (flush) { + setItem(xpath + "/flush", flush, SR_BOOL_T); + created = true; + } + // There is no mandatory fields outside the key so force creation. + if (!created) { + ConstElementPtr list = Element::createList(); + setItem(xpath, list, SR_LIST_T); + } +} + +void +TranslatorLogger::setOutputOptions(const string& xpath, ConstElementPtr elem) { + for (size_t i = 0; i < elem->size(); ++i) { + ConstElementPtr option = elem->get(i); + if (!option->contains("output")) { + isc_throw(BadValue, "output-options without output: " + << option->str()); + } + string output = option->get("output")->stringValue(); + ostringstream key; + key << xpath << "/option[output='" << output << "']"; + setOutputOption(key.str(), option); + } +} + +TranslatorLoggers::TranslatorLoggers(S_Session session, const string& model) + : TranslatorBasic(session), + TranslatorLogger(session, model), + model_(model) { +} + +TranslatorLoggers::~TranslatorLoggers() { +} + +ConstElementPtr +TranslatorLoggers::getLoggers(const string& xpath) { + try { + if ((model_ == "kea-dhcp4") || (model_ == "kea-dhcp6") || + (model_ == "kea-dhcpddns") || (model_ == "kea-control-agent")) { + return (getLoggersKea(xpath)); + } + } catch (const sysrepo_exception& ex) { + isc_throw(SysrepoError, + "sysrepo error getting loggeres at '" << xpath + << "': " << ex.what()); + } + isc_throw(NotImplemented, + "getLoggers not implemented for the model: " << model_); +} + +ElementPtr +TranslatorLoggers::getLoggersKea(const string& xpath) { + S_Iter_Value iter = getIter(xpath + "/*"); + if (!iter) { + return (ElementPtr()); + } + ElementPtr result = Element::createList(); + for (;;) { + const string& logger = getNext(iter); + if (logger.empty()) { + break; + } + result->add(getLogger(logger)); + } + return (result); +} + +void +TranslatorLoggers::setLoggers(const string& xpath, ConstElementPtr elem) { + try { + if ((model_ == "kea-dhcp4") || (model_ == "kea-dhcp6") || + (model_ == "kea-dhcpddns") || (model_ == "kea-control-agent")) { + setLoggersKea(xpath, elem); + } else { + isc_throw(NotImplemented, + "setLoggers not implemented for the model: " << model_); + } + } catch (const sysrepo_exception& ex) { + isc_throw(SysrepoError, + "sysrepo error setting loggeres '" << elem->str() + << "' at '" << xpath << "': " << ex.what()); + } +} + +void +TranslatorLoggers::setLoggersKea(const string& xpath, ConstElementPtr elem) { + for (size_t i = 0; i < elem->size(); ++i) { + ConstElementPtr logger = elem->get(i); + if (!logger->contains("name")) { + isc_throw(BadValue, "logger without name: " << logger->str()); + } + string name = logger->get("name")->stringValue(); + ostringstream key; + key << xpath << "/logger[name='" << name << "']"; + setLogger(key.str(), logger); + } +} + +}; // end of namespace isc::yang +}; // end of namespace isc diff --git a/src/lib/yang/translator_logger.h b/src/lib/yang/translator_logger.h new file mode 100644 index 0000000000000000000000000000000000000000..e45d9e29b50f8594bd98a5a15d4c55c84454812c --- /dev/null +++ b/src/lib/yang/translator_logger.h @@ -0,0 +1,178 @@ +// 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_LOGGER_H +#define ISC_TRANSLATOR_LOGGER_H 1 + +#include +#include + +namespace isc { +namespace yang { + +// @brief Between Yang and JSON translator class for a logger. +class TranslatorLogger : virtual public TranslatorBasic { +public: + + /// @brief Constructor. + /// + /// @param session Sysrepo session. + /// @param model Model name. + TranslatorLogger(S_Session session, const std::string& model); + + /// @brief Destructor. + virtual ~TranslatorLogger(); + + /// @brief Get and translate a logger from Yang to JSON. + /// + /// JSON syntax for all Kea servers with loggers is: + /// @code + /// { + /// "name": , + /// "output_options": [ ], + /// "severity": , + /// "debuglevel": , + /// "user-context": { }, + /// "comment": + /// } + /// @endcode + /// + /// @param xpath The xpath of the logger. + /// @return JSON representation of the logger. + /// @throw SysrepoError when sysrepo raises an error. + isc::data::ElementPtr getLogger(const std::string& xpath); + + /// @brief Translate and set logger from JSON to Yang. + /// + /// @param xpath The xpath of the logger. + /// @param elem The JSON element. + void setLogger(const std::string& xpath, isc::data::ConstElementPtr elem); + +protected: + /// @brief Get and translate an output option from Yang to JSON. + /// + /// JSON syntax for all Kea server fro output options is: + /// @code + /// { + /// "output": , + /// "maxver": , + /// "maxsize": , + /// "flush": + /// } + /// @endcode + /// + /// @param xpath The xpath of the output option. + /// @return JSON representation of the output option. + /// @throw SysrepoError when sysrepo raises an error. + isc::data::ElementPtr getOutputOption(const std::string& xpath); + + /// @brief Get and translate output options from Yang to JSON. + /// + /// @param xpath The xpath of output options. + /// @return JSON representation of output options. + /// @throw SysrepoError when sysrepo raises an error. + isc::data::ElementPtr getOutputOptions(const std::string& xpath); + + /// @brief Translate and set an output option from JSON to Yang. + /// + /// @param xpath The xpath of the output option. + /// @param elem The JSON element. + void setOutputOption(const std::string& xpath, + isc::data::ConstElementPtr elem); + + /// @brief Translate and set output options from JSON to Yang. + /// + /// @param xpath The xpath of the output options. + /// @param elem The JSON element. + void setOutputOptions(const std::string& xpath, + isc::data::ConstElementPtr elem); + + /// @brief getLogger JSON for kea-logging. + /// + /// @param xpath The xpath of the logger. + /// @return JSON representation of the logger. + /// @throw SysrepoError when sysrepo raises an error. + isc::data::ElementPtr getLoggerKea(const std::string& xpath); + + /// @brief setLogger for kea-logging. + /// + /// Yang syntax for kea-logging is: + /// @code + /// +--rw logger container + /// | + /// +--rw name? string + /// +--rw output-options container + /// | +--rw option* [output] + /// | +--rw output string + /// | +--rw maxver? uint32 + /// | +--rw maxsize? uint32 + /// | +--rw flush? boolean + /// +--rw debuglevel? uint8 + /// +--rw severity? enumeration + /// +--rw user-context? string + /// @endcode + /// + /// @param xpath The xpath of the logger. + /// @param elem The JSON element. + void setLoggerKea(const std::string& xpath, + isc::data::ConstElementPtr elem); + + /// @brief The model. + std::string model_; +}; + +// @brief Between Yang and JSON translator class for loggers. +class TranslatorLoggers : virtual public TranslatorLogger { +public: + + /// @brief Constructor. + /// + /// @param session Sysrepo session. + /// @param model Model name. + TranslatorLoggers(S_Session session, const std::string& model); + + /// @brief Destructor. + virtual ~TranslatorLoggers(); + + /// @brief Get and translate loggeres from Yang to JSON. + /// + /// @param xpath The xpath of loggers. + /// @return JSON representation of loggers. + /// @throw SysrepoError when sysrepo raises an error. + isc::data::ConstElementPtr getLoggers(const std::string& xpath); + + /// @brief Translate and set loggeres from JSON to Yang. + /// + /// @param xpath The xpath of loggers. + /// @param elem The JSON element. + void setLoggers(const std::string& xpath, + isc::data::ConstElementPtr elem); + +protected: + /// @brief getLoggers JSON for kea-logging. + /// + /// @param xpath The xpath of loggers. + /// @return JSON representation of loggers. + /// @throw SysrepoError when sysrepo raises an error. + isc::data::ElementPtr getLoggersKea(const std::string& xpath); + + /// @brief setLoggers for kea-logging. + /// + /// Logger key is the name, output options key is the output. + /// + /// @param xpath The xpath of loggers. + /// @param elem The JSON element. + void setLoggersKea(const std::string& xpath, + isc::data::ConstElementPtr elem); + + /// @brief The model. + std::string model_; +}; + +}; // end of namespace isc::yang +}; // end of namespace isc + +#endif // ISC_TRANSLATOR_LOGGER_H