Commit 17272324 authored by Francis Dupont's avatar Francis Dupont

[65-libyang-option-def_rebased] Rebasing before merge

parents 4ced4b86 45421e66
......@@ -10,6 +10,8 @@ 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_SOURCES += translator_option_def.cc
libkea_yang_la_SOURCES += translator_option_def.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
......@@ -27,7 +29,8 @@ libkea_yang_include_HEADERS = \
adaptor.h \
sysrepo_error.h \
translator.h \
translator_option_data.h
translator_option_data.h \
translator_option_def.h
EXTRA_DIST = yang.dox
# Distribute yang models.
......
......@@ -21,6 +21,7 @@ run_unittests_SOURCES = adaptor_unittests.cc
run_unittests_SOURCES += sysrepo_setup.h
run_unittests_SOURCES += translator_unittests.cc
run_unittests_SOURCES += translator_option_data_unittests.cc
run_unittests_SOURCES += translator_option_def_unittests.cc
run_unittests_SOURCES += run_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
......
// 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_def.h>
#include <yang/tests/sysrepo_setup.h>
#include <gtest/gtest.h>
#include <sstream>
using namespace std;
using namespace isc;
using namespace isc::data;
using namespace isc::yang;
using namespace isc::yang::test;
namespace {
/// @brief Translator name.
extern char const option_definition_list[] = "option definition list";
/// @brief Test fixture class for @ref TranslatorOptionDefList.
class TranslatorOptionDefListTest :
public GenericTranslatorTest<option_definition_list, TranslatorOptionDefList> {
public:
/// Constructor.
TranslatorOptionDefListTest() { }
/// Destructor (does nothing).
virtual ~TranslatorOptionDefListTest() { }
};
// This test verifies that an empty option definition list can be properly
// translated from YANG to JSON.
TEST_F(TranslatorOptionDefListTest, getEmpty) {
useModel("kea-dhcp4-server");
// Get the option definition list and checks it is empty.
const string& xpath = "/kea-dhcp4-server:config/option-def-list";
ConstElementPtr options;
EXPECT_NO_THROW(options = t_obj_->getOptionDefList(xpath));
ASSERT_TRUE(options);
ASSERT_EQ(Element::list, options->getType());
EXPECT_EQ(0, options->size());
}
// This test verifies that one option definition can be properly
// translated from YANG to JSON.
TEST_F(TranslatorOptionDefListTest, get) {
useModel("kea-dhcp6-server");
// Create the option code 100.
const string& xpath = "/kea-dhcp6-server:config/option-def-list";
const string& xdef = xpath + "/option-def[code='100'][space='isc']";
const string& xname = xdef + "/name";
const string& xtype = xdef + "/type";
const string& xarray = xdef + "/array";
S_Val s_name(new Val("foo"));
ASSERT_NO_THROW(sess_->set_item(xname.c_str(), s_name));
S_Val s_type(new Val("string"));
ASSERT_NO_THROW(sess_->set_item(xtype.c_str(), s_type));
S_Val s_array(new Val(false));
ASSERT_NO_THROW(sess_->set_item(xarray.c_str(), s_array));
// Get the option def.
ConstElementPtr def;
EXPECT_NO_THROW(def = t_obj_->getOptionDef(xdef));
ASSERT_TRUE(def);
EXPECT_EQ("{ "
"\"array\": false, "
"\"code\": 100, "
"\"name\": \"foo\", "
"\"space\": \"isc\", "
"\"type\": \"string\" }",
def->str());
// Get the option definition list.
ConstElementPtr defs;
EXPECT_NO_THROW(defs = t_obj_->getOptionDefList(xpath));
ASSERT_TRUE(defs);
ASSERT_EQ(Element::list, defs->getType());
EXPECT_EQ(1, defs->size());
EXPECT_TRUE(def->equals(*defs->get(0)));
}
// This test verifies that an empty option definition list can be properly
// translated from JSON to YANG.
TEST_F(TranslatorOptionDefListTest, setEmpty) {
useModel("kea-dhcp4-server");
// Set empty list.
const string& xpath = "/kea-dhcp4-server:config/option-def-list";
ConstElementPtr defs = Element::createList();
EXPECT_NO_THROW(t_obj_->setOptionDefList(xpath, defs));
// Get it back.
defs.reset();
EXPECT_NO_THROW(defs = t_obj_->getOptionDefList(xpath));
ASSERT_TRUE(defs);
EXPECT_EQ(0, defs->size());
// Check that the tree representation is empty.
S_Tree tree;
EXPECT_NO_THROW(tree = sess_->get_subtree("/kea-dhcp4-server:config"));
EXPECT_FALSE(tree);
}
// This test verifies that one option definition can be properly
// translated from JSON to YANG.
TEST_F(TranslatorOptionDefListTest, set) {
useModel("kea-dhcp6-server");
// Set one option def.
const string& xpath = "/kea-dhcp6-server:config/option-def-list";
ElementPtr defs = Element::createList();
ElementPtr def = Element::createMap();
def->set("code", Element::create(100));
def->set("name", Element::create(string("foo")));
def->set("space", Element::create(string("isc")));
def->set("type", Element::create(string("string")));
def->set("array", Element::create(false));
defs->add(def);
EXPECT_NO_THROW(t_obj_->setOptionDefList(xpath, defs));
// Get it back.
ConstElementPtr got;
EXPECT_NO_THROW(got = t_obj_->getOptionDefList(xpath));
ASSERT_TRUE(got);
ASSERT_EQ(1, got->size());
EXPECT_TRUE(def->equals(*got->get(0)));
// Check the tree representation.
S_Tree tree;
EXPECT_NO_THROW(tree = sess_->get_subtree("/kea-dhcp6-server:config"));
ASSERT_TRUE(tree);
string expected =
"kea-dhcp6-server:config (container)\n"
" |\n"
" -- option-def-list (container)\n"
" |\n"
" -- option-def (list instance)\n"
" |\n"
" -- code = 100\n"
" |\n"
" -- space = isc\n"
" |\n"
" -- name = foo\n"
" |\n"
" -- type = string\n"
" |\n"
" -- array = false\n";
EXPECT_EQ(expected, tree->to_string(100));
// Check it validates.
EXPECT_NO_THROW(sess_->validate());
}
}; // end of anonymous namespace
......@@ -13,66 +13,66 @@
namespace isc {
namespace yang {
// Option data translation between YANG and 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
//
// 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
//
// An example in JSON and YANG formats:
// @code
// [
// {
// "code": 100,
// "space": "dns",
// "csv-format": false,
// "data": "12121212",
// "always-send": false
// }
// ]
// @endcode
// @code
// /kea-dhcp6-server:config (container)
// /kea-dhcp6-server:config/option-data-list (container)
// /kea-dhcp6-server:config/option-data-list/
// option-data[code='100'][space='dns'] (list instance)
// /kea-dhcp6-server:config/option-data-list/
// option-data[code='100'][space='dns']/code = 100
// /kea-dhcp6-server:config/option-data-list/
// option-data[code='100'][space='dns']/space = dns
// /kea-dhcp6-server:config/option-data-list/
// option-data[code='100'][space='dns']/data = 12121212
// /kea-dhcp6-server:config/option-data-list/
// option-data[code='100'][space='dns']/csv-format = false
// /kea-dhcp6-server:config/option-data-list/
// option-data[code='100'][space='dns']/always-send = false
// @endcode
// @brief A translator class for converting an option data between
// YANG and JSON.
//
// Currently supports on kea-dhcp[46]-server, not yet ietf-dhcpv6-server.
/// Option data translation between YANG and 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
///
/// 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
///
/// An example in JSON and YANG formats:
/// @code
/// [
/// {
/// "code": 100,
/// "space": "dns",
/// "csv-format": false,
/// "data": "12121212",
/// "always-send": false
/// }
/// ]
/// @endcode
/// @code
/// /kea-dhcp6-server:config (container)
/// /kea-dhcp6-server:config/option-data-list (container)
/// /kea-dhcp6-server:config/option-data-list/
/// option-data[code='100'][space='dns'] (list instance)
/// /kea-dhcp6-server:config/option-data-list/
/// option-data[code='100'][space='dns']/code = 100
/// /kea-dhcp6-server:config/option-data-list/
/// option-data[code='100'][space='dns']/space = dns
/// /kea-dhcp6-server:config/option-data-list/
/// option-data[code='100'][space='dns']/data = 12121212
/// /kea-dhcp6-server:config/option-data-list/
/// option-data[code='100'][space='dns']/csv-format = false
/// /kea-dhcp6-server:config/option-data-list/
/// option-data[code='100'][space='dns']/always-send = false
/// @endcode
/// @brief A translator class for converting an option data between
/// YANG and JSON.
///
/// Currently supports on kea-dhcp[46]-server, not yet ietf-dhcpv6-server.
class TranslatorOptionData : virtual public TranslatorBasic {
public:
......@@ -119,10 +119,10 @@ protected:
std::string model_;
};
// @brief A translator class for converting an option data list between
// YANG and JSON.
//
// Currently supports on kea-dhcp[46]-server, not yet ietf-dhcpv6-server.
/// @brief A translator class for converting an option data list between
/// YANG and JSON.
///
/// Currently supports on kea-dhcp[46]-server, not yet ietf-dhcpv6-server.
class TranslatorOptionDataList : virtual public TranslatorOptionData {
public:
......
// 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_def.h>
#include <yang/adaptor.h>
#include <sstream>
using namespace std;
using namespace isc::data;
namespace isc {
namespace yang {
TranslatorOptionDef::TranslatorOptionDef(S_Session session,
const string& model)
: TranslatorBasic(session), model_(model) {
}
TranslatorOptionDef::~TranslatorOptionDef() {
}
ElementPtr
TranslatorOptionDef::getOptionDef(const string& xpath) {
try {
if ((model_ == "kea-dhcp4-server") ||
(model_ == "kea-dhcp6-server")) {
return (getOptionDefKea(xpath));
}
} catch (const sysrepo_exception& ex) {
isc_throw(SysrepoError,
"sysrepo error getting option definition at '" << xpath
<< "': " << ex.what());
}
isc_throw(NotImplemented,
"getOptionDef not implemented for the model: " << model_);
}
ElementPtr
TranslatorOptionDef::getOptionDefKea(const string& xpath) {
ConstElementPtr code = getItem(xpath + "/code");
ConstElementPtr name = getItem(xpath + "/name");
ConstElementPtr type = getItem(xpath + "/type");
ConstElementPtr space = getItem(xpath + "/space");
if (!code || !space) {
// Can't happen as code and space are the keys.
isc_throw(Unexpected, "getOptionDefKea requires code and space: "
<< xpath);
}
if (!name || !type) {
isc_throw(BadValue, "getOptionDefKea requires name and type: "
<< xpath);
}
ElementPtr result = Element::createMap();
result->set("code", code);
result->set("name", name);
result->set("type", type);
result->set("space", getItem(xpath + "/space"));
ConstElementPtr record = getItem(xpath + "/record-types");
if (record) {
result->set("record-types", record);
}
ConstElementPtr array = getItem(xpath + "/array");
if (array) {
result->set("array", array);
}
ConstElementPtr encapsulate = getItem(xpath + "/encapsulate");
if (encapsulate) {
result->set("encapsulate", encapsulate);
}
ConstElementPtr context = getItem(xpath + "/user-context");
if (context) {
result->set("user-context", Element::fromJSON(context->stringValue()));
}
return (result);
}
void
TranslatorOptionDef::setOptionDef(const string& xpath, ConstElementPtr elem) {
try {
if ((model_ == "kea-dhcp4-server") ||
(model_ == "kea-dhcp6-server")) {
setOptionDefKea(xpath, elem);
} else {
isc_throw(NotImplemented,
"setOptionDef not implemented for the model: "
<< model_);
}
} catch (const sysrepo_exception& ex) {
isc_throw(SysrepoError,
"sysrepo error setting option definition '" << elem->str()
<< "' at '" << xpath << "': " << ex.what());
}
}
void
TranslatorOptionDef::setOptionDefKea(const string& xpath,
ConstElementPtr elem) {
// Skip code and space as they are the keys.
ConstElementPtr name = elem->get("name");
if (!name) {
isc_throw(BadValue, "option definition with name: " << elem->str());
}
setItem(xpath + "/name", name, SR_STRING_T);
ConstElementPtr type = elem->get("type");
if (!type) {
isc_throw(BadValue, "option definition with type: " << elem->str());
}
setItem(xpath + "/type", type, SR_STRING_T);
ConstElementPtr record = elem->get("record-types");
if (record) {
setItem(xpath + "/record-types", record, SR_STRING_T);
}
ConstElementPtr array = elem->get("array");
if (array) {
setItem(xpath + "/array", array, SR_BOOL_T);
}
ConstElementPtr encapsulate = elem->get("encapsulate");
if (encapsulate) {
setItem(xpath + "/encapsulate", encapsulate, SR_STRING_T);
}
ConstElementPtr context = Adaptor::getContext(elem);
if (context) {
setItem(xpath + "/user-context", Element::create(context->str()),
SR_STRING_T);
}
}
TranslatorOptionDefList::TranslatorOptionDefList(S_Session session,
const string& model)
: TranslatorBasic(session), TranslatorOptionDef(session, model),
model_(model) {
}
TranslatorOptionDefList::~TranslatorOptionDefList() {
}
ConstElementPtr
TranslatorOptionDefList::getOptionDefList(const string& xpath) {
try {
if ((model_ == "kea-dhcp4-server") ||
(model_ == "kea-dhcp6-server")) {
return (getOptionDefListKea(xpath));
}
} catch (const sysrepo_exception& ex) {
isc_throw(SysrepoError,
"sysrepo error getting option definition list at '" << xpath
<< "': " << ex.what());
}
isc_throw(NotImplemented,
"getOptionDefList not implemented for the model: " << model_);
}
ConstElementPtr
TranslatorOptionDefList::getOptionDefListKea(const string& xpath) {
ElementPtr result = Element::createList();
S_Iter_Value iter = getIter(xpath + "/*");
if (!iter) {
// Can't happen.
isc_throw(Unexpected, "getOptionDefListKea: can't get iterator: "
<< xpath);
}
for (;;) {
const string& def = getNext(iter);
if (def.empty()) {
break;
}
result->add(getOptionDef(def));
}
return (result);
}
void
TranslatorOptionDefList::setOptionDefList(const string& xpath,
ConstElementPtr elem) {
try {
if ((model_ == "kea-dhcp4-server") ||
(model_ == "kea-dhcp6-server")) {
setOptionDefListKea(xpath, elem);
} else {
isc_throw(NotImplemented,
"setOptionDefList not implemented for the model: "
<< model_);
}
} catch (const sysrepo_exception& ex) {
isc_throw(SysrepoError,
"sysrepo error setting option definition list '"
<< elem->str() << "' at '" << xpath << "': " << ex.what());
}
}
void
TranslatorOptionDefList::setOptionDefListKea(const string& xpath,
ConstElementPtr elem) {
for (size_t i = 0; i < elem->size(); ++i) {
ConstElementPtr def = elem->get(i);
if (!def->contains("code")) {
isc_throw(BadValue,
"option definition without code: " << def->str());
}
unsigned code = static_cast<unsigned>(def->get("code")->intValue());
if (!def->contains("space")) {
isc_throw(BadValue,
"option definition without space: " << def->str());
}
string space = def->get("space")->stringValue();
ostringstream keys;
keys << xpath << "/option-def[code='" << code
<< "'][space='" << space << "']";
setOptionDef(keys.str(), def);
}
}
}; // end of namespace isc::yang
}; // end of namespace isc
// 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_DEF_H
#define ISC_TRANSLATOR_OPTION_DEF_H 1
#include <yang/translator.h>
#include <list>
namespace isc {
namespace yang {
/// @brief Option definition translation between YANG and JSON
///
/// JSON syntax for Kea DHCP servers is:
/// @code
/// {
/// "code": <code>,
/// "name": <name>,
/// "space": <space>,
/// "type": <type>,
/// "array": <array flag>,
/// "encapsulate": <encapsulated space>,
/// "record-types": <record types>,
/// "user-context": { <json map> },
/// "comment": "<comment>"
/// }
/// @endcode
///
/// YANG syntax for kea-dhcp[46] with code and space as keys is:
/// @code
/// +--rw name string
/// +--rw code uint8 / uint16
/// +--rw type string
/// +--rw record-types? string
/// +--rw space string
/// +--rw encapsulate? string
/// +--rw array? boolean
/// +--rw user-context? string
/// @endcode
///
/// An example in JSON and YANG formats:
/// @code
/// [
/// {
/// "code": 100,
/// "name": "foo",
/// "space": "isc",
/// "type": "string",
/// "array": false
/// }
/// ]
/// @endcode
/// @code
/// /kea-dhcp6-server:config (container)
/// /kea-dhcp6-server:config/option-def-list (container)
/// /kea-dhcp6-server:config/option-def-list/
/// option-def[code='100'][space='isc'] (list instance)
/// /kea-dhcp6-server:config/option-def-list/
/// option-def[code='100'][space='isc']/code = 100
/// /kea-dhcp6-server:config/option-def-list/
/// option-def[code='100'][space='isc']/space = isc
/// /kea-dhcp6-server:config/option-def-list/
/// option-def[code='100'][space='isc']/name = foo
/// /kea-dhcp6-server:config/option-def-list/
/// option-def[code='100'][space='isc']/type = string
/// /kea-dhcp6-server:config/option-def-list/
/// option-def[code='100'][space='isc']/array = false
/// @endcode
/// @brief A translator class for converting an option definition between
/// YANG and JSON.
///
/// Currently supports kea-dhcp[46]-server models.
/// @todo: Support for ietf-dhcpv6-server model.
class TranslatorOptionDef : virtual public TranslatorBasic {
public:
/// @brief Constructor.
///
/// @param session Sysrepo session.
/// @param model Model name.
TranslatorOptionDef(S_Session session, const std::string& model);
/// @brief Destructor.
virtual ~TranslatorOptionDef();