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_control_socket.cc
+libkea_yang_la_SOURCES += translator_control_socket.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
if HAVE_GTEST
TESTS += run_unittests
run_unittests_SOURCES = translator_unittests.cc
+run_unittests_SOURCES += translator_control_socket_unittests.cc
run_unittests_SOURCES += run_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
--- /dev/null
+// 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_control_socket.h>
+
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::data;
+using namespace isc::yang;
+
+namespace {
+
+// Test get empty control socket using the Kea ad hoc model.
+TEST(TranslatorControlSocketTest, getEmpty) {
+ // Get a translator control socket object to play with.
+ S_Connection conn(new Connection("translator control socket unittests"));
+ S_Session sess(new Session(conn, SR_DS_CANDIDATE));
+ boost::scoped_ptr<TranslatorControlSocket> tcs_obj;
+
+ // Use the ad hoc model.
+ const string& model = "kea-dhcp4";
+ EXPECT_NO_THROW(tcs_obj.reset(new TranslatorControlSocket(sess, model)));
+
+ // Get empty.
+ const string& xpath = "/kea-dhcp4:config/control-socket";
+ ConstElementPtr sock;
+ EXPECT_NO_THROW(sock = tcs_obj->getControlSocket(xpath));
+ EXPECT_FALSE(sock);
+}
+
+// Test get not empty control socket using the Kea ad hoc model.
+TEST(TranslatorControlSocketTest, get) {
+ // Get a translator control socket object to play with.
+ S_Connection conn(new Connection("translator control socket unittests"));
+ S_Session sess(new Session(conn, SR_DS_CANDIDATE));
+ boost::scoped_ptr<TranslatorControlSocket> tcs_obj;
+
+ // Use the ad hoc model.
+ const string& model = "kea-dhcp6";
+ EXPECT_NO_THROW(tcs_obj.reset(new TranslatorControlSocket(sess, model)));
+
+ // Set a value.
+ const string& xpath = "/kea-dhcp6:config/control-socket";
+ const string& xname = xpath + "/socket-name";
+ const string& xtype = xpath + "/socket-type";
+ const string& xcontext = xpath + "/user-context";
+ S_Val s_name(new Val("/tmp/kea.sock"));
+ EXPECT_NO_THROW(sess->set_item(xname.c_str(), s_name));
+ S_Val s_type(new Val("unix", SR_ENUM_T));
+ EXPECT_NO_THROW(sess->set_item(xtype.c_str(), s_type));
+ S_Val s_context(new Val("{ \"foo\": 1 }"));
+ EXPECT_NO_THROW(sess->set_item(xcontext.c_str(), s_context));
+
+ // Get it.
+ ConstElementPtr sock;
+ EXPECT_NO_THROW(sock = tcs_obj->getControlSocket(xpath));
+ ASSERT_TRUE(sock);
+ ASSERT_EQ(Element::map, sock->getType());
+ EXPECT_EQ(3, sock->size());
+ ConstElementPtr type = sock->get("socket-type");
+ ASSERT_TRUE(type);
+ ASSERT_EQ(Element::string, type->getType());
+ EXPECT_EQ("unix", type->stringValue());
+ ConstElementPtr name = sock->get("socket-name");
+ ASSERT_TRUE(name);
+ ASSERT_EQ(Element::string, name->getType());
+ EXPECT_EQ("/tmp/kea.sock", name->stringValue());
+ ConstElementPtr context = sock->get("user-context");
+ ASSERT_TRUE(context);
+ EXPECT_EQ("{ \"foo\": 1 }", context->str());
+}
+
+// Test set not empty control socket using the Kea ad hoc model.
+TEST(TranslatorControlSocketTest, set) {
+ // Get a translator control socket object to play with.
+ S_Connection conn(new Connection("translator control socket unittests"));
+ S_Session sess(new Session(conn, SR_DS_CANDIDATE));
+ boost::scoped_ptr<TranslatorControlSocket> tcs_obj;
+
+ // Use the ad hoc model.
+ const string& model = "kea-control-agent";
+ EXPECT_NO_THROW(tcs_obj.reset(new TranslatorControlSocket(sess, model)));
+
+ // Set a value.
+ const string& xpath =
+ "/kea-control-agent:config/control-sockets/socket[server-type='dhcp4']/control-socket";
+ ElementPtr sock = Element::createMap();
+ sock->set("socket-name", Element::create(string("/tmp/kea.sock")));
+ sock->set("socket-type", Element::create(string("unix")));
+ sock->set("comment", Element::create(string("a comment")));
+ try {
+ tcs_obj->setControlSocket(xpath, sock);
+ } catch (const std::exception& ex) {
+ cerr << "setControlSocket fail with " << ex.what() << endl;
+ }
+ ASSERT_NO_THROW(tcs_obj->setControlSocket(xpath, sock));
+
+ // Get it back.
+ ConstElementPtr got;
+ EXPECT_NO_THROW(got = tcs_obj->getControlSocket(xpath));
+ ASSERT_TRUE(got);
+ ASSERT_EQ(Element::map, got->getType());
+ EXPECT_EQ(3, got->size());
+ ConstElementPtr name = got->get("socket-name");
+ ASSERT_TRUE(name);
+ ASSERT_EQ(Element::string, name->getType());
+ EXPECT_EQ("/tmp/kea.sock", name->stringValue());
+ ConstElementPtr type = got->get("socket-type");
+ ASSERT_TRUE(type);
+ ASSERT_EQ(Element::string, type->getType());
+ EXPECT_EQ("unix", type->stringValue());
+ ConstElementPtr context = got->get("user-context");
+ ASSERT_TRUE(context);
+ EXPECT_EQ("{ \"comment\": \"a comment\" }", context->str());
+}
+
+// Test set empty control socket using the Kea ad hoc model.
+TEST(TranslatorControlSocketTest, setEmpty) {
+ // Get a translator control socket object to play with.
+ S_Connection conn(new Connection("translator control socket unittests"));
+ S_Session sess(new Session(conn, SR_DS_CANDIDATE));
+ boost::scoped_ptr<TranslatorControlSocket> tcs_obj;
+
+ // Use the ad hoc model.
+ const string& model = "kea-dhcp4";
+ EXPECT_NO_THROW(tcs_obj.reset(new TranslatorControlSocket(sess, model)));
+
+ // Set a value.
+ const string& xpath = "/kea-dhcp4:config/control-socket";
+ const string& xname = xpath + "/socket-name";
+ const string& xtype = xpath + "/socket-type";
+ const string& xcontext = xpath + "/user-context";
+ S_Val s_name(new Val("/tmp/kea.sock"));
+ EXPECT_NO_THROW(sess->set_item(xname.c_str(), s_name));
+ S_Val s_type(new Val("unix", SR_ENUM_T));
+ EXPECT_NO_THROW(sess->set_item(xtype.c_str(), s_type));
+ S_Val s_context(new Val("{ \"foo\": 1 }"));
+ EXPECT_NO_THROW(sess->set_item(xcontext.c_str(), s_context));
+
+ // Reset to empty.
+ ASSERT_NO_THROW(tcs_obj->setControlSocket(xpath, ConstElementPtr()));
+
+ // Get it back.
+ ConstElementPtr sock;
+ EXPECT_NO_THROW(sock = tcs_obj->getControlSocket(xpath));
+ EXPECT_FALSE(sock);
+}
+
+}; // end of anonymous namespace
--- /dev/null
+// 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_control_socket.h>
+#include <yang/adaptor.h>
+#include <sstream>
+
+using namespace std;
+using namespace isc::data;
+
+namespace isc {
+namespace yang {
+
+TranslatorControlSocket::TranslatorControlSocket(S_Session session,
+ const string& model)
+ : TranslatorBasic(session), model_(model) {
+}
+
+TranslatorControlSocket::~TranslatorControlSocket() {
+}
+
+ConstElementPtr
+TranslatorControlSocket::getControlSocket(const string& xpath) {
+ try {
+ if ((model_ == "kea-dhcp4") || (model_ == "kea-dhcp6") ||
+ (model_ == "kea-dhcpddns") || (model_ == "kea-control-agent")) {
+ return (getControlSocketKea(xpath));
+ }
+ } catch (const sysrepo_exception& ex) {
+ isc_throw(SysrepoError,
+ "sysrepo error getting control socket at '" << xpath
+ << "': " << ex.what());
+ }
+ isc_throw(NotImplemented,
+ "getControlSocket not implemented for the model: " << model_);
+}
+
+ElementPtr
+TranslatorControlSocket::getControlSocketKea(const string& xpath) {
+ ConstElementPtr name = getItem(xpath + "/socket-name");
+ ConstElementPtr type = getItem(xpath + "/socket-type");
+ if (name && type) {
+ ElementPtr result = Element::createMap();
+ result->set("socket-name", name);
+ result->set("socket-type", type);
+ ConstElementPtr context = getItem(xpath + "/user-context");
+ if (context) {
+ result->set("user-context",
+ Element::fromJSON(context->stringValue()));
+ }
+ return (result);
+ }
+ return (ElementPtr());
+}
+
+void
+TranslatorControlSocket::setControlSocket(const string& xpath,
+ ConstElementPtr elem) {
+ try {
+ if ((model_ == "kea-dhcp4") || (model_ == "kea-dhcp6") ||
+ (model_ == "kea-dhcpddns") || (model_ == "kea-control-agent")) {
+ setControlSocketKea(xpath, elem);
+ } else {
+ isc_throw(NotImplemented,
+ "setControlSocket not implemented for the model: "
+ << model_);
+ }
+ } catch (const sysrepo_exception& ex) {
+ isc_throw(SysrepoError,
+ "sysrepo error setting control socket '" << elem->str()
+ << "' at '" << xpath << "': " << ex.what());
+ }
+}
+
+void
+TranslatorControlSocket::setControlSocketKea(const string& xpath,
+ ConstElementPtr elem) {
+ if (!elem) {
+ delItem(xpath + "/socket-name");
+ delItem(xpath + "/socket-type");
+ delItem(xpath + "/user-context");
+ delItem(xpath);
+ return;
+ }
+ ConstElementPtr name = elem->get("socket-name");
+ if (!name) {
+ isc_throw(BadValue, "setControlSocket missing socket name");
+ }
+ ConstElementPtr type = elem->get("socket-type");
+ if (!type) {
+ isc_throw(BadValue, "setControlSocket missing socket type");
+ }
+ setItem(xpath + "/socket-name", name, SR_STRING_T);
+ setItem(xpath + "/socket-type", type, SR_ENUM_T);
+ ConstElementPtr context = Adaptor::getContext(elem);
+ if (context) {
+ setItem(xpath + "/user-context", Element::create(context->str()),
+ SR_STRING_T);
+ }
+}
+
+}; // end of namespace isc::yang
+}; // end of namespace isc
--- /dev/null
+// 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_CONTROL_SOCKET_H
+#define ISC_TRANSLATOR_CONTROL_SOCKET_H 1
+
+#include <yang/translator.h>
+#include <list>
+
+namespace isc {
+namespace yang {
+
+// @brief Between Yang and JSON translator class for the control socket.
+class TranslatorControlSocket : virtual public TranslatorBasic {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// @param session Sysrepo session.
+ /// @param model Model name.
+ TranslatorControlSocket(S_Session session, const std::string& model);
+
+ /// @brief Destructor.
+ virtual ~TranslatorControlSocket();
+
+ /// @brief Get and translate a control socket from Yang to JSON.
+ ///
+ /// JSON syntax for all Kea servers with command channel is:
+ /// @code
+ /// "control-socket": {
+ /// "socket-type": "<socket type>",
+ /// "socket-name": "<socket name>",
+ /// "user-context": { <json map> },
+ /// "comment": "<comment>"
+ /// }
+ /// @endcode
+ ///
+ /// @param xpath The xpath of the control socket.
+ /// @return JSON representation of the control socket.
+ /// @throw SysrepoError when sysrepo raises an error.
+ isc::data::ConstElementPtr getControlSocket(const std::string& xpath);
+
+ /// @brief Translate and set control socket from JSON to Yang.
+ ///
+ /// @param xpath The xpath of the control socket.
+ /// @param elem The JSON element.
+ void setControlSocket(const std::string& xpath,
+ isc::data::ConstElementPtr elem);
+
+protected:
+ /// @brief getControlSocket JSON for kea models.
+ ///
+ /// @param xpath The xpath of the control socket.
+ /// @return JSON representation of the control socket.
+ /// @throw SysrepoError when sysrepo raises an error.
+ isc::data::ElementPtr getControlSocketKea(const std::string& xpath);
+
+ /// @brief setControlSocket for kea models.
+ ///
+ /// Yang syntax is:
+ /// @code
+ /// +--rw control-socket container
+ /// |
+ /// +--rw socket-name string
+ /// +--rw socket-type enumeration
+ /// +--rw user-context? string
+ /// @endcode
+ ///
+ /// @param xpath The xpath of the control socket.
+ /// @param elem The JSON element.
+ void setControlSocketKea(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_CONTROL_SOCKET_H