]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[65-libyang-option-data] Filled files, waiting for adaptor merge
authorFrancis Dupont <fdupont@isc.org>
Thu, 13 Sep 2018 13:38:51 +0000 (15:38 +0200)
committerFrancis Dupont <fdupont@isc.org>
Fri, 14 Sep 2018 14:12:48 +0000 (16:12 +0200)
src/lib/yang/Makefile.am
src/lib/yang/tests/Makefile.am
src/lib/yang/tests/translator_option_data_unittests.cc [new file with mode: 0644]
src/lib/yang/translator_option_data.cc [new file with mode: 0644]
src/lib/yang/translator_option_data.h [new file with mode: 0644]

index 01b3a633afbfb8df9ff014a2abcdd6323c8c2823..c743e1e50ba73d7d5893f2cd86ba61e64d3e3714 100644 (file)
@@ -7,6 +7,8 @@ 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_option_data.cc
+libkea_yang_la_SOURCES += translator_option_data.h
 
 libkea_yang_la_LIBADD =  $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
 libkea_yang_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la
index f54de8916496de5858c5fadc2a26c0872e7490d0..55cc0b97a2a11b5069118a7aa3ff0ee2622a0828 100644 (file)
@@ -18,6 +18,7 @@ TESTS =
 if HAVE_GTEST
 TESTS += run_unittests
 run_unittests_SOURCES  = translator_unittests.cc
+run_unittests_SOURCES += translator_option_data_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_option_data_unittests.cc b/src/lib/yang/tests/translator_option_data_unittests.cc
new file mode 100644 (file)
index 0000000..1de5dc6
--- /dev/null
@@ -0,0 +1,157 @@
+// 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_option_data.h>
+
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+#include <sstream>
+
+using namespace std;
+using namespace isc;
+using namespace isc::data;
+using namespace isc::yang;
+
+namespace {
+
+// Test get empty option data list.
+TEST(TranslatorOptionDataListTest, getEmpty) {
+    // Get a translator option data list object to play with.
+    S_Connection conn(new Connection("translator option data list unittests"));
+    S_Session sess(new Session(conn, SR_DS_CANDIDATE));
+    boost::scoped_ptr<TranslatorOptionDataList> todl_obj;
+
+    // Use the ad hoc model.
+    const string& model = "kea-dhcp4";
+    EXPECT_NO_THROW(todl_obj.reset(new TranslatorOptionDataList(sess, model)));
+
+    // Get the option data list and checks it is empty.
+    const string& xpath = "/kea-dhcp4:config/option-data-list";
+    ConstElementPtr options;
+    EXPECT_NO_THROW(options = todl_obj->getOptionDataList(xpath));
+    ASSERT_TRUE(options);
+    ASSERT_EQ(Element::list, options->getType());
+    EXPECT_EQ(0, options->size());
+}
+
+// Test get one option data.
+TEST(TranslatorOptionDataListTest, get) {
+    // Get a translator option data list object to play with.
+    S_Connection conn(new Connection("translator option data list unittests"));
+    S_Session sess(new Session(conn, SR_DS_CANDIDATE));
+    boost::scoped_ptr<TranslatorOptionDataList> todl_obj;
+
+    // Use the ad hoc model.
+    const string& model = "kea-dhcp6";
+    EXPECT_NO_THROW(todl_obj.reset(new TranslatorOptionDataList(sess, model)));
+
+    // Create the option code 100.
+    const string& xpath = "/kea-dhcp6:config/option-data-list";
+    const string& xoption = xpath + "/option-data[code='100'][space='dns']";
+    const string& xformat = xoption + "/csv-format";
+    const string& xdata = xoption + "/data";
+    const string& xsend = xoption + "/always-send";
+    S_Val s_false(new Val(false));
+    ASSERT_NO_THROW(sess->set_item(xformat.c_str(), s_false));
+    S_Val s_data(new Val("12121212"));
+    ASSERT_NO_THROW(sess->set_item(xdata.c_str(), s_data));
+    ASSERT_NO_THROW(sess->set_item(xsend.c_str(), s_false));
+    
+    // Get the option data.
+    ConstElementPtr option;
+    EXPECT_NO_THROW(option = todl_obj->getOptionData(xoption));
+    ASSERT_TRUE(option);
+    EXPECT_EQ("{ \"always-send\": false, \"code\": 100, \"csv-format\": false, \"data\": \"12121212\", \"space\": \"dns\" }",
+              option->str());
+
+    // Get the option data list.
+    ConstElementPtr options;
+    EXPECT_NO_THROW(options = todl_obj->getOptionDataList(xpath));
+    ASSERT_TRUE(options);
+    ASSERT_EQ(Element::list, options->getType());
+    EXPECT_EQ(1, options->size());
+    EXPECT_TRUE(option->equals(*options->get(0)));
+}
+
+// Test set empty option data list.
+TEST(TranslatorOptionDataListTest, setEmpty) {
+    // Get a translator option data list object to play with.
+    S_Connection conn(new Connection("translator option data list unittests"));
+    S_Session sess(new Session(conn, SR_DS_CANDIDATE));
+    boost::scoped_ptr<TranslatorOptionDataList> todl_obj;
+
+    // Use the ad hoc model.
+    const string& model = "kea-dhcp4";
+    EXPECT_NO_THROW(todl_obj.reset(new TranslatorOptionDataList(sess, model)));
+
+    // Set empty list.
+    const string& xpath = "/kea-dhcp4:config/option-data-list";
+    ConstElementPtr options = Element::createList();
+    EXPECT_NO_THROW(todl_obj->setOptionDataList(xpath, options));
+
+    // Get it back.
+    options.reset();
+    EXPECT_NO_THROW(options = todl_obj->getOptionDataList(xpath));
+    ASSERT_TRUE(options);
+    EXPECT_EQ(0, options->size());
+}
+
+// Test set an option data.
+TEST(TranslatorOptionDataListTest, set) {
+    // Get a translator option data list object to play with.
+    S_Connection conn(new Connection("translator option data list unittests"));
+    S_Session sess(new Session(conn, SR_DS_CANDIDATE));
+    boost::scoped_ptr<TranslatorOptionDataList> todl_obj;
+
+    // Use the ad hoc model.
+    const string& model = "kea-dhcp6";
+    EXPECT_NO_THROW(todl_obj.reset(new TranslatorOptionDataList(sess, model)));
+
+    // Set one option data.
+    const string& xpath = "/kea-dhcp6:config/option-data-list";
+    ElementPtr options = Element::createList();
+    ElementPtr option = Element::createMap();
+    option->set("code", Element::create(100));
+    option->set("space", Element::create(string("dns")));
+    option->set("csv-format", Element::create(false));
+    option->set("data", Element::create(string("12121212")));
+    option->set("always-send", Element::create(false));
+    options->add(option);
+    EXPECT_NO_THROW(todl_obj->setOptionDataList(xpath, options));
+
+    // Get it back.
+    ConstElementPtr got;
+    EXPECT_NO_THROW(got = todl_obj->getOptionDataList(xpath));
+    ASSERT_TRUE(got);
+    ASSERT_EQ(1, got->size());
+    EXPECT_TRUE(option->equals(*got->get(0)));
+
+    // Check the tree representation.
+    S_Tree tree;
+    EXPECT_NO_THROW(tree = sess->get_subtree("/kea-dhcp6:config"));
+    ASSERT_TRUE(tree);
+    string expected =
+        "kea-dhcp6:config (container)\n"
+        " |\n"
+        " -- option-data-list (container)\n"
+        "     |\n"
+        "     -- option-data (list instance)\n"
+        "         |\n"
+        "         -- code = 100\n"
+        "         |\n"
+        "         -- space = dns\n"
+        "         |\n"
+        "         -- data = 12121212\n"
+        "         |\n"
+        "         -- csv-format = false\n"
+        "         |\n"
+        "         -- always-send = false\n";
+    EXPECT_EQ(expected, tree->to_string(100));
+}
+
+}; // end of anonymous namespace
diff --git a/src/lib/yang/translator_option_data.cc b/src/lib/yang/translator_option_data.cc
new file mode 100644 (file)
index 0000000..304bd37
--- /dev/null
@@ -0,0 +1,197 @@
+// 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_option_data.h>
+#include <yang/adaptor.h>
+#include <sstream>
+
+using namespace std;
+using namespace isc::data;
+
+namespace isc {
+namespace yang {
+
+TranslatorOptionData::TranslatorOptionData(S_Session session,
+                                           const string& model)
+    : TranslatorBasic(session), model_(model) {
+}
+
+TranslatorOptionData::~TranslatorOptionData() {
+}
+
+ElementPtr
+TranslatorOptionData::getOptionData(const string& xpath) {
+    try {
+        if ((model_ == "kea-dhcp4") || (model_ == "kea-dhcp6")) {
+            return (getOptionDataKea(xpath));
+        }
+    } catch (const sysrepo_exception& ex) {
+        isc_throw(SysrepoError,
+                  "sysrepo error getting option data at '" << xpath
+                  << "': " << ex.what());
+    }
+    isc_throw(NotImplemented,
+              "getOptionData not implemented for the model: " << model_);
+}
+
+ElementPtr
+TranslatorOptionData::getOptionDataKea(const string& xpath) {
+    ConstElementPtr code = getItem(xpath + "/code");
+    ConstElementPtr space = getItem(xpath + "/space");
+    if (!code || !space) {
+        return (ElementPtr());
+    }
+    ElementPtr result = Element::createMap();
+    result->set("code", code);
+    result->set("space", space);
+    ConstElementPtr name = getItem(xpath + "/name");
+    if (name) {
+        result->set("name", name);
+    }
+    ConstElementPtr data = getItem(xpath + "/data");
+    if (data) {
+        result->set("data", data);
+    }
+    ConstElementPtr format = getItem(xpath + "/csv-format");
+    if (format) {
+        result->set("csv-format", format);
+    }
+    ConstElementPtr send = getItem(xpath + "/always-send");
+    if (send) {
+        result->set("always-send", send);
+    }
+    ConstElementPtr context = getItem(xpath + "/user-context");
+    if (context) {
+        result->set("user-context", Element::fromJSON(context->stringValue()));
+    }
+    return (result);
+}
+
+void
+TranslatorOptionData::setOptionData(const string& xpath,
+                                    ConstElementPtr elem) {
+    try {
+        if ((model_ == "kea-dhcp4") || (model_ == "kea-dhcp6")) {
+            setOptionDataKea(xpath, elem);
+        } else {
+            isc_throw(NotImplemented,
+                      "setOptionData not implemented for the model: "
+                      << model_);
+        }
+    } catch (const sysrepo_exception& ex) {
+        isc_throw(SysrepoError,
+                  "sysrepo error setting option data '" << elem->str()
+                  << "' at '" << xpath << "': " << ex.what());
+    }
+}
+
+void
+TranslatorOptionData::setOptionDataKea(const string& xpath,
+                                       ConstElementPtr elem) {
+    ConstElementPtr name = elem->get("name");
+    if (name) {
+        setItem(xpath + "/name", name, SR_STRING_T);
+    }
+    ConstElementPtr data = elem->get("data");
+    if (data) {
+        setItem(xpath + "/data", data, SR_STRING_T);
+    }
+    ConstElementPtr format = elem->get("csv-format");
+    if (format) {
+        setItem(xpath + "/csv-format", format, SR_BOOL_T);
+    }
+    ConstElementPtr send = elem->get("always-send");
+    if (send) {
+        setItem(xpath + "/always-send", send, SR_BOOL_T);
+    }
+    ConstElementPtr context = Adaptor::getContext(elem);
+    if (context) {
+        setItem(xpath + "/user-context", Element::create(context->str()),
+                SR_STRING_T);
+    }
+}
+
+TranslatorOptionDataList::TranslatorOptionDataList(S_Session session,
+                                                   const string& model)
+    : TranslatorBasic(session), TranslatorOptionData(session, model),
+      model_(model) {
+}
+
+TranslatorOptionDataList::~TranslatorOptionDataList() {
+}
+
+ConstElementPtr
+TranslatorOptionDataList::getOptionDataList(const string& xpath) {
+    try {
+        if ((model_ == "kea-dhcp4") || (model_ == "kea-dhcp6")) {
+            return (getOptionDataListKea(xpath));
+        }
+    } catch (const sysrepo_exception& ex) {
+        isc_throw(SysrepoError,
+                  "sysrepo error getting option data list at '" << xpath
+                  << "': " << ex.what());
+    }
+    isc_throw(NotImplemented,
+              "getOptionDataList not implemented for the model: " << model_);
+}
+
+ConstElementPtr
+TranslatorOptionDataList::getOptionDataListKea(const string& xpath) {
+    ElementPtr result = Element::createList();
+    S_Iter_Value iter = getIter(xpath + "/*");
+    if (!iter) {
+        return (ConstElementPtr());
+    }
+    for (;;) {
+        const string& option = getNext(iter);
+        if (option.empty()) {
+            break;
+        }
+        result->add(getOptionData(option));
+    }
+    return (result);
+}
+
+void
+TranslatorOptionDataList::setOptionDataList(const string& xpath,
+                                            ConstElementPtr elem) {
+    try {
+        if ((model_ == "kea-dhcp4") || (model_ == "kea-dhcp6")) {
+            setOptionDataListKea(xpath, elem);
+        } else {
+            isc_throw(NotImplemented,
+                      "setOptionDataList not implemented for the model: "
+                      << model_);
+        }
+    } catch (const sysrepo_exception& ex) {
+        isc_throw(SysrepoError,
+                  "sysrepo error setting option data list '" << elem->str()
+                  << "' at '" << xpath << "': " << ex.what());
+    }
+}
+
+void
+TranslatorOptionDataList::setOptionDataListKea(const string& xpath,
+                                               ConstElementPtr elem) {
+    for (size_t i = 0; i < elem->size(); ++i) {
+        ConstElementPtr option = elem->get(i);
+        if (!option->contains("code")) {
+            isc_throw(BadValue, "option data without code: " << option->str());
+        }
+        unsigned code = static_cast<unsigned>(option->get("code")->intValue());
+        if (!option->contains("space")) {
+            isc_throw(BadValue,"option data without space: " <<option->str());
+        }
+        string space = option->get("space")->stringValue();
+        ostringstream keys;
+        keys << xpath << "/option-data[code='" << code
+             << "'][space='" << space << "']";
+        setOptionData(keys.str(), option);
+    }
+}
+
+}; // end of namespace isc::yang
+}; // end of namespace isc
diff --git a/src/lib/yang/translator_option_data.h b/src/lib/yang/translator_option_data.h
new file mode 100644 (file)
index 0000000..3600a80
--- /dev/null
@@ -0,0 +1,136 @@
+// 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_OPTION_DATA_H
+#define ISC_TRANSLATOR_OPTION_DATA_H 1
+
+#include <yang/translator.h>
+#include <list>
+
+namespace isc {
+namespace yang {
+
+// @brief Between Yang and JSON translator class for the option data.
+class TranslatorOptionData : virtual public TranslatorBasic {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// @param session Sysrepo session.
+    /// @param model Model name.
+    TranslatorOptionData(S_Session session, const std::string& model);
+
+    /// @brief Destructor.
+    virtual ~TranslatorOptionData();
+
+    /// @brief Get and translate an option data from Yang to JSON.
+    ///
+    /// JSON syntax for Kea DHCP with command channel is:
+    /// @code
+    /// {
+    ///     "code": <code>,
+    ///     "name": <name>,
+    ///     "space": <space>,
+    ///     "csv-format": <csv format flag>,
+    ///     "data": <value>,
+    ///     "always-send": <always send flag>,
+    ///     "user-context": { <json map> },
+    ///     "comment": "<comment>"
+    /// }
+    /// @endcode
+    ///
+    /// @param xpath The xpath of the option data.
+    /// @return JSON representation of the option data.
+    /// @throw SysrepoError when sysrepo raises an error.
+    isc::data::ElementPtr getOptionData(const std::string& xpath);
+
+    /// @brief Translate and set option data from JSON to Yang.
+    ///
+    /// @param xpath The xpath of the option data.
+    /// @param elem The JSON element.
+    void setOptionData(const std::string& xpath,
+                       isc::data::ConstElementPtr elem);
+
+protected:
+    /// @brief getOptionData JSON for kea-dhcp[46].
+    ///
+    /// @param xpath The xpath of the option data.
+    /// @return JSON representation of the option data.
+    /// @throw SysrepoError when sysrepo raises an error.
+    isc::data::ElementPtr getOptionDataKea(const std::string& xpath);
+
+    /// @brief setOptionData for kea-dhcp[46].
+    ///
+    /// Yang syntax for kea-dhcp[46] with code and space as keys is:
+    /// @code
+    ///    +--rw name?          string
+    ///    +--rw data?          string
+    ///    +--rw code           uint8 / uint16
+    ///    +--rw space          string
+    ///    +--rw csv-format?    string
+    ///    +--rw always-send?   boolean
+    ///    +--rw user-context?  string
+    /// @endcode
+    ///
+    /// @param xpath The xpath of the option data.
+    /// @param elem The JSON element.
+    void setOptionDataKea(const std::string& xpath,
+                          isc::data::ConstElementPtr elem);
+
+    /// @brief The model.
+    std::string model_;
+};
+
+// @brief Between Yang and JSON translator class for option data list.
+class TranslatorOptionDataList : virtual public TranslatorOptionData {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// @param session Sysrepo session.
+    /// @param model Model name.
+    TranslatorOptionDataList(S_Session session, const std::string& model);
+
+    /// @brief Destructor.
+    virtual ~TranslatorOptionDataList();
+
+    /// @brief Get and translate option data list from Yang to JSON.
+    ///
+    /// @param xpath The xpath of the option data list.
+    /// @throw SysrepoError when sysrepo raises an error.
+    isc::data::ConstElementPtr getOptionDataList(const std::string& xpath);
+
+    /// @brief Translate and set option data list from JSON to Yang.
+    ///
+    /// @param xpath The xpath of the option data list.
+    /// @param elem The JSON element.
+    void setOptionDataList(const std::string& xpath,
+                           isc::data::ConstElementPtr elem);
+
+protected:
+    /// @brief getOptionDataList for kea-dhcp[46].
+    ///
+    /// @param xpath The xpath of the option data list.
+    /// @throw SysrepoError when sysrepo raises an error.
+    isc::data::ConstElementPtr getOptionDataListKea(const std::string& xpath);
+
+    /// @brief setOptionDataList for kea-dhcp[46].
+    ///
+    /// Yang syntax is a option-data list keyed by code and space.
+    ///
+    /// @param xpath The xpath of the option data list.
+    /// @param elem The JSON element.
+    void setOptionDataListKea(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_OPTION_DATA_H