summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrei Pavel <andrei@isc.org>2024-01-10 10:20:26 +0100
committerAndrei Pavel <andrei@isc.org>2024-01-26 11:48:18 +0100
commit11de31d8bab2b34e5587c87b1bbe146c8a7fc223 (patch)
treeab7dd0a28a7440d4dc0d7defd5c2795de452e8ac
parent[#3198] add data as key for option-data in YANG modules (diff)
downloadkea-11de31d8bab2b34e5587c87b1bbe146c8a7fc223.tar.xz
kea-11de31d8bab2b34e5587c87b1bbe146c8a7fc223.zip
[#3198] make data a key for option-data in code
- Add ability to set list element that only has keys in Translator::setItem. - Explicitly set list elements in case they contain only keys which can be more common now that data is a key since it is likely one can have entries that only have code, space, and data. - Handle no data as empty data when setting, and empty data as no data when getting. This avoids the need to add an empty "data" element to all options that lack it in all-options.json so that the unit tests pass. But this goes to show that data-less entries may be encountered in production as well, so more importantly this caters to that scenario. - Adjust data in kea4/all-options.json to not contain singlequotes. There was only one occurrence of it. This is a limitation related to unit testing only. Opened issue 3216 about it. - Add missing tests that are not strictly related to the data key, but they are related to option data: - TranslatorOptionDataListTestv6.getEmpty - TranslatorOptionDataListTestv4.get - TranslatorOptionDataListTestv6.setEmpty - TranslatorOptionDataListTestv4.set - Add unit tests: - TranslatorOptionDataListTestv4.optionsSameCodeAndSpace - TranslatorOptionDataListTestv6.optionsSameCodeAndSpace - Add snippet that tests setting of list element with keys only in TranslatorTest.setItem.
-rw-r--r--doc/examples/kea4/all-options.json2
-rw-r--r--src/lib/yang/tests/translator_option_data_unittests.cc253
-rw-r--r--src/lib/yang/tests/translator_unittests.cc14
-rw-r--r--src/lib/yang/tests/yang_configs.h32
-rw-r--r--src/lib/yang/translator.cc6
-rw-r--r--src/lib/yang/translator.h5
-rw-r--r--src/lib/yang/translator_host.cc6
-rw-r--r--src/lib/yang/translator_logger.cc6
-rw-r--r--src/lib/yang/translator_option_data.cc32
-rw-r--r--src/lib/yang/translator_option_data.h14
-rw-r--r--src/lib/yang/translator_option_def.cc6
-rw-r--r--src/lib/yang/translator_pool.cc6
-rw-r--r--src/lib/yang/translator_shared_network.cc6
-rw-r--r--src/lib/yang/translator_subnet.cc12
14 files changed, 348 insertions, 52 deletions
diff --git a/doc/examples/kea4/all-options.json b/doc/examples/kea4/all-options.json
index a997a5ce16..76f640ab2b 100644
--- a/doc/examples/kea4/all-options.json
+++ b/doc/examples/kea4/all-options.json
@@ -689,7 +689,7 @@
// Type: string
{
"code": 56,
- "data": "Error: here's a DHCPNAK!",
+ "data": "Error: here is a DHCPNAK!",
"name": "dhcp-message"
},
diff --git a/src/lib/yang/tests/translator_option_data_unittests.cc b/src/lib/yang/tests/translator_option_data_unittests.cc
index 3917e26f0f..ae69616607 100644
--- a/src/lib/yang/tests/translator_option_data_unittests.cc
+++ b/src/lib/yang/tests/translator_option_data_unittests.cc
@@ -12,6 +12,8 @@
#include <yang/translator_option_data.h>
#include <yang/yang_models.h>
+#include <sysrepo-cpp/utils/exception.hpp>
+
using namespace std;
using namespace isc;
using namespace isc::data;
@@ -44,7 +46,7 @@ public:
}; // TranslatorOptionDataListTestv6
// This test verifies that an empty option data list can be properly
-// translated from YANG to JSON.
+// translated from YANG to JSON for v4.
TEST_F(TranslatorOptionDataListTestv4, getEmpty) {
// Get the option data list and check if it is empty.
const string& xpath = "/kea-dhcp4-server:config";
@@ -53,20 +55,65 @@ TEST_F(TranslatorOptionDataListTestv4, getEmpty) {
ASSERT_FALSE(options);
}
+// This test verifies that an empty option data list can be properly
+// translated from YANG to JSON for v6.
+TEST_F(TranslatorOptionDataListTestv6, getEmpty) {
+ // Get the option data list and check if it is empty.
+ const string& xpath = "/kea-dhcp6-server:config";
+ ConstElementPtr options;
+ EXPECT_NO_THROW_LOG(options = translator_->getOptionDataListFromAbsoluteXpath(xpath));
+ ASSERT_FALSE(options);
+}
+
+// This test verifies that one option data can be properly translated
+// from YANG to JSON for v4.
+TEST_F(TranslatorOptionDataListTestv4, get) {
+ // Create the option code 100.
+ const string& xpath = "/kea-dhcp4-server:config";
+ const string& xoption = xpath + "/option-data[code='100'][space='dns'][data='12121212']";
+ const string& xformat = xoption + "/csv-format";
+ const string& xalways = xoption + "/always-send";
+ const string& xnever = xoption + "/never-send";
+ string const s_false("false");
+ ASSERT_NO_THROW_LOG(sess_->setItem(xformat, s_false));
+ ASSERT_NO_THROW_LOG(sess_->setItem(xalways, s_false));
+ ASSERT_NO_THROW_LOG(sess_->setItem(xnever, s_false));
+ sess_->applyChanges();
+
+ // Get the option data.
+ ConstElementPtr option;
+ EXPECT_NO_THROW_LOG(option = translator_->getOptionDataFromAbsoluteXpath(xoption));
+ ASSERT_TRUE(option);
+ EXPECT_EQ("{"
+ " \"always-send\": false,"
+ " \"code\": 100,"
+ " \"csv-format\": false,"
+ " \"data\": \"12121212\","
+ " \"never-send\": false,"
+ " \"space\": \"dns\""
+ " }",
+ option->str());
+
+ // Get the option data list.
+ ConstElementPtr options;
+ EXPECT_NO_THROW_LOG(options = translator_->getOptionDataListFromAbsoluteXpath(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 one option data can be properly translated
-// from YANG to JSON.
+// from YANG to JSON for v6.
TEST_F(TranslatorOptionDataListTestv6, get) {
// Create the option code 100.
const string& xpath = "/kea-dhcp6-server:config";
- const string& xoption = xpath + "/option-data[code='100'][space='dns']";
+ const string& xoption = xpath + "/option-data[code='100'][space='dns'][data='12121212']";
const string& xformat = xoption + "/csv-format";
- const string& xdata = xoption + "/data";
const string& xalways = xoption + "/always-send";
const string& xnever = xoption + "/never-send";
string const s_false("false");
ASSERT_NO_THROW_LOG(sess_->setItem(xformat, s_false));
- string const s_data("12121212");
- ASSERT_NO_THROW_LOG(sess_->setItem(xdata, s_data));
ASSERT_NO_THROW_LOG(sess_->setItem(xalways, s_false));
ASSERT_NO_THROW_LOG(sess_->setItem(xnever, s_false));
sess_->applyChanges();
@@ -95,7 +142,7 @@ TEST_F(TranslatorOptionDataListTestv6, get) {
}
// This test verifies that an empty option data list can be properly
-// translated from JSON to YANG.
+// translated from JSON to YANG for v4.
TEST_F(TranslatorOptionDataListTestv4, setEmpty) {
// Set empty list.
const string& xpath = "/kea-dhcp4-server:config";
@@ -108,11 +155,91 @@ TEST_F(TranslatorOptionDataListTestv4, setEmpty) {
ASSERT_FALSE(options);
}
+// This test verifies that an empty option data list can be properly
+// translated from JSON to YANG for v6.
+TEST_F(TranslatorOptionDataListTestv6, setEmpty) {
+ // Set empty list.
+ const string& xpath = "/kea-dhcp6-server:config";
+ ConstElementPtr options = Element::createList();
+ EXPECT_NO_THROW_LOG(translator_->setOptionDataList(xpath, options));
+
+ // Get it back.
+ options.reset();
+ EXPECT_NO_THROW_LOG(options = translator_->getOptionDataListFromAbsoluteXpath(xpath));
+ ASSERT_FALSE(options);
+}
+
// This test verifies that one option data can be properly translated
-// from JSON to YANG.
+// from JSON to YANG for v4.
+TEST_F(TranslatorOptionDataListTestv4, set) {
+ // Negative tests.
+ string const xpath("/kea-dhcp4-server:config");
+ string const xoption(xpath + "/option-data[code='100'][space='dns'][data='12121212']");
+ string const s_code("15");
+ string const s_space("dhcp4");
+ string const s_data("12121212");
+ EXPECT_THROW_MSG(sess_->setItem(xoption + "/code", s_code), sysrepo::Error,
+ "Session::setItem: Couldn't set "
+ "'/kea-dhcp4-server:config/option-data[code='100'][space='dns'][data='12121212']/code' to "
+ "'15': SR_ERR_INVAL_ARG");
+ EXPECT_THROW_MSG(sess_->setItem(xoption + "/space", s_space), sysrepo::Error,
+ "Session::setItem: Couldn't set "
+ "'/kea-dhcp4-server:config/option-data[code='100'][space='dns'][data='12121212']/space' to "
+ "'dhcp4': SR_ERR_INVAL_ARG");
+ EXPECT_THROW_MSG(sess_->setItem(xoption + "/data", s_data), sysrepo::Error,
+ "Session::setItem: Couldn't set "
+ "'/kea-dhcp4-server:config/option-data[code='100'][space='dns'][data='12121212']/data' to "
+ "'12121212': SR_ERR_INVAL_ARG");
+
+ // Setting the list element directly should work.
+ EXPECT_NO_THROW_LOG(sess_->setItem(xoption, std::nullopt));
+
+ // Set one option data.
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ option->set("code", Element::create(100));
+ option->set("space", Element::create("dns"));
+ option->set("csv-format", Element::create(false));
+ option->set("data", Element::create("12121212"));
+ option->set("always-send", Element::create(false));
+ option->set("never-send", Element::create(false));
+ options->add(option);
+ EXPECT_NO_THROW_LOG(translator_->setOptionDataList(xpath, options));
+
+ // Get it back.
+ ConstElementPtr got;
+ EXPECT_NO_THROW_LOG(got = translator_->getOptionDataListFromAbsoluteXpath(xpath));
+ ASSERT_TRUE(got);
+ ASSERT_EQ(1, got->size());
+ EXPECT_TRUE(option->equals(*got->get(0)));
+}
+
+// This test verifies that one option data can be properly translated
+// from JSON to YANG for v6.
TEST_F(TranslatorOptionDataListTestv6, set) {
+ // Negative tests.
+ string const xpath("/kea-dhcp6-server:config");
+ string const xoption(xpath + "/option-data[code='100'][space='dns'][data='12121212']");
+ string const s_code("15");
+ string const s_space("dhcp6");
+ string const s_data("12121212");
+ EXPECT_THROW_MSG(sess_->setItem(xoption + "/code", s_code), sysrepo::Error,
+ "Session::setItem: Couldn't set "
+ "'/kea-dhcp6-server:config/option-data[code='100'][space='dns'][data='12121212']/code' to "
+ "'15': SR_ERR_INVAL_ARG");
+ EXPECT_THROW_MSG(sess_->setItem(xoption + "/space", s_space), sysrepo::Error,
+ "Session::setItem: Couldn't set "
+ "'/kea-dhcp6-server:config/option-data[code='100'][space='dns'][data='12121212']/space' to "
+ "'dhcp6': SR_ERR_INVAL_ARG");
+ EXPECT_THROW_MSG(sess_->setItem(xoption + "/data", s_data), sysrepo::Error,
+ "Session::setItem: Couldn't set "
+ "'/kea-dhcp6-server:config/option-data[code='100'][space='dns'][data='12121212']/data' to "
+ "'12121212': SR_ERR_INVAL_ARG");
+
+ // Setting the list element directly should work.
+ EXPECT_NO_THROW_LOG(sess_->setItem(xoption, std::nullopt));
+
// Set one option data.
- const string& xpath = "/kea-dhcp6-server:config";
ElementPtr options = Element::createList();
ElementPtr option = Element::createMap();
option->set("code", Element::create(100));
@@ -132,4 +259,112 @@ TEST_F(TranslatorOptionDataListTestv6, set) {
EXPECT_TRUE(option->equals(*got->get(0)));
}
+// This test verifies that multiple options of smae code and space but different data can be
+// configured for v4.
+TEST_F(TranslatorOptionDataListTestv4, optionsSameCodeAndSpace) {
+ string const xpath("/kea-dhcp4-server:config");
+
+ // Set one option data.
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ option->set("code", Element::create(100));
+ option->set("space", Element::create("dns"));
+ option->set("csv-format", Element::create(false));
+ option->set("data", Element::create("12121212"));
+ option->set("always-send", Element::create(false));
+ option->set("never-send", Element::create(false));
+ options->add(option);
+ option = Element::createMap();
+ option->set("code", Element::create(100));
+ option->set("space", Element::create("dns"));
+ option->set("csv-format", Element::create(false));
+ option->set("data", Element::create("34343434"));
+ option->set("always-send", Element::create(false));
+ option->set("never-send", Element::create(false));
+ options->add(option);
+ EXPECT_NO_THROW_LOG(translator_->setOptionDataList(xpath, options));
+
+ // Get it back.
+ ConstElementPtr got;
+ EXPECT_NO_THROW_LOG(got = translator_->getOptionDataListFromAbsoluteXpath(xpath));
+ ASSERT_TRUE(got);
+ EXPECT_EQ(2, got->size());
+ EXPECT_TRUE(options->equals(*got));
+
+ // Now with keys only.
+ options = Element::createList();
+ option = Element::createMap();
+ option->set("code", Element::create(100));
+ option->set("space", Element::create("dns"));
+ option->set("data", Element::create("56565656"));
+ options->add(option);
+ option = Element::createMap();
+ option->set("code", Element::create(100));
+ option->set("space", Element::create("dns"));
+ option->set("data", Element::create("78787878"));
+ options->add(option);
+ EXPECT_NO_THROW_LOG(translator_->setOptionDataList(xpath, options));
+
+ // Get it back.
+ EXPECT_NO_THROW_LOG(got = translator_->getOptionDataListFromAbsoluteXpath(xpath));
+ ASSERT_TRUE(got);
+ EXPECT_EQ(4, got->size());
+ EXPECT_TRUE(options->get(0)->equals(*got->get(2)));
+ EXPECT_TRUE(options->get(1)->equals(*got->get(3)));
+}
+
+// This test verifies that multiple options of smae code and space but different data can be
+// configured for v6.
+TEST_F(TranslatorOptionDataListTestv6, optionsSameCodeAndSpace) {
+ string const xpath("/kea-dhcp6-server:config");
+
+ // Set one option data.
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ option->set("code", Element::create(100));
+ option->set("space", Element::create("dns"));
+ option->set("csv-format", Element::create(false));
+ option->set("data", Element::create("12121212"));
+ option->set("always-send", Element::create(false));
+ option->set("never-send", Element::create(false));
+ options->add(option);
+ option = Element::createMap();
+ option->set("code", Element::create(100));
+ option->set("space", Element::create("dns"));
+ option->set("csv-format", Element::create(false));
+ option->set("data", Element::create("34343434"));
+ option->set("always-send", Element::create(false));
+ option->set("never-send", Element::create(false));
+ options->add(option);
+ EXPECT_NO_THROW_LOG(translator_->setOptionDataList(xpath, options));
+
+ // Get it back.
+ ConstElementPtr got;
+ EXPECT_NO_THROW_LOG(got = translator_->getOptionDataListFromAbsoluteXpath(xpath));
+ ASSERT_TRUE(got);
+ EXPECT_EQ(2, got->size());
+ EXPECT_TRUE(options->equals(*got));
+
+ // Now with keys only.
+ options = Element::createList();
+ option = Element::createMap();
+ option->set("code", Element::create(100));
+ option->set("space", Element::create("dns"));
+ option->set("data", Element::create("56565656"));
+ options->add(option);
+ option = Element::createMap();
+ option->set("code", Element::create(100));
+ option->set("space", Element::create("dns"));
+ option->set("data", Element::create("78787878"));
+ options->add(option);
+ EXPECT_NO_THROW_LOG(translator_->setOptionDataList(xpath, options));
+
+ // Get it back.
+ EXPECT_NO_THROW_LOG(got = translator_->getOptionDataListFromAbsoluteXpath(xpath));
+ ASSERT_TRUE(got);
+ EXPECT_EQ(4, got->size());
+ EXPECT_TRUE(options->get(0)->equals(*got->get(2)));
+ EXPECT_TRUE(options->get(1)->equals(*got->get(3)));
+}
+
} // namespace
diff --git a/src/lib/yang/tests/translator_unittests.cc b/src/lib/yang/tests/translator_unittests.cc
index 853241902a..441bf3e3f3 100644
--- a/src/lib/yang/tests/translator_unittests.cc
+++ b/src/lib/yang/tests/translator_unittests.cc
@@ -523,9 +523,21 @@ TEST_F(TranslatorTest, setItem) {
ElementPtr element;
string xpath;
+ optional<DataNode> data_node;
+
+ // List.
+ xpath = "/keatest-module:list[key='value']";
+ element = Element::create("value");
+ EXPECT_NO_THROW_LOG(translator->setItem(xpath, ElementPtr(), LeafBaseType::Unknown));
+ EXPECT_NO_THROW_LOG(translator->setItem(xpath, ElementPtr(), LeafBaseType::Unknown));
+ EXPECT_NO_THROW_LOG(data_node = sess.getData(xpath + "/key"));
+ ASSERT_TRUE(data_node);
+ EXPECT_NO_THROW_LOG(data_node = data_node->findPath(xpath + "/key"));
+ ASSERT_TRUE(data_node);
+ ASSERT_EQ(LeafBaseType::String, data_node->schema().asLeaf().valueType().base());
+ EXPECT_EQ(element->stringValue(), string(data_node->asTerm().valueStr()));
// String.
- optional<DataNode> data_node;
xpath = "/keatest-module:main/string";
element = Element::create("str");
EXPECT_NO_THROW_LOG(translator->setItem(xpath, element, LeafBaseType::String));
diff --git a/src/lib/yang/tests/yang_configs.h b/src/lib/yang/tests/yang_configs.h
index d3825c9c24..ac138ff6aa 100644
--- a/src/lib/yang/tests/yang_configs.h
+++ b/src/lib/yang/tests/yang_configs.h
@@ -406,25 +406,25 @@ const YRTree subnetOptionsTreeKeaDhcp4 = YangRepr::buildTreeFromVector({
{ "/kea-dhcp4-server:config/subnet4[id='111']/id",
"111", libyang::LeafBaseType::Uint32, false },
{ "/kea-dhcp4-server:config/subnet4[id='111']/"
- "option-data[code='100'][space='dns']",
+ "option-data[code='100'][space='dns'][data='12121212']",
std::nullopt, libyang::LeafBaseType::Unknown, false },
{ "/kea-dhcp4-server:config/subnet4[id='111']/"
- "option-data[code='100'][space='dns']/code",
+ "option-data[code='100'][space='dns'][data='12121212']/code",
"100", libyang::LeafBaseType::Uint8, false },
{ "/kea-dhcp4-server:config/subnet4[id='111']/"
- "option-data[code='100'][space='dns']/space",
+ "option-data[code='100'][space='dns'][data='12121212']/space",
"dns", libyang::LeafBaseType::String, false },
{ "/kea-dhcp4-server:config/subnet4[id='111']/"
- "option-data[code='100'][space='dns']/data",
- "12121212", libyang::LeafBaseType::String, true },
+ "option-data[code='100'][space='dns'][data='12121212']/data",
+ "12121212", libyang::LeafBaseType::String, false },
{ "/kea-dhcp4-server:config/subnet4[id='111']/"
- "option-data[code='100'][space='dns']/csv-format",
+ "option-data[code='100'][space='dns'][data='12121212']/csv-format",
"false", libyang::LeafBaseType::Bool, true },
{ "/kea-dhcp4-server:config/subnet4[id='111']/"
- "option-data[code='100'][space='dns']/always-send",
+ "option-data[code='100'][space='dns'][data='12121212']/always-send",
"false", libyang::LeafBaseType::Bool, true },
{ "/kea-dhcp4-server:config/subnet4[id='111']/"
- "option-data[code='100'][space='dns']/never-send",
+ "option-data[code='100'][space='dns'][data='12121212']/never-send",
"false", libyang::LeafBaseType::Bool, true },
{ "/kea-dhcp4-server:config/subnet4[id='111']/"
"pool[start-address='10.0.1.0'][end-address='10.0.1.255']",
@@ -485,31 +485,31 @@ const YRTree subnetOptionsTreeKeaDhcp6 = YangRepr::buildTreeFromVector({
"2001:db8::1:0/112", libyang::LeafBaseType::String, true },
{ "/kea-dhcp6-server:config/subnet6[id='111']/"
"pool[start-address='2001:db8::1:0'][end-address='2001:db8::1:ffff']/"
- "option-data[code='100'][space='dns']",
+ "option-data[code='100'][space='dns'][data='12121212']",
std::nullopt, libyang::LeafBaseType::Unknown, false },
{ "/kea-dhcp6-server:config/subnet6[id='111']/"
"pool[start-address='2001:db8::1:0'][end-address='2001:db8::1:ffff']/"
- "option-data[code='100'][space='dns']/code",
+ "option-data[code='100'][space='dns'][data='12121212']/code",
"100", libyang::LeafBaseType::Uint16, false },
{ "/kea-dhcp6-server:config/subnet6[id='111']/"
"pool[start-address='2001:db8::1:0'][end-address='2001:db8::1:ffff']/"
- "option-data[code='100'][space='dns']/space",
+ "option-data[code='100'][space='dns'][data='12121212']/space",
"dns", libyang::LeafBaseType::String, false },
{ "/kea-dhcp6-server:config/subnet6[id='111']/"
"pool[start-address='2001:db8::1:0'][end-address='2001:db8::1:ffff']/"
- "option-data[code='100'][space='dns']/data",
- "12121212", libyang::LeafBaseType::String, true },
+ "option-data[code='100'][space='dns'][data='12121212']/data",
+ "12121212", libyang::LeafBaseType::String, false },
{ "/kea-dhcp6-server:config/subnet6[id='111']/"
"pool[start-address='2001:db8::1:0'][end-address='2001:db8::1:ffff']/"
- "option-data[code='100'][space='dns']/csv-format",
+ "option-data[code='100'][space='dns'][data='12121212']/csv-format",
"false", libyang::LeafBaseType::Bool, true },
{ "/kea-dhcp6-server:config/subnet6[id='111']/"
"pool[start-address='2001:db8::1:0'][end-address='2001:db8::1:ffff']/"
- "option-data[code='100'][space='dns']/always-send",
+ "option-data[code='100'][space='dns'][data='12121212']/always-send",
"false", libyang::LeafBaseType::Bool, true },
{ "/kea-dhcp6-server:config/subnet6[id='111']/"
"pool[start-address='2001:db8::1:0'][end-address='2001:db8::1:ffff']/"
- "option-data[code='100'][space='dns']/never-send",
+ "option-data[code='100'][space='dns'][data='12121212']/never-send",
"false", libyang::LeafBaseType::Bool, true },
{ "/kea-dhcp6-server:config/subnet6[id='111']/subnet",
"2001:db8::/48", libyang::LeafBaseType::String, true },
diff --git a/src/lib/yang/translator.cc b/src/lib/yang/translator.cc
index 9c5ed9ce04..4386b9485d 100644
--- a/src/lib/yang/translator.cc
+++ b/src/lib/yang/translator.cc
@@ -228,7 +228,9 @@ Translator::schemaNodeExists(string const& xpath) const {
}
void
-Translator::setItem(const string& xpath, ConstElementPtr elem, LeafBaseType type) {
+Translator::setItem(const string& xpath,
+ ConstElementPtr const elem,
+ LeafBaseType const type) {
optional<string> const value(translateToYang(elem, type));
try {
session_.setItem(xpath, value);
@@ -346,7 +348,7 @@ Translator::initializeDeserializer() {
for (LeafBaseType const& i :
{LeafBaseType::Bits, LeafBaseType::Empty, LeafBaseType::Enum, LeafBaseType::IdentityRef,
LeafBaseType::InstanceIdentifier, LeafBaseType::Leafref, LeafBaseType::String,
- LeafBaseType::Union, LeafBaseType::Unknown}) {
+ LeafBaseType::Union}) {
result.emplace(i, [](string const& value) -> ElementPtr const {
return Element::create(value);
});
diff --git a/src/lib/yang/translator.h b/src/lib/yang/translator.h
index 5bbaa7d102..98cc2df5c6 100644
--- a/src/lib/yang/translator.h
+++ b/src/lib/yang/translator.h
@@ -11,6 +11,7 @@
#include <yang/netconf_error.h>
#include <sysrepo-cpp/Connection.hpp>
+#include <sysrepo-cpp/Enum.hpp>
#include <sysrepo-cpp/Session.hpp>
#include <unordered_map>
@@ -335,8 +336,8 @@ public:
/// @param elem The JSON element.
/// @param type The sysrepo type.
void setItem(const std::string& xpath,
- isc::data::ConstElementPtr elem,
- libyang::LeafBaseType type);
+ isc::data::ConstElementPtr const elem,
+ libyang::LeafBaseType const type);
/// @brief Get an element from given ElementPtr node and set it in sysrepo
/// at given xpath.
diff --git a/src/lib/yang/translator_host.cc b/src/lib/yang/translator_host.cc
index 7aa6fa407d..451c283e7c 100644
--- a/src/lib/yang/translator_host.cc
+++ b/src/lib/yang/translator_host.cc
@@ -102,7 +102,11 @@ TranslatorHost::setHost(string const& xpath, ConstElementPtr elem) {
void
TranslatorHost::setHostKea(string const& xpath, ConstElementPtr elem) {
- // Skip keys "identifier" and "identifier-type".
+ // Set the list element. This is important in case we have no other elements except the keys.
+ setItem(xpath, ElementPtr(), LeafBaseType::Unknown);
+
+ // Skip keys "identifier" and "identifier-type" since they were set with the
+ // list element in the call above with the LeafBaseType::Unknown parameter.
checkAndSetLeaf(elem, xpath, "hostname", LeafBaseType::String);
diff --git a/src/lib/yang/translator_logger.cc b/src/lib/yang/translator_logger.cc
index 9f845321d0..6c2382999e 100644
--- a/src/lib/yang/translator_logger.cc
+++ b/src/lib/yang/translator_logger.cc
@@ -100,7 +100,11 @@ TranslatorLogger::setLogger(string const& xpath, ConstElementPtr elem) {
void
TranslatorLogger::setLoggerKea(string const& xpath, ConstElementPtr elem) {
- // Skip key "name".
+ // Set the list element. This is important in case we have no other elements except the key.
+ setItem(xpath, ElementPtr(), LeafBaseType::Unknown);
+
+ // Skip key "name" since it was set with the list element in the call above
+ // with the LeafBaseType::Unknown parameter.
checkAndSetLeaf(elem, xpath, "debuglevel", LeafBaseType::Uint8);
checkAndSetLeaf(elem, xpath, "severity", LeafBaseType::Enum);
diff --git a/src/lib/yang/translator_option_data.cc b/src/lib/yang/translator_option_data.cc
index 5826869f2b..7d6e153116 100644
--- a/src/lib/yang/translator_option_data.cc
+++ b/src/lib/yang/translator_option_data.cc
@@ -53,12 +53,18 @@ ElementPtr
TranslatorOptionData::getOptionDataKea(DataNode const& data_node) {
ElementPtr result = Element::createMap();
+ // Code and space must exist.
getMandatoryLeaf(result, data_node, "code");
getMandatoryLeaf(result, data_node, "space");
+ // Data must exist according to the YANG module too, but empty data is considered no data.
+ ElementPtr const& x(getItem(data_node, "data"));
+ if (x && !x->stringValue().empty()) {
+ result->set("data", x);
+ }
+
checkAndGetLeaf(result, data_node, "always-send");
checkAndGetLeaf(result, data_node, "csv-format");
- checkAndGetLeaf(result, data_node, "data");
checkAndGetLeaf(result, data_node, "name");
checkAndGetLeaf(result, data_node, "never-send");
@@ -89,11 +95,14 @@ TranslatorOptionData::setOptionData(string const& xpath,
void
TranslatorOptionData::setOptionDataKea(string const& xpath,
ConstElementPtr elem) {
- // Skip keys "code" and "space".
+ // Set the list element. This is important in case we have no other elements except the keys.
+ setItem(xpath, ElementPtr(), LeafBaseType::Unknown);
+
+ // Skip keys "code", "space", and "data" since they were set with the
+ // list element in the call above with the LeafBaseType::Unknown parameter.
checkAndSetLeaf(elem, xpath, "always-send", LeafBaseType::Bool);
checkAndSetLeaf(elem, xpath, "csv-format", LeafBaseType::Bool);
- checkAndSetLeaf(elem, xpath, "data", LeafBaseType::String);
checkAndSetLeaf(elem, xpath, "name", LeafBaseType::String);
checkAndSetLeaf(elem, xpath, "never-send", LeafBaseType::Bool);
@@ -161,17 +170,28 @@ TranslatorOptionDataList::setOptionDataListKea(string const& xpath,
ConstElementPtr elem) {
for (size_t i = 0; i < elem->size(); ++i) {
ElementPtr option = elem->getNonConst(i);
+
+ // Code and space must exist in the input.
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());
+ isc_throw(BadValue, "option data without space: " << option->str());
}
string space = option->get("space")->stringValue();
+
+ // Data must exist according to the YANG module too, but no data in the input is allowed and
+ // converted to an empty string.
+ string data;
+ if (option->contains("data")) {
+ data = option->get("data")->stringValue();
+ }
+
ostringstream keys;
- keys << xpath << "/option-data[code='" << code
- << "'][space='" << space << "']";
+ keys << xpath << "/option-data[code='" << code <<
+ "'][space='" << space <<
+ "'][data='" << data << "']";
setOptionData(keys.str(), option);
}
}
diff --git a/src/lib/yang/translator_option_data.h b/src/lib/yang/translator_option_data.h
index d8d4f6039e..bf11249f71 100644
--- a/src/lib/yang/translator_option_data.h
+++ b/src/lib/yang/translator_option_data.h
@@ -58,19 +58,17 @@ namespace yang {
/// @code
/// /kea-dhcp6-server:config (container)
/// /kea-dhcp6-server:config/
-/// option-data[code='100'][space='dns'] (list instance)
+/// option-data[code='100'][space='dns'][data='12121212'] (list instance)
/// /kea-dhcp6-server:config/
-/// option-data[code='100'][space='dns']/code = 100
+/// option-data[code='100'][space='dns'][data='12121212']/code = 100
/// /kea-dhcp6-server:config/
-/// option-data[code='100'][space='dns']/space = dns
+/// option-data[code='100'][space='dns'][data='12121212']/space = dns
/// /kea-dhcp6-server:config/
-/// option-data[code='100'][space='dns']/data = 12121212
+/// option-data[code='100'][space='dns'][data='12121212']/csv-format = false
/// /kea-dhcp6-server:config/
-/// option-data[code='100'][space='dns']/csv-format = false
+/// option-data[code='100'][space='dns'][data='12121212']/always-send = false
/// /kea-dhcp6-server:config/
-/// option-data[code='100'][space='dns']/always-send = false
-/// /kea-dhcp6-server:config/
-/// option-data[code='100'][space='dns']/never-send = false
+/// option-data[code='100'][space='dns'][data='12121212']/never-send = false
/// @endcode
/// @brief A translator class for converting an option data between
diff --git a/src/lib/yang/translator_option_def.cc b/src/lib/yang/translator_option_def.cc
index 70bdd9f1d1..6a82d51980 100644
--- a/src/lib/yang/translator_option_def.cc
+++ b/src/lib/yang/translator_option_def.cc
@@ -88,7 +88,11 @@ TranslatorOptionDef::setOptionDef(string const& xpath, ConstElementPtr elem) {
void
TranslatorOptionDef::setOptionDefKea(string const& xpath,
ConstElementPtr elem) {
- // Skip keys "code" and "space".
+ // Set the list element. This is important in case we have no other elements except the keys.
+ setItem(xpath, ElementPtr(), LeafBaseType::Unknown);
+
+ // Skip keys "code" and "space" since they were set with the
+ // list element in the call above with the LeafBaseType::Unknown parameter.
setMandatoryLeaf(elem, xpath, "name", LeafBaseType::String);
setMandatoryLeaf(elem, xpath, "type", LeafBaseType::String);
diff --git a/src/lib/yang/translator_pool.cc b/src/lib/yang/translator_pool.cc
index f1bb8a3fc1..48d6ba6353 100644
--- a/src/lib/yang/translator_pool.cc
+++ b/src/lib/yang/translator_pool.cc
@@ -176,7 +176,11 @@ TranslatorPool::setPoolIetf6(string const& xpath, ConstElementPtr elem) {
void
TranslatorPool::setPoolKea(string const& xpath, ConstElementPtr elem) {
- // Skip keys "start-address" and "end-address".
+ // Set the list element. This is important in case we have no other elements except the keys.
+ setItem(xpath, ElementPtr(), LeafBaseType::Unknown);
+
+ // Skip keys "start-address" and "end-address" since they were set with the
+ // list element in the call above with the LeafBaseType::Unknown parameter.
ConstElementPtr pool = elem->get("pool");
if (!pool) {
diff --git a/src/lib/yang/translator_shared_network.cc b/src/lib/yang/translator_shared_network.cc
index 00044969d7..e9ade22f7a 100644
--- a/src/lib/yang/translator_shared_network.cc
+++ b/src/lib/yang/translator_shared_network.cc
@@ -160,7 +160,11 @@ void
TranslatorSharedNetwork::setSharedNetworkKea(string const& xpath,
ConstElementPtr elem,
string const& subsel) {
- // Skip key "name".
+ // Set the list element. This is important in case we have no other elements except the key.
+ setItem(xpath, ElementPtr(), LeafBaseType::Unknown);
+
+ // Skip key "name" since it was set with the list element in the call above
+ // with the LeafBaseType::Unknown parameter.
checkAndSetLeaf(elem, xpath, "allocator", LeafBaseType::String);
checkAndSetLeaf(elem, xpath, "cache-max-age", LeafBaseType::Uint32);
diff --git a/src/lib/yang/translator_subnet.cc b/src/lib/yang/translator_subnet.cc
index 438895ae86..c89ac2229e 100644
--- a/src/lib/yang/translator_subnet.cc
+++ b/src/lib/yang/translator_subnet.cc
@@ -204,7 +204,11 @@ TranslatorSubnet::setSubnet(string const& xpath, ConstElementPtr elem) {
void
TranslatorSubnet::setSubnetIetf6(string const& xpath, ConstElementPtr elem) {
- /// Skip key "id".
+ // Set the list element. This is important in case we have no other elements except the key.
+ setItem(xpath, ElementPtr(), LeafBaseType::Unknown);
+
+ // Skip key "id" since it was set with the list element in the call above
+ // with the LeafBaseType::Unknown parameter.
setMandatoryDivergingLeaf(elem, xpath, "subnet", "network-prefix", LeafBaseType::String);
@@ -236,7 +240,11 @@ TranslatorSubnet::setSubnetIetf6(string const& xpath, ConstElementPtr elem) {
void
TranslatorSubnet::setSubnetKea(string const& xpath, ConstElementPtr elem) {
- /// Skip key "id".
+ // Set the list element. This is important in case we have no other elements except the key.
+ setItem(xpath, ElementPtr(), LeafBaseType::Unknown);
+
+ // Skip key "id" since it was set with the list element in the call above
+ // with the LeafBaseType::Unknown parameter.
ConstElementPtr subnet = elem->get("subnet");
if (!subnet) {