diff options
Diffstat (limited to 'src/lib/yang/translator_subnet.cc')
-rw-r--r-- | src/lib/yang/translator_subnet.cc | 430 |
1 files changed, 430 insertions, 0 deletions
diff --git a/src/lib/yang/translator_subnet.cc b/src/lib/yang/translator_subnet.cc new file mode 100644 index 0000000..438895a --- /dev/null +++ b/src/lib/yang/translator_subnet.cc @@ -0,0 +1,430 @@ +// Copyright (C) 2018-2023 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/adaptor_pool.h> +#include <yang/translator_subnet.h> +#include <yang/yang_models.h> + +#include <sstream> + +using namespace std; +using namespace isc::data; +using namespace libyang; +using namespace sysrepo; + +namespace isc { +namespace yang { + +TranslatorSubnet::TranslatorSubnet(Session session, const string& model) + : Translator(session, model), + TranslatorOptionData(session, model), + TranslatorOptionDataList(session, model), + TranslatorPool(session, model), + TranslatorPools(session, model), + TranslatorPdPool(session, model), + TranslatorPdPools(session, model), + TranslatorHost(session, model), + TranslatorHosts(session, model) { +} + +ElementPtr +TranslatorSubnet::getSubnet(DataNode const& data_node) { + try { + if (model_ == IETF_DHCPV6_SERVER) { + return (getSubnetIetf6(data_node)); + } else if ((model_ == KEA_DHCP4_SERVER) || + (model_ == KEA_DHCP6_SERVER)) { + return (getSubnetKea(data_node)); + } + } catch (Error const& ex) { + isc_throw(NetconfError, + "getting subnet:" + << ex.what()); + } + isc_throw(NotImplemented, + "getSubnet not implemented for the model: " << model_); +} + +ElementPtr +TranslatorSubnet::getSubnetFromAbsoluteXpath(string const& xpath) { + try { + return getSubnet(findXPath(xpath)); + } catch (NetconfError const&) { + return ElementPtr(); + } +} + +ElementPtr +TranslatorSubnet::getSubnetIetf6(DataNode const& data_node) { + ElementPtr result = Element::createMap(); + getMandatoryDivergingLeaf(result, data_node, "subnet", "network-prefix"); + getMandatoryDivergingLeaf(result, data_node, "id", "network-range-id"); + + checkAndGetDiverging(result, data_node, "pools", "address-pools", + [&](DataNode const& node) -> ElementPtr const { + return getPools(node); + }); + + checkAndGet(result, data_node, "pd-pools", + [&](DataNode const& node) -> ElementPtr const { + return getPdPools(node); + }); + + ConstElementPtr description = getItem(data_node, "network-description"); + if (description) { + ElementPtr context = Element::createMap(); + context->set("description", description); + result->set("user-context", context); + } + + if (result->get("pools")) { + AdaptorPool::toSubnet(model_, result, result->get("pools")); + } + + /// @todo: timers + /// @todo: option-data + /// @todo: reservations + + return (result->empty() ? ElementPtr() : result); +} + +ElementPtr +TranslatorSubnet::getSubnetKea(DataNode const& data_node) { + ElementPtr result = Element::createMap(); + + getMandatoryLeaf(result, data_node, "id"); + getMandatoryLeaf(result, data_node, "subnet"); + + checkAndGetLeaf(result, data_node, "allocator"); + checkAndGetLeaf(result, data_node, "cache-max-age"); + checkAndGetLeaf(result, data_node, "cache-threshold"); + checkAndGetLeaf(result, data_node, "calculate-tee-times"); + checkAndGetLeaf(result, data_node, "client-class"); + checkAndGetLeaf(result, data_node, "ddns-generated-prefix"); + checkAndGetLeaf(result, data_node, "ddns-override-client-update"); + checkAndGetLeaf(result, data_node, "ddns-override-no-update"); + checkAndGetLeaf(result, data_node, "ddns-qualifying-suffix"); + checkAndGetLeaf(result, data_node, "ddns-replace-client-name"); + checkAndGetLeaf(result, data_node, "ddns-send-updates"); + checkAndGetLeaf(result, data_node, "ddns-ttl-percent"); + checkAndGetLeaf(result, data_node, "ddns-update-on-renew"); + checkAndGetLeaf(result, data_node, "ddns-use-conflict-resolution"); + checkAndGetLeaf(result, data_node, "hostname-char-replacement"); + checkAndGetLeaf(result, data_node, "hostname-char-set"); + checkAndGetLeaf(result, data_node, "interface"); + checkAndGetLeaf(result, data_node, "max-valid-lifetime"); + checkAndGetLeaf(result, data_node, "min-valid-lifetime"); + checkAndGetLeaf(result, data_node, "rebind-timer"); + checkAndGetLeaf(result, data_node, "renew-timer"); + checkAndGetLeaf(result, data_node, "require-client-classes"); + checkAndGetLeaf(result, data_node, "reservation-mode"); + checkAndGetLeaf(result, data_node, "reservations-global"); + checkAndGetLeaf(result, data_node, "reservations-in-subnet"); + checkAndGetLeaf(result, data_node, "reservations-out-of-pool"); + checkAndGetLeaf(result, data_node, "store-extended-info"); + checkAndGetLeaf(result, data_node, "t1-percent"); + checkAndGetLeaf(result, data_node, "t2-percent"); + checkAndGetLeaf(result, data_node, "valid-lifetime"); + + checkAndGetAndJsonifyLeaf(result, data_node, "user-context"); + + ConstElementPtr options = getOptionDataList(data_node); + if (options) { + result->set("option-data", options); + } + + ConstElementPtr pools = getPools(data_node); + if (pools) { + result->set("pools", pools); + } + + checkAndGet(result, data_node, "relay", + [&](DataNode const& node) -> ElementPtr const { + ElementPtr relay(Element::createMap()); + checkAndGetLeaf(relay, node, "ip-addresses"); + return relay; + }); + + ConstElementPtr hosts = getHosts(data_node); + if (hosts) { + result->set("reservations", hosts); + } + + if (model_ == KEA_DHCP6_SERVER) { + checkAndGetLeaf(result, data_node, "interface-id"); + checkAndGetLeaf(result, data_node, "max-preferred-lifetime"); + checkAndGetLeaf(result, data_node, "min-preferred-lifetime"); + checkAndGetLeaf(result, data_node, "pd-allocator"); + checkAndGetLeaf(result, data_node, "preferred-lifetime"); + checkAndGetLeaf(result, data_node, "rapid-commit"); + + ElementPtr pd_pools = getPdPools(data_node); + if (pd_pools) { + result->set("pd-pools", pd_pools); + } + } else if (model_ == KEA_DHCP4_SERVER) { + checkAndGetLeaf(result, data_node, "authoritative"); + checkAndGetLeaf(result, data_node, "boot-file-name"); + checkAndGetLeaf(result, data_node, "match-client-id"); + checkAndGetLeaf(result, data_node, "next-server"); + checkAndGetLeaf(result, data_node, "offer-lifetime"); + checkAndGetLeaf(result, data_node, "server-hostname"); + + checkAndGetDivergingLeaf(result, data_node, "4o6-interface", "subnet-4o6-interface"); + checkAndGetDivergingLeaf(result, data_node, "4o6-interface-id", "subnet-4o6-interface-id"); + checkAndGetDivergingLeaf(result, data_node, "4o6-subnet", "subnet-4o6-subnet"); + } + + return (result->empty() ? ElementPtr() : result); +} + +void +TranslatorSubnet::setSubnet(string const& xpath, ConstElementPtr elem) { + try { + if (model_ == IETF_DHCPV6_SERVER) { + setSubnetIetf6(xpath, elem); + } else if ((model_ == KEA_DHCP4_SERVER) || + (model_ == KEA_DHCP6_SERVER)) { + setSubnetKea(xpath, elem); + } else { + isc_throw(NotImplemented, + "setSubnet not implemented for the model: " << model_); + } + } catch (Error const& ex) { + isc_throw(NetconfError, + "setting subnet '" << elem->str() + << "' : " << ex.what()); + } +} + +void +TranslatorSubnet::setSubnetIetf6(string const& xpath, ConstElementPtr elem) { + /// Skip key "id". + + setMandatoryDivergingLeaf(elem, xpath, "subnet", "network-prefix", LeafBaseType::String); + + AdaptorPool::fromSubnet(model_, elem, elem->get("pools")); + ConstElementPtr context = elem->get("user-context"); + if (context && context->contains("description")) { + ConstElementPtr description = context->get("description"); + if (description->getType() == Element::string) { + setItem(xpath + "/network-description", description, LeafBaseType::String); + } + } + ConstElementPtr subnet = elem->get("subnet"); + if (!subnet) { + isc_throw(BadValue, "setSubnetIetf6 requires subnet: " << elem->str()); + } + setItem(xpath + "/network-prefix", subnet, LeafBaseType::String); + ConstElementPtr pools = elem->get("pools"); + if (pools && !pools->empty()) { + setPools(xpath + "/address-pools", pools); + } + pools = elem->get("pd-pools"); + if (pools && !pools->empty()) { + setPdPools(xpath + "/pd-pools", pools); + } + + /// @todo: option-data + /// @todo: reservations +} + +void +TranslatorSubnet::setSubnetKea(string const& xpath, ConstElementPtr elem) { + /// Skip key "id". + + ConstElementPtr subnet = elem->get("subnet"); + if (!subnet) { + isc_throw(BadValue, "setSubnetKea requires subnet: " << elem->str()); + } + setItem(xpath + "/subnet", subnet, LeafBaseType::String); + + checkAndSetLeaf(elem, xpath, "allocator", LeafBaseType::String); + checkAndSetLeaf(elem, xpath, "cache-max-age", LeafBaseType::Uint32); + checkAndSetLeaf(elem, xpath, "cache-threshold", LeafBaseType::Dec64); + checkAndSetLeaf(elem, xpath, "calculate-tee-times", LeafBaseType::Bool); + checkAndSetLeaf(elem, xpath, "client-class", LeafBaseType::String); + checkAndSetLeaf(elem, xpath, "ddns-generated-prefix", LeafBaseType::String); + checkAndSetLeaf(elem, xpath, "ddns-override-client-update", LeafBaseType::Bool); + checkAndSetLeaf(elem, xpath, "ddns-override-no-update", LeafBaseType::Bool); + checkAndSetLeaf(elem, xpath, "ddns-qualifying-suffix", LeafBaseType::String); + checkAndSetLeaf(elem, xpath, "ddns-replace-client-name", LeafBaseType::String); + checkAndSetLeaf(elem, xpath, "ddns-send-updates", LeafBaseType::Bool); + checkAndSetLeaf(elem, xpath, "ddns-ttl-percent", LeafBaseType::Dec64); + checkAndSetLeaf(elem, xpath, "ddns-update-on-renew", LeafBaseType::Bool); + checkAndSetLeaf(elem, xpath, "ddns-use-conflict-resolution", LeafBaseType::Bool); + checkAndSetLeaf(elem, xpath, "hostname-char-replacement", LeafBaseType::String); + checkAndSetLeaf(elem, xpath, "hostname-char-set", LeafBaseType::String); + checkAndSetLeaf(elem, xpath, "interface", LeafBaseType::String); + checkAndSetLeaf(elem, xpath, "max-valid-lifetime", LeafBaseType::Uint32); + checkAndSetLeaf(elem, xpath, "min-valid-lifetime", LeafBaseType::Uint32); + checkAndSetLeaf(elem, xpath, "rebind-timer", LeafBaseType::Uint32); + checkAndSetLeaf(elem, xpath, "renew-timer", LeafBaseType::Uint32); + checkAndSetLeaf(elem, xpath, "reservation-mode", LeafBaseType::Enum); + checkAndSetLeaf(elem, xpath, "reservations-global", LeafBaseType::Bool); + checkAndSetLeaf(elem, xpath, "reservations-in-subnet", LeafBaseType::Bool); + checkAndSetLeaf(elem, xpath, "reservations-out-of-pool", LeafBaseType::Bool); + checkAndSetLeaf(elem, xpath, "store-extended-info", LeafBaseType::Bool); + checkAndSetLeaf(elem, xpath, "t1-percent", LeafBaseType::Dec64); + checkAndSetLeaf(elem, xpath, "t2-percent", LeafBaseType::Dec64); + checkAndSetLeaf(elem, xpath, "valid-lifetime", LeafBaseType::Uint32); + + checkAndSetLeafList(elem, xpath, "require-client-classes", LeafBaseType::String); + + ConstElementPtr options = elem->get("option-data"); + if (options && !options->empty()) { + setOptionDataList(xpath, options); + } + ConstElementPtr pools = elem->get("pools"); + if (pools && !pools->empty()) { + setPools(xpath, pools); + } + ConstElementPtr relay = elem->get("relay"); + if (relay) { + ConstElementPtr address = relay->get("ip-address"); + ConstElementPtr addresses = relay->get("ip-addresses"); + if (address) { + setItem(xpath + "/relay/ip-addresses", address, LeafBaseType::String); + } else if (addresses && !addresses->empty()) { + for (ElementPtr const& addr : addresses->listValue()) { + setItem(xpath + "/relay/ip-addresses", addr, LeafBaseType::String); + } + } + } + ConstElementPtr hosts = elem->get("reservations"); + if (hosts && !hosts->empty()) { + setHosts(xpath, hosts); + } + + if (model_ == KEA_DHCP6_SERVER) { + checkAndSetLeaf(elem, xpath, "interface-id", LeafBaseType::String); + checkAndSetLeaf(elem, xpath, "max-preferred-lifetime", LeafBaseType::Uint32); + checkAndSetLeaf(elem, xpath, "min-preferred-lifetime", LeafBaseType::Uint32); + checkAndSetLeaf(elem, xpath, "pd-allocator", LeafBaseType::String); + checkAndSetLeaf(elem, xpath, "preferred-lifetime", LeafBaseType::Uint32); + checkAndSetLeaf(elem, xpath, "rapid-commit", LeafBaseType::Bool); + + pools = elem->get("pd-pools"); + if (pools && !pools->empty()) { + setPdPools(xpath, pools); + } + } else { + checkAndSetLeaf(elem, xpath, "authoritative", LeafBaseType::Bool); + checkAndSetLeaf(elem, xpath, "boot-file-name", LeafBaseType::String); + checkAndSetLeaf(elem, xpath, "match-client-id", LeafBaseType::Bool); + checkAndSetLeaf(elem, xpath, "next-server", LeafBaseType::String); + checkAndSetLeaf(elem, xpath, "offer-lifetime", LeafBaseType::Uint32); + checkAndSetLeaf(elem, xpath, "server-hostname", LeafBaseType::String); + + checkAndSetDivergingLeaf(elem, xpath, "4o6-interface", "subnet-4o6-interface", LeafBaseType::String); + checkAndSetDivergingLeaf(elem, xpath, "4o6-interface-id", "subnet-4o6-interface-id", LeafBaseType::String); + checkAndSetDivergingLeaf(elem, xpath, "4o6-subnet", "subnet-4o6-subnet", LeafBaseType::String); + } + checkAndSetUserContext(elem, xpath); +} + +TranslatorSubnets::TranslatorSubnets(Session session, const string& model) + : Translator(session, model), + TranslatorOptionData(session, model), + TranslatorOptionDataList(session, model), + TranslatorPool(session, model), + TranslatorPools(session, model), + TranslatorPdPool(session, model), + TranslatorPdPools(session, model), + TranslatorHost(session, model), + TranslatorHosts(session, model), + TranslatorSubnet(session, model) { +} + +ElementPtr +TranslatorSubnets::getSubnets(DataNode const& data_node) { + try { + if (model_ == IETF_DHCPV6_SERVER) { + return (getSubnetsCommon(data_node, "network-range")); + } else if (model_ == KEA_DHCP4_SERVER) { + return (getSubnetsCommon(data_node, "subnet4")); + } else if (model_ == KEA_DHCP6_SERVER) { + return (getSubnetsCommon(data_node, "subnet6")); + } + } catch (Error const& ex) { + isc_throw(NetconfError, + "getting subnets:" + << ex.what()); + } + isc_throw(NotImplemented, + "getSubnets not implemented for the model: " << model_); +} + +ElementPtr +TranslatorSubnets::getSubnetsFromAbsoluteXpath(string const& xpath) { + try { + return getSubnets(findXPath(xpath)); + } catch (NetconfError const&) { + return ElementPtr(); + } +} + +ElementPtr +TranslatorSubnets::getSubnetsCommon(DataNode const& data_node, + string const& subsel) { + return getList<TranslatorSubnet>(data_node, subsel, *this, + &TranslatorSubnet::getSubnet); +} + +void +TranslatorSubnets::setSubnets(string const& xpath, ConstElementPtr elem) { + try { + if (model_ == IETF_DHCPV6_SERVER) { + setSubnetsIetf6(xpath, elem); + } else if (model_ == KEA_DHCP4_SERVER) { + setSubnetsKea(xpath, elem, "subnet4"); + } else if (model_ == KEA_DHCP6_SERVER) { + setSubnetsKea(xpath, elem, "subnet6"); + } else { + isc_throw(NotImplemented, + "setSubnets not implemented for the model: " << model_); + } + } catch (Error const& ex) { + isc_throw(NetconfError, + "setting subnets '" << elem->str() + << "' : " << ex.what()); + } +} + +void +TranslatorSubnets::setSubnetsIetf6(string const& xpath, ConstElementPtr elem) { + for (size_t i = 0; i < elem->size(); ++i) { + ElementPtr subnet = elem->getNonConst(i); + ostringstream range; + range << xpath << "/network-range[network-range-id='"; + ConstElementPtr id = subnet->get("id"); + if (!id) { + isc_throw(BadValue, "subnet without id: " << elem->str()); + } + range << id->intValue() << "']"; + setSubnet(range.str(), subnet); + } +} + +void +TranslatorSubnets::setSubnetsKea(string const& xpath, ConstElementPtr elem, + string const& subsel) { + for (size_t i = 0; i < elem->size(); ++i) { + ElementPtr subnet = elem->getNonConst(i); + if (!subnet->contains("id")) { + isc_throw(BadValue, "subnet without id: " << subnet->str()); + } + ostringstream prefix; + prefix << xpath << "/" << subsel << "[id='" + << subnet->get("id")->intValue() << "']"; + setSubnet(prefix.str(), subnet); + } +} + +} // namespace yang +} // namespace isc |