2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-09-05 00:15:17 +00:00

[65-libyang-pd-pool_rebased] Rebased after database translator merge

This commit is contained in:
Francis Dupont
2018-10-16 17:35:50 +02:00
5 changed files with 918 additions and 0 deletions

View File

@@ -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 += yang_models.h
@@ -40,6 +41,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

View File

@@ -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 += run_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)

View File

@@ -0,0 +1,322 @@
// 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 empty 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 empty 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());
}
}; // end of anonymous namespace

View File

@@ -0,0 +1,366 @@
// 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: prefix is required");
}
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(xpath + "/require-client-classes", rclass, SR_STRING_T);
created = true;
}
}
ConstElementPtr context = Adaptor::getContext(elem);
if (context) {
setItem(xpath + "/user-context", Element::create(context->str()),
SR_STRING_T);
created = true;
}
// There is no mandatory fields outside the keys so force creation.
if (!created) {
ConstElementPtr list = Element::createList();
setItem(xpath, list, SR_LIST_T);
}
}
TranslatorPdPools::TranslatorPdPools(S_Session session, const string& model)
: TranslatorBasic(session),
TranslatorOptionData(session, model),
TranslatorOptionDataList(session, model),
TranslatorPdPool(session, model),
model_(model) {
}
TranslatorPdPools::~TranslatorPdPools() {
}
ElementPtr
TranslatorPdPools::getPdPools(const string& xpath) {
try {
ElementPtr result = Element::createList();
S_Iter_Value iter = getIter(xpath + "/*");
if (!iter) {
// Can't happen.
isc_throw(Unexpected, "getPdPools: can't get iterator: " << xpath);
}
for (;;) {
const string& pool = getNext(iter);
if (pool.empty()) {
break;
}
result->add(getPdPool(pool));
}
return (result);
} catch (const sysrepo_exception& ex) {
isc_throw(SysrepoError,
"sysrepo error getting pd-pools at '" << xpath
<< "': " << ex.what());
}
}
void
TranslatorPdPools::setPdPools(const string& xpath, ConstElementPtr elem) {
try {
if (model_ == IETF_DHCPV6_SERVER) {
setPdPoolsId(xpath, elem);
} else if (model_ == KEA_DHCP6_SERVER) {
setPdPoolsPrefix(xpath, elem);
} else {
isc_throw(NotImplemented,
"setPdPools not implemented for the model: " << model_);
}
} catch (const sysrepo_exception& ex) {
isc_throw(SysrepoError,
"sysrepo error setting pools '" << elem->str()
<< "' at '" << xpath << "': " << ex.what());
}
}
void
TranslatorPdPools::setPdPoolsId(const string& xpath, ConstElementPtr elem) {
for (size_t i = 0; i < elem->size(); ++i) {
ConstElementPtr pool = elem->get(i);
ostringstream prefix;
prefix << xpath << "/pd-pool[pool-id='" << i << "']";
setPdPool(prefix.str(), pool);
}
}
void
TranslatorPdPools::setPdPoolsPrefix(const string& xpath,
ConstElementPtr elem) {
for (size_t i = 0; i < elem->size(); ++i) {
ConstElementPtr pool = elem->get(i);
if (!pool->contains("prefix") || !pool->contains("prefix-len")) {
isc_throw(BadValue, "pd-pool requires prefix and prefix length: "
<< pool->str());
}
ostringstream prefix;
prefix << xpath << "/pd-pool[prefix='"
<< pool->get("prefix")->stringValue() << "/"
<< pool->get("prefix-len")->intValue() << "']";
setPdPool(prefix.str(), pool);
}
}
}; // end of namespace isc::yang
}; // end of namespace isc

View File

@@ -0,0 +1,227 @@
// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef ISC_TRANSLATOR_PD_POOL_H
#define ISC_TRANSLATOR_PD_POOL_H 1
#include <yang/translator_option_data.h>
#include <list>
namespace isc {
namespace yang {
/// Prefix delegation pool translation between YANG and JSON
///
/// JSON syntax for both kea-dhcp4 and kea-dhcp6 is:
/// @code
/// {
/// "prefix": <prefix base>,
/// "prefix-len": <prefix length>,
/// "delegated-len": <delegated length>,
/// "excluded-prefix": <excluded prefix>,
/// "excluded-prefix-len": <excluded prefix length>,
/// "option-data": [ <list of option data> ],
/// "client-class": "<guard class name>",
/// "require-client-classes": [ <list of required class names> ],
/// "user-context": { <json map> },
/// "comment": "<comment>"
/// }
/// @endcode
///
/// YANG syntax for ietf-dhcpv6-server is with pool-id as the key.
/// @code
/// +--rw pool-id uint32
/// +--rw prefix inet:ipv6-prefix
/// +--rw prefix-length uint8
/// +--rw valid-lifetime yang:timeticks
/// +--rw renew-time yang:timeticks
/// +--rw rebind-time yang:timeticks
/// +--rw preferred-lifetime yang:timeticks
/// +--rw rapid-commit? boolean
/// +--rw client-class? string
/// +--rw max-pd-space-utilization? threshold
/// +--rw option-set-id?
/// /server/server-config/option-sets/option-set/option-set-id
/// @endcode
///
/// YANG syntax for kea-dhcp6 is with prefix as the key.
/// @code
/// +--rw prefix? inet:ipv6-prefix
/// +--rw delegated-len? uint8
/// +--rw excluded-prefix? inet:ipv6-prefix
/// +--rw option-data-list option-data*
/// +--rw client-class? string
/// +--rw require-client-classes* string
/// +--rw user-context? string
/// @endcode
///
/// An example in JSON and YANG formats:
/// @code
/// [
/// {
/// "prefix": "2001:db8:0:1000::",
/// "prefix-len": 56,
/// "delegated-len": 64
/// }
/// ]
/// @endcode
/// @code
/// /ietf-dhcpv6-server:server (container)
/// /ietf-dhcpv6-server:server/server-config (container)
/// /ietf-dhcpv6-server:server/server-config/network-ranges (container)
/// /ietf-dhcpv6-server:server/server-config/network-ranges
/// network-range[network-range-id='111'] (list instance)
/// /ietf-dhcpv6-server:server/server-config/network-ranges
/// network-range[network-range-id='111']/network-range-id = 111
/// /ietf-dhcpv6-server:server/server-config/network-ranges
/// network-range[network-range-id='111']/network-prefix = 2001:db8::/48
/// /ietf-dhcpv6-server:server/server-config/network-ranges
/// network-range[network-range-id='111']/pd-pools (container)
/// /ietf-dhcpv6-server:server/server-config/network-ranges
/// network-range[network-range-id='111']/pd-pools/
/// pd-pool[pool-id='0'] (list instance)
/// /ietf-dhcpv6-server:server/server-config/network-ranges/
/// pd-pool[pool-id='0']/pool-id = 0
/// network-range[network-range-id='111']/pd-pools/
/// pd-pool[pool-id='0']/prefix = 2001:db8:0:1000::/56
/// /ietf-dhcpv6-server:server/server-config/network-ranges
/// network-range[network-range-id='111']/pd-pools/
/// pd-pool[pool-id='0']/prefix-length = 56
/// /ietf-dhcpv6-server:server/server-config/network-ranges
/// network-range[network-range-id='111']/pd-pools/
/// pd-pool[pool-id='0']/max-pd-space-utilization = disabled
/// @endcode
/// @code
/// /kea-dhcp6-server:config (container)
/// /kea-dhcp6-server:config/subnet6 (container)
/// /kea-dhcp6-server:config/subnet6/subnet6[id='111'] (list instance)
/// /kea-dhcp6-server:config/subnet6/subnet6[id='111']/id = 111
/// /kea-dhcp6-server:config/subnet6/subnet6[id='111']/subnet = 2001:db8::/48
/// /kea-dhcp6-server:config/subnet6/subnet6[id='111']/pd-pools (container)
/// /kea-dhcp6-server:config/subnet6/subnet6[id='111']/pd-pools/
/// pd-pool[prefix='2001:db8:0:1000::/56' (list instance)
/// /kea-dhcp6-server:config/subnet6/subnet6[id='111']/pd-pools/
/// pd-pool[prefix='2001:db8:0:1000::/56'/prefix = 2001:db8:0:1000::/56
/// /kea-dhcp6-server:config/subnet6/subnet6[id='111']/pd-pools/
/// pd-pool[prefix='2001:db8:0:1000::/56'/delegated-len = 64
/// @endcode
/// @brief A translator class for converting a pd-pool between
/// YANG and JSON.
///
/// Currently supports on kea-dhcp[46]-server and partially ietf-dhcpv6-server.
class TranslatorPdPool : virtual public TranslatorOptionDataList {
public:
/// @brief Constructor.
///
/// @param session Sysrepo session.
/// @param model Model name.
TranslatorPdPool(S_Session session, const std::string& model);
/// @brief Destructor.
virtual ~TranslatorPdPool();
/// @brief Get and translate a pd-pool from YANG to JSON.
///
/// @param xpath The xpath of the pd-pool.
/// @return JSON representation of the pd-pool.
/// @throw SysrepoError when sysrepo raises an error.
/// @throw BadValue on pd-pool without well formed prefix.
isc::data::ElementPtr getPdPool(const std::string& xpath);
/// @brief Translate and set pd-pool from JSON to YANG.
///
/// @param xpath The xpath of the pd-pool.
/// @param elem The JSON element.
void setPdPool(const std::string& xpath, isc::data::ConstElementPtr elem);
protected:
/// @brief getPdPool for ietf-dhcpv6-server.
///
/// @param xpath The xpath of the pd-pool.
/// @return JSON representation of the pd-pool.
/// @throw SysrepoError when sysrepo raises an error.
isc::data::ElementPtr getPdPoolIetf6(const std::string& xpath);
/// @brief setPdPool for ietf-dhcpv6-server.
///
/// @param xpath The xpath of the pd-pool.
/// @param elem The JSON element.
/// @throw BadValue on pd-pool without prefix or prefix length.
void setPdPoolIetf6(const std::string& xpath,
isc::data::ConstElementPtr elem);
/// @brief getPdPool for kea-dhcp6.
///
/// @param xpath The xpath of the pd-pool.
/// @return JSON representation of the pd-pool.
/// @throw SysrepoError when sysrepo raises an error.
isc::data::ElementPtr getPdPoolKea(const std::string& xpath);
/// @brief setPdPool for kea-dhcp6.
///
/// @param xpath The xpath of the pd-pool.
/// @param elem The JSON element.
void setPdPoolKea(const std::string& xpath,
isc::data::ConstElementPtr elem);
/// @brief The model.
std::string model_;
};
/// @brief A translator class for converting a pd-pool list between
/// YANG and JSON.
///
/// Currently supports on kea-dhcp[46]-server and partially ietf-dhcpv6-server.
class TranslatorPdPools : virtual public TranslatorPdPool {
public:
/// @brief Constructor.
///
/// @param session Sysrepo session.
/// @param model Model name.
TranslatorPdPools(S_Session session, const std::string& model);
/// @brief Destructor.
virtual ~TranslatorPdPools();
/// @brief Get and translate pd-pools from YANG to JSON.
///
/// @param xpath The xpath of the pd-pool list.
/// @throw SysrepoError when sysrepo raises an error.
isc::data::ElementPtr getPdPools(const std::string& xpath);
/// @brief Translate and set pd-pools from JSON to YANG.
///
/// @param xpath The xpath of the pd-pool list.
/// @param elem The JSON element.
void setPdPools(const std::string& xpath, isc::data::ConstElementPtr elem);
protected:
/// @brief setPdPools using pool-id.
///
/// @param xpath The xpath of the pd-pool list.
/// @param elem The JSON element.
void setPdPoolsId(const std::string& xpath,
isc::data::ConstElementPtr elem);
/// @brief setPdPools using prefix.
///
/// @param xpath The xpath of the pd-pool list.
/// @param elem The JSON element.
/// @throw BadValue on pd-pool without prefix or prefix length.
void setPdPoolsPrefix(const std::string& xpath,
isc::data::ConstElementPtr elem);
/// @brief The model.
std::string model_;
};
}; // end of namespace isc::yang
}; // end of namespace isc
#endif // ISC_TRANSLATOR_PD_POOL_H