Commit c929ddb9 authored by Francis Dupont's avatar Francis Dupont

[65-libyang-adaptors] Added high level adaptors from lib-yang

parent 642d4b71
......@@ -6,6 +6,10 @@ AM_CXXFLAGS = $(KEA_CXXFLAGS)
lib_LTLIBRARIES = libkea-yang.la
libkea_yang_la_SOURCES = adaptor.cc adaptor.h
libkea_yang_la_SOURCES += adaptor_host.cc adaptor_host.h
libkea_yang_la_SOURCES += adaptor_pool.cc adaptor_pool.h
libkea_yang_la_SOURCES += adaptor_option.cc adaptor_option.h
libkea_yang_la_SOURCES += adaptor_subnet.cc adaptor_subnet.h
libkea_yang_la_SOURCES += sysrepo_error.h
libkea_yang_la_SOURCES += translator.cc translator.h
libkea_yang_la_SOURCES += translator_control_socket.cc
......@@ -36,6 +40,10 @@ libkea_yang_la_LDFLAGS = -no-undefined -version-info 0:0:0
libkea_yang_includedir = $(pkgincludedir)/yang
libkea_yang_include_HEADERS = \
adaptor.h \
adaptor_host.h \
adaptor_option.h \
adaptor_pool.h \
adaptor_subnet.h \
sysrepo_error.h \
translator.h \
translator_control_socket.h \
......
// 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 <util/encode/hex.h>
#include <util/strutil.h>
#include <yang/adaptor_host.h>
#include <iomanip>
#include <sstream>
using namespace std;
using namespace isc::data;
using namespace isc::util;
namespace isc {
namespace yang {
const string
AdaptorHost::STD_CHARACTERS =
"0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-.@_";
AdaptorHost::AdaptorHost() {
}
AdaptorHost::~AdaptorHost() {
}
void
AdaptorHost::quoteIdentifier(ElementPtr host) {
ConstElementPtr flex_id = host->get("flex-id");
if (!flex_id) {
return;
}
const string& id = flex_id->stringValue();
if (id.empty()) {
isc_throw(BadValue, "empty flexible identifier in " << host->str());
}
// No special and no not printable characters?
if (id.find_first_not_of(STD_CHARACTERS) == string::npos) {
return;
}
// Quoted identifier?
vector<uint8_t> binary = str::quotedStringToBinary(id);
if (binary.empty()) {
binary.assign(id.begin(), id.end());
}
// Convert in hexadecimal (from DUID::toText()).
stringstream tmp;
tmp << hex;
bool delim = false;
for (vector<uint8_t>::const_iterator it = binary.begin();
it != binary.end(); ++it) {
if (delim) {
tmp << ":";
}
tmp << setw(2) << setfill('0') << static_cast<unsigned int>(*it);
delim = true;
}
host->set("flex-id", Element::create(tmp.str()));
}
}; // end of namespace isc::yang
}; // end of namespace isc
// 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_ADAPTOR_HOST_H
#define ISC_ADAPTOR_HOST_H 1
#include <yang/adaptor.h>
namespace isc {
namespace yang {
/// @brief JSON adaptor for host reservations quoting identifiers.
///
/// The identifier type and identifier value are used as keys in YANG
/// host reservation lists so soem constraints were put on their contents.
/// For instance a quoted flex-id identifier raises an error (keys
/// are between quotes in setItem commands).
class AdaptorHost {
public:
/// @brief The string of standard (vs special or not printable)
/// characters (digit, letters, -, ., @, _).
static const std::string STD_CHARACTERS;
/// @brief Constructor.
AdaptorHost();
/// @brief Destructor.
virtual ~AdaptorHost();
/// @brief Quote when needed a host identifier.
///
/// Check if the flex-id identifier includes a special (including quote)
/// or not printable character. When it is the case produce and replace
/// by a hexadecimal identifier trying first for a quoted identifier.
///
/// @param host The host.
static void quoteIdentifier(isc::data::ElementPtr host);
};
}; // end of namespace isc::yang
}; // end of namespace isc
#endif // ISC_ADAPTOR_HOST_H
// 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_option.h>
#include <dhcp/std_option_defs.h>
#include <dhcp/docsis3_option_defs.h>
using namespace std;
using namespace isc::data;
using namespace isc::dhcp;
namespace isc {
namespace yang {
AdaptorOption::AdaptorOption() {
}
AdaptorOption::~AdaptorOption() {
}
void
AdaptorOption::setSpace(ElementPtr option, const string& space) {
if (!option->contains("space")) {
option->set("space", Element::create(space));
}
}
void
AdaptorOption::checkType(ConstElementPtr option) {
if (!option->contains("type")) {
isc_throw(MissingKey, "missing type in option definition "
<< option->str());
}
}
void
AdaptorOption::checkCode(ConstElementPtr option) {
if (!option->contains("code")) {
isc_throw(MissingKey, "missing code in option " << option->str());
}
}
void
AdaptorOption::collect(ConstElementPtr option, OptionCodes& codes) {
ConstElementPtr space = option->get("space");
ConstElementPtr code = option->get("code");
ConstElementPtr name = option->get("name");
if (name) {
string index = space->stringValue() + "@" + name->stringValue();
uint16_t val = static_cast<uint16_t>(code->intValue());
codes.insert(std::pair<string, uint16_t>(index, val));
}
}
void
AdaptorOption::setCode(ElementPtr option, const OptionCodes& codes) {
ConstElementPtr code = option->get("code");
if (!code) {
ConstElementPtr name = option->get("name");
if (!name) {
isc_throw(MissingKey, "missing name and code in option "
<< option->str());
}
ConstElementPtr space = option->get("space");
string index = space->stringValue() + "@" + name->stringValue();
OptionCodes::const_iterator it = codes.find(index);
if (it == codes.end()) {
isc_throw(MissingKey, "can't get code from option "
<< option->str());
}
option->set("code", Element::create(static_cast<int>(it->second)));
}
}
void
AdaptorOption::initCodes(OptionCodes& codes, const string& space) {
if (space == "dhcp4") {
initCodesInternal(codes, space, STANDARD_V4_OPTION_DEFINITIONS,
STANDARD_V4_OPTION_DEFINITIONS_SIZE);
initCodesInternal(codes, space, LAST_RESORT_V4_OPTION_DEFINITIONS,
LAST_RESORT_V4_OPTION_DEFINITIONS_SIZE);
initCodesInternal(codes, "vendor-4491",
DOCSIS3_V4_DEFS, DOCSIS3_V4_DEFS_SIZE);
} else if (space == "dhcp6") {
initCodesInternal(codes, space, STANDARD_V6_OPTION_DEFINITIONS,
STANDARD_V6_OPTION_DEFINITIONS_SIZE);
initCodesInternal(codes, "vendor-4491",
DOCSIS3_V6_DEFS, DOCSIS3_V6_DEFS_SIZE);
initCodesInternal(codes, MAPE_V6_OPTION_SPACE,
MAPE_V6_OPTION_DEFINITIONS,
MAPE_V6_OPTION_DEFINITIONS_SIZE);
initCodesInternal(codes, MAPT_V6_OPTION_SPACE,
MAPT_V6_OPTION_DEFINITIONS,
MAPT_V6_OPTION_DEFINITIONS_SIZE);
initCodesInternal(codes, LW_V6_OPTION_SPACE,
LW_V6_OPTION_DEFINITIONS,
LW_V6_OPTION_DEFINITIONS_SIZE);
initCodesInternal(codes, V4V6_RULE_OPTION_SPACE,
V4V6_RULE_OPTION_DEFINITIONS,
V4V6_RULE_OPTION_DEFINITIONS_SIZE);
initCodesInternal(codes, V4V6_BIND_OPTION_SPACE,
V4V6_BIND_OPTION_DEFINITIONS,
V4V6_BIND_OPTION_DEFINITIONS_SIZE);
initCodesInternal(codes, "vendor-2495",
ISC_V6_OPTION_DEFINITIONS,
ISC_V6_OPTION_DEFINITIONS_SIZE);
}
}
void
AdaptorOption::initCodesInternal(OptionCodes& codes, const string& space,
const OptionDefParams* params,
size_t params_size) {
for (size_t i = 0; i < params_size; ++i) {
string index = space + "@" + params[i].name;
codes.insert(std::pair<string, uint16_t>(index, params[i].code));
}
}
}; // end of namespace isc::yang
}; // end of namespace isc
// 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_ADAPTOR_OPTION_H
#define ISC_ADAPTOR_OPTION_H 1
#include <yang/adaptor.h>
#include <map>
#include <list>
namespace isc {
namespace dhcp {
/// @brief Forward declaration of option definition parameters.
struct OptionDefParams;
};
namespace yang {
/// @brief Map for DHCP option definitions handling code and
/// an index built from space and name.
typedef std::map<std::string, uint16_t> OptionCodes;
/// @brief JSON adaptor for option data or definition setting defaults.
///
/// Both option data and option definition lists are keyed by the code
/// and space pair so both must be available in setOptionXXX methods.
/// For space there is an implicit default so setSpace must be called
/// to add this default to option entries without space.
/// Note remaining adaptors assume this was done first.
///
/// checkCode and checkType can be used to check if code or type is present
/// (type is mandatory in option definitions).
///
/// A missing code can be found in standard definitions (loaded by initCodes)
/// or in configuration option definitions (at global and client classes
/// scopes). setCode uses the space+name to code map to set missing codes
/// and raises an error when it can't.
class AdaptorOption {
public:
/// @brief Constructor.
AdaptorOption();
/// @brief Destructor.
virtual ~AdaptorOption();
/// @brief Set space.
///
/// @param option The option.
/// @param space The default space name.
static void setSpace(isc::data::ElementPtr option,
const std::string& space);
/// @brief Check type (option definition).
///
/// @param option The option.
/// @throw MissingKey if the type is not present.
static void checkType(isc::data::ConstElementPtr option);
/// @brief Check code.
///
/// @param option The option.
/// @throw MissingKey if the code is not present.
static void checkCode(isc::data::ConstElementPtr option);
/// @brief Collect definition.
///
/// @param option The option definition.
/// @param codes The reference to option definitions.
static void collect(isc::data::ConstElementPtr option, OptionCodes& codes);
/// @brief Set code from name and definitions.
///
/// @param option The option data.
/// @param codes Option definitions.
static void setCode(isc::data::ElementPtr option,
const OptionCodes& codes);
/// @brief Initialize code map.
///
/// @param codes The reference to option definitions.
/// @param space The space name.
static void initCodes(OptionCodes& codes, const std::string& space);
protected:
/// @brief Initialize code map from option definition parameters.
///
/// @param codes The reference to option definitions.
/// @param space The space name.
/// @param params Array of option definition parameters
/// @param params_size The size of the array.
static void initCodesInternal(OptionCodes& codes, const std::string& space,
const isc::dhcp::OptionDefParams* params,
size_t params_size);
};
}; // end of namespace isc::yang
}; // end of namespace isc
#endif // ISC_ADAPTOR_OPTION_H
// 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_pool.h>
#include <yang/yang_models.h>
using namespace std;
using namespace isc::data;
namespace isc {
namespace yang {
AdaptorPool::AdaptorPool() {
}
AdaptorPool::~AdaptorPool() {
}
void
AdaptorPool::canonizePool(ElementPtr pool) {
const string& orig = pool->get("pool")->stringValue();
vector<char> v;
for (char ch : orig) {
if ((ch == ' ') || (ch == '\t') || (ch == '\n')) {
continue;
} else if (ch == '-') {
v.push_back(' ');
v.push_back(ch);
v.push_back(' ');
} else {
v.push_back(ch);
}
}
string canon;
canon.assign(v.begin(), v.end());
if (orig != canon) {
pool->set("pool", Element::create(canon));
}
}
void
AdaptorPool::fromSubnet(const string& model, ConstElementPtr subnet,
ConstElementPtr pools) {
if (model == IETF_DHCPV6_SERVER) {
fromSubnetIetf6(subnet, pools);
} else if ((model != KEA_DHCP4_SERVER) &&
(model != KEA_DHCP6_SERVER)) {
isc_throw(NotImplemented,
"fromSubnet not implemented for the model: " << model);
}
}
void
AdaptorPool::fromSubnetIetf6(ConstElementPtr subnet, ConstElementPtr pools) {
Adaptor::fromParent("valid-lifetime", subnet, pools);
Adaptor::fromParent("preferred-lifetime", subnet, pools);
Adaptor::fromParent("renew-timer", subnet, pools);
Adaptor::fromParent("rebind-timer", subnet, pools);
}
void
AdaptorPool::toSubnet(const string& model, ElementPtr subnet,
ConstElementPtr pools) {
if (model == IETF_DHCPV6_SERVER) {
toSubnetIetf6(subnet, pools);
} else if ((model != KEA_DHCP4_SERVER) &&
(model != KEA_DHCP6_SERVER)) {
isc_throw(NotImplemented,
"toSubnet not implemented for the model: " << model);
}
}
void
AdaptorPool::toSubnetIetf6(ElementPtr subnet, ConstElementPtr pools) {
Adaptor::toParent("valid-lifetime", subnet, pools);
Adaptor::toParent("preferred-lifetime", subnet, pools);
Adaptor::toParent("renew-timer", subnet, pools);
Adaptor::toParent("rebind-timer", subnet, pools);
}
}; // end of namespace isc::yang
}; // end of namespace isc
// 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_ADAPTOR_POOL_H
#define ISC_ADAPTOR_POOL_H 1
#include <yang/adaptor.h>
#include <list>
namespace isc {
namespace yang {
/// @brief JSON adaptor for pools between canonical Kea and YANG models.
///
/// First adaptor canonizePool checks and fixes if needed the pool entry
/// to a canonical form which is no space for prefix and one space each
/// side of the minus character for ranges.
///
/// Second adaptor is specific to the IETF DHCPv6 model (and does nothing
/// with a Kea DHCP model): it moves timer definitions from the subnet scope,
/// i.e. the scope where they are in Kea, to the pool scope, i.e. the scope
/// where they are in the IETF model, and back. The from way leaves timers
/// in the subnet scope as they are ignored by the translator, the to way
/// removes timers from pools as they are not expected by Kea at this scope.
class AdaptorPool {
public:
/// @brief Constructor.
AdaptorPool();
/// @brief Destructor.
virtual ~AdaptorPool();
/// @brief Canonize pool.
///
/// Remove spaces and replace "-" by " - " for readability.
/// @param pool The pool.
static void canonizePool(isc::data::ElementPtr pool);
/// @brief From subnets.
///
/// Move parameters from the subnet to each pool.
///
/// @param model Model name.
/// @param subnet The subnet element.
/// @param pools The children pools.
/// @throw NotImplemented on unexpected model.
static void fromSubnet(const std::string& model,
isc::data::ConstElementPtr subnet,
isc::data::ConstElementPtr pools);
/// @brief To subnet.
///
/// Move parameters from pools to the subnet.
///
/// @param model Model name.
/// @param subnet The subnet element.
/// @param pools The children pools.
/// @throw NotImplemented on unexpected model.
/// @throw BadValue on inconsistent (different timer values) pools.
static void toSubnet(const std::string& model,
isc::data::ElementPtr subnet,
isc::data::ConstElementPtr pools);
protected:
/// @brief From subnets for ietf-dhcpv6-server.
///
/// Use common and move valid-lifetime and preferred-lifetime.
///
/// @param subnet The subnet element.
/// @param pools The children pools.
static void fromSubnetIetf6(isc::data::ConstElementPtr subnet,
isc::data::ConstElementPtr pools);
/// @brief To subnet for ietf-dhcpv6-server.
///
/// Use common and move valid-lifetime and preferred-lifetime.
///
/// @param subnet The subnet element.
/// @param pools The children pools.
static void toSubnetIetf6(isc::data::ElementPtr subnet,
isc::data::ConstElementPtr pools);
};
}; // end of namespace isc::yang
}; // end of namespace isc
#endif // ISC_ADAPTOR_POOL_H
// 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_subnet.h>
using namespace std;
using namespace isc::data;
using namespace isc::dhcp;
namespace isc {
namespace yang {
AdaptorSubnet::AdaptorSubnet() {
}
AdaptorSubnet::~AdaptorSubnet() {
}
bool
AdaptorSubnet::collectID(ConstElementPtr subnet, SubnetIDSet& set) {
ConstElementPtr id = subnet->get("id");
if (id) {
set.insert(static_cast<SubnetID>(id->intValue()));
return (true);
}
return (false);
}
void
AdaptorSubnet::assignID(ElementPtr subnet, SubnetIDSet& set, SubnetID& next) {
ConstElementPtr id = subnet->get("id");
if (!id) {
// Skip already used.
while (set.count(next) > 0) {
++next;
}
subnet->set("id", Element::create(static_cast<long long>(next)));
set.insert(next);
++next;
}
}
void
AdaptorSubnet::updateRelay(ElementPtr subnet) {
ConstElementPtr relay = subnet->get("relay");
if (!relay) {
return;
}
ConstElementPtr addresses = relay->get("ip-addresses");
if (!addresses) {
ConstElementPtr address = relay->get("ip-address");
if (!address) {
subnet->remove("relay");
return;
}
ElementPtr addr = Element::create(address->stringValue());
ElementPtr addrs = Element::createList();
addrs->add(addr);
ElementPtr updated = Element::createMap();
updated->set("ip-addresses", addrs);
subnet->set("relay", updated);
} else if (addresses->size() == 0) {
subnet->remove("relay");
}
}
}; // end of namespace isc::yang
}; // end of namespace isc
// 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_ADAPTOR_SUBNET_H
#define ISC_ADAPTOR_SUBNET_H 1
#include <yang/adaptor.h>
#include <dhcpsrv/subnet_id.h>
#include <set>
namespace isc {
namespace yang {
/// @brief Set of SubnetIDs.
typedef std::set<isc::dhcp::SubnetID> SubnetIDSet;
/// @brief JSON adaptor for subnets adding IDs and canonizes relays.
///
/// Adding IDs is done in two passes walking through subnets.
/// -1- Add in the set used values and return false when there is no ID
/// so the caller can decide if the second pass is needed.
/// -2- For a subnet without an ID, assigned the next unused ID.
///
/// For relays an old syntax ip-address is translated into a new syntax
/// ip-addresses. Note as all canonization adaptor it is optional, i.e.,
/// code should work without it.
class AdaptorSubnet {
public:
/// @brief Constructor.
AdaptorSubnet();
/// @brief Destructor.
virtual ~AdaptorSubnet();
/// @brief Collect a subnet ID.
///
/// @param subnet The subnet.
/// @param set The reference to the set of assigned IDs.
/// @return True if the subnet has an ID, false otherwise.
static bool collectID(isc::data::ConstElementPtr subnet, SubnetIDSet& set);
/// @brief Assign subnet ID.
///
/// @param subnet The subnet.
/// @param set The reference to the set of assigned IDs.
/// @param next The next ID.
static void assignID(isc::data::ElementPtr subnet, SubnetIDSet& set,
isc::dhcp::SubnetID& next);
/// @brief Update relay.
///
/// Force the use of ip-addresses vs. ip-address.
/// Can be used for shared networks too.
///
/// @param subnet The subnet.
static void updateRelay(isc::data::ElementPtr subnet);
};
}; // end of namespace isc::yang
}; // end of namespace isc
#endif // ISC_ADAPTOR_SUBNET_H
......@@ -18,6 +18,10 @@ TESTS =
if HAVE_GTEST
TESTS += run_unittests
run_unittests_SOURCES = adaptor_unittests.cc
run_unittests_SOURCES += adaptor_option_unittests.cc
run_unittests_SOURCES += adaptor_pool_unittests.cc
run_unittests_SOURCES += adaptor_host_unittests.cc