Commit 0eee9f99 authored by Francis Dupont's avatar Francis Dupont

[65-libyang-option-data_rebased] Rebased before merge

parents 5bb9d24e 2fec3c3c
......@@ -1606,6 +1606,7 @@ AC_CONFIG_FILES([Makefile
src/lib/util/threads/tests/Makefile
src/lib/util/unittests/Makefile
src/lib/yang/Makefile
src/lib/yang/pretests/Makefile
src/lib/yang/tests/Makefile
src/share/Makefile
src/share/database/Makefile
......
SUBDIRS = . tests
SUBDIRS = . pretests tests
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES) $(SYSREPO_CPPFLAGS)
......@@ -8,6 +8,8 @@ lib_LTLIBRARIES = libkea-yang.la
libkea_yang_la_SOURCES = adaptor.cc adaptor.h
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
......@@ -24,7 +26,8 @@ libkea_yang_includedir = $(pkgincludedir)/yang
libkea_yang_include_HEADERS = \
adaptor.h \
sysrepo_error.h \
translator.h
translator.h \
translator_option_data.h
EXTRA_DIST = yang.dox
......
......@@ -21,10 +21,10 @@ public:
{}
};
/// @brief JSON adaptor between canonical Kea and Yang models.
/// @brief JSON adaptor between canonical Kea and YANG models.
///
/// An adaptor slightly modifies a JSON configuration between canonical Kea
/// what required or rendered by a Yang model, e.g. moving a parameter
/// what required or rendered by a YANG model, e.g. moving a parameter
/// to/from a parent.
/// The basic adaptor provides a set of tools.
class Adaptor {
......
CLEANFILES = *.gcno *.gcda
TESTS = sysrepo_setup_tests
sysrepo_setup_tests_SOURCES = sysrepo_setup_tests.cc
sysrepo_setup_tests_CPPFLAGS = $(SYSREPO_CPPFLAGS)
sysrepo_setup_tests_LDFLAGS = $(AM_LDFLAGS)
sysrepo_setup_tests_LDADD = $(SYSREPO_LIBS)
noinst_PROGRAMS = $(TESTS)
// 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 <sysrepo-cpp/Session.h>
#include <sstream>
using namespace std;
const string TEST_MODULE = "keatest-module";
const string IETF_TYPES = "ietf-inet-types";
const string YANG_TYPES = "ietf-yang-types";
const string IETF_MODULE = "ietf-dhcpv6-server";
const string KEA_DHCP4_MODULE = "kea-dhcp4-server";
const string KEA_DHCP6_MODULE = "kea-dhcp6-server";
const string KEA_CTRL_AGENT_MODULE = "kea-ctrl-agent";
const string KEA_D2_MODULE = "kea-dhcp-ddns";
// const string REPOSITORY = SYSREPO_REPO;
const string REPOSITORY = "<sysrepo repository>";
/// @brief Returns nicely formed error message if module is missing
///
/// @param name name of the YANG module to complain about
/// @return a text explaining what the problem is and how to fix it
string missingModuleText(const string& name) {
stringstream tmp;
tmp << "ERROR: YANG model " << name << " is not installed." << endl
<< "The environment is not suitable for running unit-tests." << endl
<< "Please locate " << name << ".yang, change to its directory and "
<< "issue the following command:" << endl << endl
<< "# sysrepoctl -i -s " << REPOSITORY << "/yang "
<< "-s . -g " << name << ".yang" << endl
<< endl << endl;
return (tmp.str());
}
/// @brief Checks sysrepo setup:
/// - connection establishment
/// - daemon required
/// - session establishment
/// - test module
/// - type modules
/// - IETF module
/// - Kea modules.
int main() {
S_Connection conn;
try {
conn.reset(new Connection("sysrepo setup check"));
} catch (const sysrepo_exception& ex) {
cerr << "ERROR: Can't connect to sysrepo: " << ex.what() << endl;
exit(-1);
}
try {
conn.reset(new Connection("sysrepo setup check",
SR_CONN_DAEMON_REQUIRED));
} catch (const sysrepo_exception& ex) {
cerr <<"ERROR: Can't connect to sysrepo daemon: " <<ex.what() << endl
<< endl
<< "Sysrepo daemon is required or actions will be local to "
<< "the local library instance." << endl;
exit(-2);
}
S_Session sess;
try {
sess.reset(new Session(conn, SR_DS_CANDIDATE));
} catch (const sysrepo_exception& ex) {
cerr << "ERROR: Can't establish a sysrepo session: "
<< ex.what() << endl;
exit(-3);
}
S_Yang_Schemas schemas;
try {
schemas = sess->list_schemas();
} catch (const sysrepo_exception& ex) {
cerr << "ERROR: Can't list available schemas: " << ex.what() << endl;
exit(-4);
}
bool found_test = false;
bool found_ietf_types = false;
bool found_yang_types =false;
bool found_ietf = false;
bool found_kea4 = false;
bool found_kea6 = false;
bool found_keaca = false;
bool found_kea2 = false;
for (size_t i = 0; i < schemas->schema_cnt(); ++i) {
string module = schemas->schema(i)->module_name();
size_t rev = module.find("@");
if (rev != string::npos) {
module = module.substr(0, rev);
}
if (module == TEST_MODULE) {
found_test = true;
} else if (module == IETF_TYPES) {
found_ietf_types = true;
} else if (module == YANG_TYPES) {
found_yang_types = true;
} else if (module == IETF_MODULE) {
found_ietf = true;
} else if (module == KEA_DHCP4_MODULE) {
found_kea4 = true;
} else if (module == KEA_DHCP6_MODULE) {
found_kea6 = true;
} else if (module == KEA_CTRL_AGENT_MODULE) {
found_keaca = true;
} else if (module == KEA_D2_MODULE) {
found_kea2 = true;
}
}
int exit_code = 0;
if (!found_test || !found_ietf_types || !found_yang_types ||
!found_ietf || !found_kea4 || !found_kea6 || !found_keaca ||
!found_kea2) {
exit_code = 4;
}
if (!found_test) {
cerr << missingModuleText(TEST_MODULE);
--exit_code;
}
if (!found_ietf_types) {
cerr << missingModuleText(IETF_TYPES);
--exit_code;
}
if (!found_yang_types) {
cerr << missingModuleText(YANG_TYPES);
--exit_code;
}
if (!found_ietf) {
cerr << missingModuleText(IETF_MODULE);
--exit_code;
}
if (!found_kea4) {
cerr << missingModuleText(KEA_DHCP4_MODULE);
--exit_code;
}
if (!found_kea6) {
cerr << missingModuleText(KEA_DHCP6_MODULE);
--exit_code;
}
if (!found_keaca) {
cerr << missingModuleText(KEA_CTRL_AGENT_MODULE);
--exit_code;
}
if (!found_kea2) {
cerr << missingModuleText(KEA_D2_MODULE);
--exit_code;
}
exit(exit_code);
}
......@@ -18,7 +18,9 @@ TESTS =
if HAVE_GTEST
TESTS += run_unittests
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 += 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/.
#ifndef SYSREPO_SETUP_H
#define SYSREPO_SETUP_H
#include <config.h>
#include <yang/translator.h>
#include <gtest/gtest.h>
namespace isc {
namespace yang {
namespace test {
/// @brief Test Fixture template for translator tests.
///
/// @tparam Name The name of the translator to test.
/// @tparam Type The type of the translator to test.
template<char const* Name, typename Type>
class GenericTranslatorTest : public ::testing::Test {
public:
/// @brief Constructor.
GenericTranslatorTest() : conn_(), sess_(), t_obj_() { }
/// @brief useModel
///
/// Open a sysrepo session and create a translator object using
/// the given model.
///
/// @param model The model to use.
void useModel(std::string model) {
std::string full_name =
"translator " + std::string(Name) + " unittests";
conn_.reset(new Connection(full_name.c_str()));
sess_.reset(new Session(conn_, SR_DS_CANDIDATE));
EXPECT_NO_THROW(t_obj_.reset(new Type(sess_, model)));
}
/// @brief Destructor.
///
/// Destroy all objects.
virtual ~GenericTranslatorTest() {
t_obj_.reset();
sess_.reset();
conn_.reset();
}
/// @brief Sysrepo connection.
S_Connection conn_;
/// @brief Sysrepo session.
S_Session sess_;
/// @brief Shared pointer to the transaction object.
boost::shared_ptr<Type> t_obj_;
};
} // namespace test
} // namespace yang
} // namespace isc
#endif // SYSREPO_SETUP_H
// 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 <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_data_list[] = "option data list";
/// @brief Test fixture class for @ref TranslatorOptionDataList.
class TranslatorOptionDataListTest :
public GenericTranslatorTest<option_data_list, TranslatorOptionDataList> {
public:
/// Constructor.
TranslatorOptionDataListTest() { }
/// Destructor (does nothing).
virtual ~TranslatorOptionDataListTest() { }
};
// This test verifies that an empty option data list can be properly
// translated from YANG to JSON.
TEST_F(TranslatorOptionDataListTest, getEmpty) {
useModel("kea-dhcp4-server");
// Get the option data list and checks it is empty.
const string& xpath = "/kea-dhcp4-server:config/option-data-list";
ConstElementPtr options;
EXPECT_NO_THROW(options = t_obj_->getOptionDataList(xpath));
ASSERT_TRUE(options);
ASSERT_EQ(Element::list, options->getType());
EXPECT_EQ(0, options->size());
}
// This test verifies that one option data can be properly translated
// from YANG to JSON.
TEST_F(TranslatorOptionDataListTest, get) {
useModel("kea-dhcp6-server");
// Create the option code 100.
const string& xpath = "/kea-dhcp6-server: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 = t_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 = t_obj_->getOptionDataList(xpath));
ASSERT_TRUE(options);
ASSERT_EQ(Element::list, options->getType());
EXPECT_EQ(1, options->size());
EXPECT_TRUE(option->equals(*options->get(0)));
}
// This test verifies that an empty option data list can be properly
// translated from JSON to YANG.
TEST_F(TranslatorOptionDataListTest, setEmpty) {
useModel("kea-dhcp4-server");
// Set empty list.
const string& xpath = "/kea-dhcp4-server:config/option-data-list";
ConstElementPtr options = Element::createList();
EXPECT_NO_THROW(t_obj_->setOptionDataList(xpath, options));
// Get it back.
options.reset();
EXPECT_NO_THROW(options = t_obj_->getOptionDataList(xpath));
ASSERT_TRUE(options);
EXPECT_EQ(0, options->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 data can be properly translated
// from JSON to YANG.
TEST_F(TranslatorOptionDataListTest, set) {
useModel("kea-dhcp6-server");
// Set one option data.
const string& xpath = "/kea-dhcp6-server: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(t_obj_->setOptionDataList(xpath, options));
// Get it back.
ConstElementPtr got;
EXPECT_NO_THROW(got = t_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-server:config"));
ASSERT_TRUE(tree);
string expected =
"kea-dhcp6-server: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));
// Check it validates.
EXPECT_NO_THROW(sess_->validate());
}
}; // end of anonymous namespace
......@@ -18,56 +18,6 @@ using namespace isc::yang;
namespace {
const std::string TEST_MODULE="keatest-module";
/// @brief checks if specified schema is installed and available in sysrepo
///
/// @name name of the schema to be checked (without .yang)
/// @verbose print installed schemas?
/// @return true if installed, false otherwise.
bool schemaInstalled(const std::string& name, bool verbose = false) {
// Get a connection.
S_Connection conn(new Connection("translator unittests"));
// Get a session.
S_Session sess(new Session(conn, SR_DS_CANDIDATE));
S_Yang_Schemas schemas = sess->list_schemas();
size_t schema_cnt = schemas->schema_cnt();
if (verbose) {
cout << "There are " << schema_cnt << " YANG schema(s) installed:" << endl;
}
bool found = false;
for (int i = 0; i < schema_cnt; i++) {
string installed_name(schemas->schema(i)->module_name());
if (installed_name == name) {
found = true;
}
if (verbose) {
std::cout << "Schema " << i << ": " << installed_name << endl;
}
}
return (found);
}
// This test verifies if the test schema is installed and accessible.
TEST(TranslatorBasicTest, environmentCheck1) {
EXPECT_TRUE(schemaInstalled(TEST_MODULE))
<< "\nERROR: Module used in unit-tests " << TEST_MODULE
<< " is not installed. The environment is not suitable for\n"
<< "ERROR: running unit-tests. Please locate " << TEST_MODULE <<".yang "
<< "and issue the following command:\n"
<< "ERROR: sysrepoctl --install --yang=" << TEST_MODULE << ".yang\n"
<< "ERROR:\n"
<< "ERROR: Following tests will most likely fail.\n";
}
// Test constructor.
TEST(TranslatorBasicTest, constructor) {
// Get a connection.
......
......@@ -15,7 +15,7 @@
namespace isc {
namespace yang {
/// @brief Between Yang and JSON translator class for basic values.
/// @brief Between YANG and JSON translator class for basic values.
class TranslatorBasic {
public:
......@@ -27,7 +27,7 @@ public:
/// @brief Destructor.
virtual ~TranslatorBasic();
/// @brief Translate basic value from Yang to JSON.
/// @brief Translate basic value from YANG to JSON.
///
/// @note Please don't use this outside tests.
///
......@@ -36,7 +36,7 @@ public:
/// @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.
/// @brief Get and translate basic value from YANG to JSON.
///
/// @note Should be const as it is read only...
///
......@@ -47,14 +47,14 @@ public:
/// @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.
/// @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.
/// @brief Translate basic value from JSON to YANG.
///
/// @note Please don't use this outside tests.
///
......@@ -62,7 +62,7 @@ public:
/// @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.
/// @brief Translate and set basic value from JSON to YANG.
///
/// @param xpath The xpath of the basic value.
/// @param elem The JSON element.
......@@ -70,20 +70,20 @@ public:
void setItem(const std::string& xpath, isc::data::ConstElementPtr elem,
sr_type_t type);
/// @brief Delete basic value from Yang.
/// @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.
/// @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.
/// @brief Get xpath of the next YANG list item.
///
/// @param iter The iterator pointing to the previous element
/// @return The xpath of the next element. Empty string when at the end of the list.
......
// 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-server") ||
(model_ == "kea-dhcp6-server")) {
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