]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3198] make data a key for option-data in code
authorAndrei Pavel <andrei@isc.org>
Wed, 10 Jan 2024 09:20:26 +0000 (11:20 +0200)
committerAndrei Pavel <andrei@isc.org>
Fri, 26 Jan 2024 10:48:18 +0000 (12:48 +0200)
- 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.

14 files changed:
doc/examples/kea4/all-options.json
src/lib/yang/tests/translator_option_data_unittests.cc
src/lib/yang/tests/translator_unittests.cc
src/lib/yang/tests/yang_configs.h
src/lib/yang/translator.cc
src/lib/yang/translator.h
src/lib/yang/translator_host.cc
src/lib/yang/translator_logger.cc
src/lib/yang/translator_option_data.cc
src/lib/yang/translator_option_data.h
src/lib/yang/translator_option_def.cc
src/lib/yang/translator_pool.cc
src/lib/yang/translator_shared_network.cc
src/lib/yang/translator_subnet.cc

index a997a5ce1626c75b297a543cf088aae42a0d0472..76f640ab2b3061cbd3a06245fb183537ae1a40a2 100644 (file)
       // Type: string
       {
         "code": 56,
-        "data": "Error: here's a DHCPNAK!",
+        "data": "Error: here is a DHCPNAK!",
         "name": "dhcp-message"
       },
 
index 3917e26f0f5f81ab1b9877ddf74872b256a193fa..ae69616607d397b1d220ff63902e3adc4763a28b 100644 (file)
@@ -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
index 853241902a7c35be6ab90c1113489816208ecd35..441bf3e3f348358e70237d928eb2b65def59b048 100644 (file)
@@ -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));
index d3825c9c24826cd8daba31d2af62cded28cde56e..ac138ff6aa3086cf7baf039fcecceb743e2f978b 100644 (file)
@@ -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 },
index 9c5ed9ce0415733453c7f60160bb45d0a8896cf0..4386b9485de1320793cfb633f8318d2eab2173aa 100644 (file)
@@ -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);
         });
index 5bbaa7d1022fc6e70edca3266ec1c1a66a9cd7c8..98cc2df5c61f190f74e1ba6dbfb4a26a94e8bdff 100644 (file)
@@ -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.
index 7aa6fa407d7ac9329af947a3454be9f8811753a0..451c283e7c07974952c78e481d8697a53003750e 100644 (file)
@@ -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);
 
index 9f845321d0a17d701c13e9b0531d0a794bca064a..6c2382999e1c3145a2c1d5d9d805a850a6ece6d4 100644 (file)
@@ -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);
index 5826869f2bb5801cce0d03bd9128810d09d671f5..7d6e1531161606d69473fb2113b6dacfb9d7863c 100644 (file)
@@ -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);
     }
 }
index d8d4f6039ef39ac49491501fe712ec5a1b8c0bd6..bf11249f7108922b73654a0e714202d2add160a6 100644 (file)
@@ -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
index 70bdd9f1d12900c08107faa33763f3a1e84b703b..6a82d519807782f66126c24178b0012b96fdcf86 100644 (file)
@@ -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);
index f1bb8a3fc15cbca484a859524db8cfe564b1a51d..48d6ba6353d5cf9e1cfb0e06a9c088c9a09d51c4 100644 (file)
@@ -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) {
index 00044969d7265808c2c4bccb1cf2765002104ad4..e9ade22f7aea34dee6006d78ec7e1d4f0b13c337 100644 (file)
@@ -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);
index 438895ae866692eb2bf102ac7700fd3cbcd501ad..c89ac2229e8fdbd7d89bf2a683853a1b99cced3b 100644 (file)
@@ -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) {