Commit 3aaecb8b authored by Francis Dupont's avatar Francis Dupont

[65-libyang-pd-pool_rebased] Addressed rebase conflicts

parents c029dc26 01bdd8fb
......@@ -16,6 +16,7 @@ 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_SOURCES += translator_pool.cc translator_pool.h
libkea_yang_la_SOURCES += translator_pd_pool.cc translator_pd_pool.h
libkea_yang_la_SOURCES += translator_host.cc translator_host.h
libkea_yang_la_SOURCES += yang_models.h
......@@ -42,6 +43,7 @@ libkea_yang_include_HEADERS = \
translator_option_data.h \
translator_option_def.h \
translator_pool.h \
translator_pd_pool.h \
yang_models.h
EXTRA_DIST = yang.dox
......
......@@ -25,6 +25,7 @@ run_unittests_SOURCES += translator_database_unittests.cc
run_unittests_SOURCES += translator_option_data_unittests.cc
run_unittests_SOURCES += translator_option_def_unittests.cc
run_unittests_SOURCES += translator_pool_unittests.cc
run_unittests_SOURCES += translator_pd_pool_unittests.cc
run_unittests_SOURCES += translator_host_unittests.cc
run_unittests_SOURCES += run_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
......
// 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_pd_pool.h>
#include <yang/yang_models.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 pd_pool_list[] = "pd pool list";
/// @brief Test fixture class for @ref TranslatorPdPools.
class TranslatorPdPoolsTest :
public GenericTranslatorTest<pd_pool_list, TranslatorPdPools> {
public:
/// Constructor.
TranslatorPdPoolsTest() { }
/// Destructor (does nothing).
virtual ~TranslatorPdPoolsTest() { }
};
// This test verifies that an empty pd pool list can be properly
// translated from YANG to JSON using the IETF model.
TEST_F(TranslatorPdPoolsTest, getEmptyIetf) {
useModel(IETF_DHCPV6_SERVER);
// Get the pd-pool list and checks it is empty.
const string& xpath =
"/ietf-dhcpv6-server:server/server-config/network-ranges"
"/network-range[network-range-id='111']/pd-pools";
ConstElementPtr pools;
EXPECT_NO_THROW(pools = t_obj_->getPdPools(xpath));
ASSERT_TRUE(pools);
ASSERT_EQ(Element::list, pools->getType());
EXPECT_EQ(0, pools->size());
}
// This test verifies that an empty pd pool list can be properly
// translated from YANG to JSON using the Kea ad hoc model.
TEST_F(TranslatorPdPoolsTest, getEmptyKea) {
useModel(KEA_DHCP6_SERVER);
// Get the pd-pool list and checks it is empty.
const string& xpath =
"/kea-dhcp6-server:config/subnet6/subnet6[id='111']/pd-pools";
ConstElementPtr pools;
EXPECT_NO_THROW(pools = t_obj_->getPdPools(xpath));
ASSERT_TRUE(pools);
ASSERT_EQ(Element::list, pools->getType());
EXPECT_EQ(0, pools->size());
}
// This test verifies that one pd pool can be properly
// translated from YANG to JSON using the IETF model.
TEST_F(TranslatorPdPoolsTest, getIetf) {
useModel(IETF_DHCPV6_SERVER);
// Create the subnet 2001:db8::/48 #111.
const string& subnet =
"/ietf-dhcpv6-server:server/server-config/network-ranges"
"/network-range[network-range-id='111']";
S_Val v_subnet(new Val("2001:db8::/48", SR_STRING_T));
const string& subnet_subnet = subnet + "/network-prefix";
EXPECT_NO_THROW(sess_->set_item(subnet_subnet.c_str(), v_subnet));
// Create the pd-pool 2001:db8:0:1000::/64 #222.
const string& xpath = subnet + "/pd-pools/pd-pool[pool-id='222']";
const string& prefix = xpath + "/prefix";
S_Val s_prefix(new Val("2001:db8:0:1000::/56"));
EXPECT_NO_THROW(sess_->set_item(prefix.c_str(), s_prefix));
const string& length = xpath + "/prefix-length";
uint8_t len = 56;
S_Val s_length(new Val(len, SR_UINT8_T));
EXPECT_NO_THROW(sess_->set_item(length.c_str(), s_length));
// Get the pool.
ConstElementPtr pool;
EXPECT_NO_THROW(pool = t_obj_->getPdPool(xpath));
ASSERT_TRUE(pool);
ElementPtr expected = Element::createMap();
expected->set("prefix", Element::create(string("2001:db8:0:1000::")));
expected->set("prefix-len", Element::create(56));
EXPECT_TRUE(expected->equals(*pool));
// Get the pd-pool list and checks the pd-pool is in it.
ConstElementPtr pools;
EXPECT_NO_THROW(pools = t_obj_->getPdPools(subnet + "/pd-pools"));
ASSERT_TRUE(pools);
ASSERT_EQ(Element::list, pools->getType());
ASSERT_EQ(1, pools->size());
EXPECT_TRUE(pool->equals(*pools->get(0)));
}
// This test verifies that one pd pool can be properly
// translated from YANG to JSON using the Kea ad hoc model.
TEST_F(TranslatorPdPoolsTest, getKea) {
useModel(KEA_DHCP6_SERVER);
// Create the subnet 2001:db8::/48 #111.
const string& subnet =
"/kea-dhcp6-server:config/subnet6/subnet6[id='111']";
S_Val v_subnet(new Val("2001:db8::/48", SR_STRING_T));
const string& subnet_subnet = subnet + "/subnet";
EXPECT_NO_THROW(sess_->set_item(subnet_subnet.c_str(), v_subnet));
// Create the pd-pool 2001:db8:0:1000::/64.
const string& xpath = subnet + "/pd-pools";
const string& prefix = "2001:db8:0:1000::/56";
ostringstream spool;
spool << xpath + "/pd-pool[prefix='" << prefix << "']";
const string& x_delegated = spool.str() + "/delegated-len";
uint8_t dl = 64;
S_Val s_delegated(new Val(dl, SR_UINT8_T));
EXPECT_NO_THROW(sess_->set_item(x_delegated.c_str(), s_delegated));
// Get the pool.
ConstElementPtr pool;
EXPECT_NO_THROW(pool = t_obj_->getPdPool(spool.str()));
ASSERT_TRUE(pool);
ElementPtr expected = Element::createMap();
expected->set("prefix", Element::create(string("2001:db8:0:1000::")));
expected->set("prefix-len", Element::create(56));
expected->set("delegated-len", Element::create(64));
EXPECT_TRUE(expected->equals(*pool));
// Get the pd-pool list and checks the pd-pool is in it.
ConstElementPtr pools;
EXPECT_NO_THROW(pools = t_obj_->getPdPools(xpath));
ASSERT_TRUE(pools);
ASSERT_EQ(Element::list, pools->getType());
ASSERT_EQ(1, pools->size());
EXPECT_TRUE(pool->equals(*pools->get(0)));
}
// This test verifies that an empty pd pool list can be properly
// translated from JSON to YANG using the IETF model.
TEST_F(TranslatorPdPoolsTest, setEmptyIetf) {
useModel(IETF_DHCPV6_SERVER);
// Create the subnet 2001:db8::/48 #111.
const string& subnet =
"/ietf-dhcpv6-server:server/server-config/network-ranges"
"/network-range[network-range-id='111']";
S_Val v_subnet(new Val("2001:db8::/48", SR_STRING_T));
const string& subnet_subnet = subnet + "/network-prefix";
EXPECT_NO_THROW(sess_->set_item(subnet_subnet.c_str(), v_subnet));
// Set empty list.
const string& xpath = subnet + "/pd-pools";
ConstElementPtr pools = Element::createList();
EXPECT_NO_THROW(t_obj_->setPdPools(xpath, pools));
// Get it back.
pools.reset();
EXPECT_NO_THROW(pools = t_obj_->getPdPools(xpath));
ASSERT_TRUE(pools);
ASSERT_EQ(Element::list, pools->getType());
EXPECT_EQ(0, pools->size());
}
// This test verifies that an empty pd pool list can be properly
// translated from JSON to YANG using the Kea ad hoc model.
TEST_F(TranslatorPdPoolsTest, setEmptyKea) {
useModel(KEA_DHCP6_SERVER);
// Create the subnet 2001:db8::/48 #111.
const string& subnet =
"/kea-dhcp6-server:config/subnet6/subnet6[id='111']";
S_Val v_subnet(new Val("2001:db8::/48", SR_STRING_T));
const string& subnet_subnet = subnet + "/subnet";
EXPECT_NO_THROW(sess_->set_item(subnet_subnet.c_str(), v_subnet));
// Set empty list.
const string& xpath = subnet + "/pd-pools";
ConstElementPtr pools = Element::createList();
EXPECT_NO_THROW(t_obj_->setPdPools(xpath, pools));
// Get it back.
pools.reset();
EXPECT_NO_THROW(pools = t_obj_->getPdPools(xpath));
ASSERT_TRUE(pools);
ASSERT_EQ(Element::list, pools->getType());
EXPECT_EQ(0, pools->size());
}
// This test verifies that one pd pool can be properly
// translated from JSON to YANG using the IETF model.
TEST_F(TranslatorPdPoolsTest, setIetf) {
useModel(IETF_DHCPV6_SERVER);
// Create the subnet 2001:db8::/48 #111.
const string& subnet =
"/ietf-dhcpv6-server:server/server-config/network-ranges"
"/network-range[network-range-id='111']";
S_Val v_subnet(new Val("2001:db8::/48", SR_STRING_T));
const string& subnet_subnet = subnet + "/network-prefix";
EXPECT_NO_THROW(sess_->set_item(subnet_subnet.c_str(), v_subnet));
// Set one pool.
const string& xpath = subnet + "/pd-pools";
ElementPtr pools = Element::createList();
ElementPtr pool = Element::createMap();
pool->set("prefix", Element::create(string("2001:db8:0:1000::")));
pool->set("prefix-len", Element::create(56));
pools->add(pool);
EXPECT_NO_THROW(t_obj_->setPdPools(xpath, pools));
// Get it back.
pools.reset();
EXPECT_NO_THROW(pools = t_obj_->getPdPools(xpath));
ASSERT_TRUE(pools);
ASSERT_EQ(Element::list, pools->getType());
ASSERT_EQ(1, pools->size());
EXPECT_TRUE(pool->equals(*pools->get(0)));
// Check the tree representation.
S_Tree tree;
EXPECT_NO_THROW(tree = sess_->get_subtree("/ietf-dhcpv6-server:server"));
ASSERT_TRUE(tree);
string expected =
"ietf-dhcpv6-server:server (container)\n"
" |\n"
" -- server-config (container)\n"
" |\n"
" -- network-ranges (container)\n"
" |\n"
" -- network-range (list instance)\n"
" |\n"
" -- network-range-id = 111\n"
" |\n"
" -- network-prefix = 2001:db8::/48\n"
" |\n"
" -- pd-pools (container)\n"
" |\n"
" -- pd-pool (list instance)\n"
" |\n"
" -- pool-id = 0\n"
" |\n"
" -- prefix = 2001:db8:0:1000::/56\n"
" |\n"
" -- prefix-length = 56\n"
" |\n"
" -- max-pd-space-utilization = disabled\n";
EXPECT_EQ(expected, tree->to_string(100));
}
// This test verifies that one pd pool can be properly
// translated from JSON to YANG using the kea ad hoc model.
TEST_F(TranslatorPdPoolsTest, setKea) {
useModel(KEA_DHCP6_SERVER);
// Create the subnet 2001:db8::/48 #111.
const string& subnet =
"/kea-dhcp6-server:config/subnet6/subnet6[id='111']";
S_Val v_subnet(new Val("2001:db8::/48", SR_STRING_T));
const string& subnet_subnet = subnet + "/subnet";
EXPECT_NO_THROW(sess_->set_item(subnet_subnet.c_str(), v_subnet));
// Set one pool.
const string& xpath = subnet + "/pd-pools";
ElementPtr pools = Element::createList();
ElementPtr pool = Element::createMap();
pool->set("prefix", Element::create(string("2001:db8:0:1000::")));
pool->set("prefix-len", Element::create(56));
pool->set("delegated-len", Element::create(64));
pools->add(pool);
EXPECT_NO_THROW(t_obj_->setPdPools(xpath, pools));
// Get it back.
pools.reset();
EXPECT_NO_THROW(pools = t_obj_->getPdPools(xpath));
ASSERT_TRUE(pools);
ASSERT_EQ(Element::list, pools->getType());
ASSERT_EQ(1, pools->size());
EXPECT_TRUE(pool->equals(*pools->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"
" -- subnet6 (container)\n"
" |\n"
" -- subnet6 (list instance)\n"
" |\n"
" -- id = 111\n"
" |\n"
" -- subnet = 2001:db8::/48\n"
" |\n"
" -- pd-pools (container)\n"
" |\n"
" -- pd-pool (list instance)\n"
" |\n"
" -- prefix = 2001:db8:0:1000::/56\n"
" |\n"
" -- delegated-len = 64\n";
EXPECT_EQ(expected, tree->to_string(100));
// Check it validates.
EXPECT_NO_THROW(sess_->validate());
}
// This test verifies that a non-empty list of pd pools can be properly
// translated from YANG to JSON using the Kea ad hoc model.
TEST_F(TranslatorPdPoolsTest, getListKea) {
useModel(KEA_DHCP6_SERVER);
// Create the subnet 2001:db8::/48 #111.
const string& subnet =
"/kea-dhcp6-server:config/subnet6/subnet6[id='111']";
S_Val v_subnet(new Val("2001:db8::/48", SR_STRING_T));
const string& subnet_subnet = subnet + "/subnet";
EXPECT_NO_THROW(sess_->set_item(subnet_subnet.c_str(), v_subnet));
// Create the first pd-pool 2001:db8:0:1000::/56.
const string& xpath = subnet + "/pd-pools";
const string& prefix = "2001:db8:0:1000::/56";
ostringstream spool;
spool << xpath + "/pd-pool[prefix='" << prefix << "']";
const string& x_delegated = spool.str() + "/delegated-len";
uint8_t dl = 64;
S_Val s_delegated(new Val(dl, SR_UINT8_T));
EXPECT_NO_THROW(sess_->set_item(x_delegated.c_str(), s_delegated));
// Create the second pd-pool 2001:db8:0:2000::/56
const string& xpath2 = subnet + "/pd-pools";
const string& prefix2 = "2001:db8:0:2000::/56";
ostringstream spool2;
spool2 << xpath2 + "/pd-pool[prefix='" << prefix2 << "']";
const string& x_delegated2 = spool2.str() + "/delegated-len";
uint8_t dl2 = 60;
S_Val s_delegated2(new Val(dl2, SR_UINT8_T));
EXPECT_NO_THROW(sess_->set_item(x_delegated2.c_str(), s_delegated2));
// Get the pools list.
ConstElementPtr pools;
EXPECT_NO_THROW(pools = t_obj_->getPdPools(xpath2));
ASSERT_TRUE(pools);
// Check that both of them are returned properly.
EXPECT_EQ(pools->str(),
"[ { \"delegated-len\": 64, \"prefix\": \"2001:db8:0:1000::\", "
"\"prefix-len\": 56 }, { \"delegated-len\": 60, \"prefix\": "
"\"2001:db8:0:2000::\", \"prefix-len\": 56 } ]");
}
}; // end of anonymous namespace
// 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/adaptor.h>
#include <yang/translator_pd_pool.h>
#include <yang/yang_models.h>
#include <boost/lexical_cast.hpp>
#include <sstream>
using namespace std;
using namespace isc::data;
namespace isc {
namespace yang {
TranslatorPdPool::TranslatorPdPool(S_Session session, const string& model)
: TranslatorBasic(session),
TranslatorOptionData(session, model),
TranslatorOptionDataList(session, model),
model_(model) {
}
TranslatorPdPool::~TranslatorPdPool() {
}
ElementPtr
TranslatorPdPool::getPdPool(const string& xpath) {
try {
if (model_ == IETF_DHCPV6_SERVER) {
return (getPdPoolIetf6(xpath));
} else if (model_ == KEA_DHCP6_SERVER) {
return (getPdPoolKea(xpath));
}
} catch (const sysrepo_exception& ex) {
isc_throw(SysrepoError,
"sysrepo error getting pd-pool at '" << xpath
<< "': " << ex.what());
}
isc_throw(NotImplemented,
"getPdPool not implemented for the model: " << model_);
}
ElementPtr
TranslatorPdPool::getPdPoolIetf6(const string& xpath) {
ElementPtr result = Element::createMap();
ConstElementPtr pref = getItem(xpath + "/prefix");
if (!pref) {
isc_throw(BadValue, "getPdPoolIetf6: prefix is required");
}
const string& prefix = pref->stringValue();
size_t slash = prefix.find("/");
if (slash == string::npos) {
isc_throw(BadValue,
"getPdPoolIetf6: no '/' in prefix '" << prefix << "'");
}
const string& address = prefix.substr(0, slash);
if (address.empty()) {
isc_throw(BadValue,
"getPdPoolIetf6: malformed prefix '" << prefix << "'");
}
result->set("prefix", Element::create(address));
// Silly: the prefix length is specified twice...
ConstElementPtr preflen = getItem(xpath + "/prefix-length");
if (!preflen) {
isc_throw(BadValue, "getPdPoolIetf6: prefix length is required");
}
result->set("prefix-len", preflen);
ConstElementPtr valid_lifetime = getItem(xpath + "/valid-lifetime");
if (valid_lifetime) {
result->set("valid-lifetime", valid_lifetime);
}
ConstElementPtr preferred_lifetime =
getItem(xpath + "/preferred-lifetime");
if (preferred_lifetime) {
result->set("preferred-lifetime", preferred_lifetime);
}
ConstElementPtr renew_time = getItem(xpath + "/renew-time");
if (renew_time) {
result->set("renew-timer", renew_time);
}
ConstElementPtr rebind_time = getItem(xpath + "/rebind-time");
if (rebind_time) {
result->set("rebind-timer", rebind_time);
}
// Skip rapid-commit.
ConstElementPtr guard = getItem(xpath + "/client-class");
if (guard) {
result->set("client-class", guard);
}
// no require-client-classes nor user-context.
// Skip max-pd-space-utilization.
// @todo option-data.
return (result);
}
ElementPtr
TranslatorPdPool::getPdPoolKea(const string& xpath) {
ElementPtr result = Element::createMap();
ConstElementPtr pref = getItem(xpath + "/prefix");
if (!pref) {
isc_throw(BadValue, "getPdPoolKea: no prefix defined at " << xpath);
}
const string& prefix = pref->stringValue();
size_t slash = prefix.find("/");
if (slash == string::npos) {
isc_throw(BadValue,
"getPdPoolKea: no '/' in prefix '" << prefix << "'");
}
const string& address = prefix.substr(0, slash);
const string& length = prefix.substr(slash + 1, string::npos);
if (address.empty() || length.empty()) {
isc_throw(BadValue,
"getPdPoolKea: malformed prefix '" << prefix << "'");
}
result->set("prefix", Element::create(address));
try {
unsigned len = boost::lexical_cast<unsigned>(length);
result->set("prefix-len", Element::create(static_cast<int>(len)));
} catch (const boost::bad_lexical_cast&) {
isc_throw(BadValue,
"getPdPoolKea: bad prefix length in '" << prefix << "'");
}
ConstElementPtr xpref = getItem(xpath + "/excluded-prefix");
if (xpref) {
const string& xprefix = xpref->stringValue();
size_t xslash = xprefix.find("/");
if (xslash == string::npos) {
isc_throw(BadValue,
"getPdPoolKea: no '/' in excluded prefix '"
<< xprefix << "'");
}
const string& xaddress = xprefix.substr(0, xslash);
const string& xlength = xprefix.substr(xslash + 1, string::npos);
if (xaddress.empty() || xlength.empty()) {
isc_throw(BadValue,
"getPdPoolKea: malformed excluded prefix '"
<< xprefix << "'");
}
result->set("excluded-prefix", Element::create(xaddress));
try {
unsigned xlen = boost::lexical_cast<unsigned>(xlength);
result->set("excluded-prefix-len",
Element::create(static_cast<int>(xlen)));
} catch (const boost::bad_lexical_cast&) {
isc_throw(BadValue,
"getPdPoolKea: bad excluded prefix length in '"
<< xprefix << "'");
}
}
ConstElementPtr delegated = getItem(xpath + "/delegated-len");
if (delegated) {
result->set("delegated-len", delegated);
}
ConstElementPtr options = getOptionDataList(xpath + "/option-data-list");
if (options && (options->size() > 0)) {
result->set("option-data", options);
}
ConstElementPtr guard = getItem(xpath + "/client-class");
if (guard) {
result->set("client-class", guard);
}
ConstElementPtr required = getItems(xpath + "/require-client-classes");
if (required && (required->size() > 0)) {
result->set("require-client-classes", required);
}
ConstElementPtr context = getItem(xpath + "/user-context");
if (context) {
result->set("user-context", Element::fromJSON(context->stringValue()));
}
return (result);
}
void
TranslatorPdPool::setPdPool(const string& xpath, ConstElementPtr elem) {
try {
if (model_ == IETF_DHCPV6_SERVER) {
setPdPoolIetf6(xpath, elem);
} else if (model_ == KEA_DHCP6_SERVER) {
setPdPoolKea(xpath, elem);
} else {
isc_throw(NotImplemented,
"setPdPool not implemented for the model: " << model_);
}
} catch (const sysrepo_exception& ex) {
isc_throw(SysrepoError,
"sysrepo error setting pd-pool '" << elem->str()
<< "' at '" << xpath << "': " << ex.what());
}
}
void
TranslatorPdPool::setPdPoolIetf6(const string& xpath, ConstElementPtr elem) {
ConstElementPtr base = elem->get("prefix");
ConstElementPtr length = elem->get("prefix-len");
if (!base || !length) {
isc_throw(BadValue,
"setPdPoolIetf6 requires prefix and prefix length: "
<< elem->str());
}
ostringstream prefix;
prefix << base->stringValue() << "/" << length->intValue();
setItem(xpath + "/prefix", Element::create(prefix.str()), SR_STRING_T);
setItem(xpath + "/prefix-length", length, SR_UINT8_T);
ConstElementPtr valid_lifetime = elem->get("valid-lifetime");
if (valid_lifetime) {
setItem(xpath + "/valid-lifetime", valid_lifetime, SR_UINT32_T);
}
ConstElementPtr preferred_lifetime = elem->get("preferred-lifetime");
if (preferred_lifetime) {
setItem(xpath + "/preferred-lifetime",
preferred_lifetime, SR_UINT32_T);
}
ConstElementPtr renew_timer = elem->get("renew-timer");
if (renew_timer) {
setItem(xpath + "/renew-time", renew_timer, SR_UINT32_T);
}
ConstElementPtr rebind_timer = elem->get("rebind-timer");
if (rebind_timer) {
setItem(xpath + "/rebind-time", rebind_timer, SR_UINT32_T);
}
// Skip rapid-commit.
ConstElementPtr guard = elem->get("client-class");
if (guard) {
setItem(xpath + "/client-class", guard, SR_STRING_T);
}
// Set max pd space utilization to disabled.
setItem(xpath + "/max-pd-space-utilization",
Element::create(string("disabled")),
SR_ENUM_T);
// @todo option-data.
}
void
TranslatorPdPool::setPdPoolKea(const string& xpath, ConstElementPtr elem) {
// Skip prefix as it is the key.
bool created = false;
ConstElementPtr delegated = elem->get("delegated-len");
if (delegated) {
setItem(xpath + "/delegated-len", delegated, SR_UINT8_T);
}
ConstElementPtr xprefix = elem->get("excluded-prefix");
ConstElementPtr xlen = elem->get("excluded-prefix-len");
if (xprefix && xlen) {
ostringstream xpref;
xpref << xprefix->stringValue() << "/" << xlen->intValue();
setItem(xpath + "/excluded-prefix", Element::create(xpref.str()),
SR_STRING_T);
created = true;
}
ConstElementPtr options = elem->get("option-data");
if (options && (options->size() > 0)) {
setOptionDataList(xpath + "/option-data-list", options);
created = true;
}
ConstElementPtr guard = elem->get("client-class");
if (guard) {
setItem(xpath + "/client-class", guard, SR_STRING_T);
created = true;
}
ConstElementPtr required = elem->get("require-client-classes");
if (required && (required->size() > 0)) {
for (ConstElementPtr rclass : required->listValue()) {
setItem(