summaryrefslogtreecommitdiffstats
path: root/src/lib/yang/translator_pool.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/yang/translator_pool.cc')
-rw-r--r--src/lib/yang/translator_pool.cc359
1 files changed, 359 insertions, 0 deletions
diff --git a/src/lib/yang/translator_pool.cc b/src/lib/yang/translator_pool.cc
new file mode 100644
index 0000000..d459385
--- /dev/null
+++ b/src/lib/yang/translator_pool.cc
@@ -0,0 +1,359 @@
+// Copyright (C) 2018-2021 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 <asiolink/io_address.h>
+#include <asiolink/addr_utilities.h>
+#include <yang/adaptor.h>
+#include <yang/translator_pool.h>
+#include <yang/yang_models.h>
+#include <boost/lexical_cast.hpp>
+#include <sstream>
+
+using namespace std;
+using namespace isc::data;
+using namespace isc::asiolink;
+using namespace sysrepo;
+
+namespace isc {
+namespace yang {
+
+TranslatorPool::TranslatorPool(S_Session session, const string& model)
+ : TranslatorBasic(session, model),
+ TranslatorOptionData(session, model),
+ TranslatorOptionDataList(session, model) {
+}
+
+TranslatorPool::~TranslatorPool() {
+}
+
+ElementPtr
+TranslatorPool::getPool(const string& xpath) {
+ try {
+ if (model_ == IETF_DHCPV6_SERVER) {
+ return (getPoolIetf6(xpath));
+ } else if ((model_ == KEA_DHCP4_SERVER) ||
+ (model_ == KEA_DHCP6_SERVER)) {
+ return (getPoolKea(xpath));
+ }
+ } catch (const sysrepo_exception& ex) {
+ isc_throw(SysrepoError,
+ "sysrepo error getting pool at '" << xpath
+ << "': " << ex.what());
+ }
+ isc_throw(NotImplemented,
+ "getPool not implemented for the model: " << model_);
+}
+
+ElementPtr
+TranslatorPool::getPoolIetf6(const string& xpath) {
+ ElementPtr result = Element::createMap();
+ // Skip pool-id which exists but is not used.
+ ConstElementPtr pool = getItem(xpath + "/pool-prefix");
+ if (!pool) {
+ isc_throw(BadValue, "getPoolIetf6 requires pool prefix at " << xpath);
+ }
+ result->set("pool", pool);
+ // Ignore start-address - end-address as prefix form is mandatory?
+ ConstElementPtr guard = getItem(xpath + "/client-class");
+ if (guard) {
+ result->set("client-class", guard);
+ }
+ 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 max-addr-count
+ // @todo: option-data
+ /// no require-client-classes nor user-context.
+ // Skip rapid-commit.
+ if (result->empty()) {
+ return ElementPtr();
+ }
+ return (result);
+}
+
+ElementPtr
+TranslatorPool::getPoolKea(const string& xpath) {
+ ElementPtr result = Element::createMap();
+ ConstElementPtr prefix = getItem(xpath + "/prefix");
+ if (prefix) {
+ result->set("pool", prefix);
+ } else {
+ ConstElementPtr start_addr = getItem(xpath + "/start-address");
+ ConstElementPtr end_addr = getItem(xpath + "/end-address");
+ if (!start_addr || !end_addr) {
+ isc_throw(BadValue, "getPoolKea requires either prefix or "
+ "both start and end addresses at " << xpath);
+ }
+ ostringstream range;
+ range << start_addr->stringValue() << " - "
+ << end_addr->stringValue();
+ result->set("pool", Element::create(range.str()));
+ }
+ ConstElementPtr options = getOptionDataList(xpath);
+ 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
+TranslatorPool::setPool(const string& xpath, ConstElementPtr elem) {
+ try {
+ if (model_ == IETF_DHCPV6_SERVER) {
+ setPoolIetf6(xpath, elem);
+ } else if ((model_ == KEA_DHCP4_SERVER) ||
+ (model_ == KEA_DHCP6_SERVER)) {
+ setPoolKea(xpath, elem);
+ } else {
+ isc_throw(NotImplemented,
+ "setPool not implemented for the model: " << model_);
+ }
+ } catch (const sysrepo_exception& ex) {
+ isc_throw(SysrepoError,
+ "sysrepo error setting pool '" << elem->str()
+ << "' at '" << xpath << "': " << ex.what());
+ }
+}
+
+void
+TranslatorPool::setPoolIetf6(const string& xpath, ConstElementPtr elem) {
+ ConstElementPtr pool = elem->get("pool");
+ if (!pool) {
+ isc_throw(BadValue, "setPoolIetf6 requires pool: " << elem->str());
+ }
+ string prefix = pool->stringValue();
+ if (prefix.find("/") == string::npos) {
+ isc_throw(BadValue,
+ "setPoolIetf only supports pools in prefix (vs range) "
+ "format and was called with '" << prefix << "'");
+ }
+ setItem(xpath + "/pool-prefix", pool, SR_STRING_T);
+ string addr = prefix.substr(0, prefix.find_first_of(" /"));
+ uint8_t plen = boost::lexical_cast<unsigned>
+ (prefix.substr(prefix.find_last_of(" /") + 1, string::npos));
+ const IOAddress& base(addr);
+ setItem(xpath + "/start-address",
+ Element::create(firstAddrInPrefix(base, plen).toText()),
+ SR_STRING_T);
+ setItem(xpath + "/end-address",
+ Element::create(lastAddrInPrefix(base, plen).toText()),
+ SR_STRING_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);
+ }
+ // skip max-addr-count
+ // @todo option-data
+ // Set max address count to disabled.
+ setItem(xpath + "/max-address-count",
+ Element::create(string("disabled")),
+ SR_ENUM_T);
+}
+
+void
+TranslatorPool::setPoolKea(const string& xpath, ConstElementPtr elem) {
+ ConstElementPtr pool = elem->get("pool");
+ if (!pool) {
+ isc_throw(BadValue, "setPoolKea requires pool: " << elem->str());
+ }
+ bool created = false;
+ string prefix = pool->stringValue();
+ string start_addr;
+ string end_addr;
+ getAddresses(prefix, start_addr, end_addr);
+ if (prefix.find("/") != string::npos) {
+ setItem(xpath + "/prefix", pool, SR_STRING_T);
+ created = true;
+ }
+ // Skip start-address and end-address as are the keys.
+ ConstElementPtr options = elem->get("option-data");
+ if (options && (options->size() > 0)) {
+ setOptionDataList(xpath, 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);
+ }
+}
+
+void
+TranslatorPool::getAddresses(const string& prefix,
+ string& start_address, string& end_address) {
+ size_t slash = prefix.find("/");
+ if (slash != string::npos) {
+ string addr = prefix.substr(0, prefix.find_first_of(" /"));
+ uint8_t plen = boost::lexical_cast<unsigned>
+ (prefix.substr(prefix.find_last_of(" /") + 1, string::npos));
+ start_address = firstAddrInPrefix(IOAddress(addr), plen).toText();
+ end_address = lastAddrInPrefix(IOAddress(addr), plen).toText();
+ return;
+ }
+ size_t dash = prefix.find("-");
+ if (dash == string::npos) {
+ isc_throw(BadValue,
+ "getAddresses called with invalid prefix: " << prefix);
+ }
+ start_address = prefix.substr(0, prefix.find_first_of(" -"));
+ end_address = prefix.substr(prefix.find_last_of(" -") + 1, string::npos);
+}
+
+TranslatorPools::TranslatorPools(S_Session session, const string& model)
+ : TranslatorBasic(session, model),
+ TranslatorOptionData(session, model),
+ TranslatorOptionDataList(session, model),
+ TranslatorPool(session, model) {
+}
+
+TranslatorPools::~TranslatorPools() {
+}
+
+ElementPtr
+TranslatorPools::getPools(const string& xpath) {
+ try {
+ if (model_ == IETF_DHCPV6_SERVER) {
+ return (getPoolsIetf(xpath));
+ } else if ((model_ == KEA_DHCP4_SERVER) ||
+ (model_ == KEA_DHCP6_SERVER)) {
+ return (getPoolsKea(xpath));
+ }
+ } catch (const sysrepo_exception& ex) {
+ isc_throw(SysrepoError,
+ "sysrepo error getting pools at '" << xpath
+ << "': " << ex.what());
+ }
+ isc_throw(NotImplemented,
+ "getPools not implemented for the model: " << model_);
+}
+
+ElementPtr
+TranslatorPools::getPoolsIetf(const string& xpath) {
+ return getList<TranslatorPool>(xpath + "/address-pool", *this,
+ &TranslatorPool::getPool);
+}
+
+ElementPtr
+TranslatorPools::getPoolsKea(const string& xpath) {
+ return getList<TranslatorPool>(xpath + "/pool", *this,
+ &TranslatorPool::getPool);
+}
+
+void
+TranslatorPools::setPools(const string& xpath, ConstElementPtr elem) {
+ try {
+ if (model_ == IETF_DHCPV6_SERVER) {
+ setPoolsById(xpath, elem);
+ } else if ((model_ == KEA_DHCP4_SERVER) ||
+ (model_ == KEA_DHCP6_SERVER)) {
+ setPoolsByAddresses(xpath, elem);
+ } else {
+ isc_throw(NotImplemented,
+ "setPools 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
+TranslatorPools::setPoolsById(const string& xpath, ConstElementPtr elem) {
+ for (size_t i = 0; i < elem->size(); ++i) {
+ ConstElementPtr pool = elem->get(i);
+ ostringstream prefix;
+ prefix << xpath << "/address-pool[pool-id='" << i << "']";
+ setPool(prefix.str(), pool);
+ }
+}
+
+void
+TranslatorPools::setPoolsByAddresses(const string& xpath,
+ ConstElementPtr elem) {
+ for (size_t i = 0; i < elem->size(); ++i) {
+ ConstElementPtr pool = elem->get(i);
+ if (!pool->contains("pool")) {
+ isc_throw(BadValue, "setPoolsByAddresses: missing required pool: "
+ << pool->str());
+ }
+ string pref = pool->get("pool")->stringValue();
+ string start_addr;
+ string end_addr;
+ getAddresses(pref, start_addr, end_addr);
+ ostringstream prefix;
+ prefix << xpath << "/pool[start-address='" << start_addr
+ << "'][end-address='" << end_addr << "']";
+ setPool(prefix.str(), pool);
+ }
+}
+
+} // namespace yang
+} // namespace isc