2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-30 21:45:37 +00:00

[116-interface-id-dhcpv4] Merge branch '116-interface-id-dhcpv4' of gitlab.isc.org:isc-projects/kea into 116-interface-id-dhcpv4

This commit is contained in:
Francis Dupont
2018-09-19 14:49:00 +02:00
55 changed files with 9523 additions and 917 deletions

View File

@@ -1,3 +1,13 @@
1453. [func] marcin
Updated MySQL schema to facilitate Kea Configuration Backend
feature.
(Gitlab #89,!22, git e28c0c7b3e7a7729167cdad993f634ed1f0ac53b)
1452. [func] marcin
Implemented libkea-cb library which includes basic class
hierarchy for the Kea Configuration Backend.
(Gitlab #28,!20, git fb5c031ecaf4182e56f62874e9a6bd4c1d755a77)
1451. [build] tmark
Resolved a namespace issue with std::distance() in libdhcp++.cc
when building with Boost 1.68. Thanks to Huy Vu and Khem Raj

View File

@@ -74,6 +74,9 @@ if test "$cross_compiling" = "yes"; then
fi
AM_CONDITIONAL([CROSS_COMPILING], [test "$cross_compiling" = "yes"])
# pkg-config can be required.
AC_PATH_PROG([PKG_CONFIG], [pkg-config])
# Enable low-performing debugging facilities? This option optionally
# enables some debugging aids that perform slowly and hence aren't built
# by default.
@@ -817,7 +820,6 @@ AC_ARG_WITH([cql],
[cql_config="$withval"])
if test "${cql_config}" = "yes" ; then
AC_PATH_PROG([PKG_CONFIG], [pkg-config])
CQL_CONFIG="$PKG_CONFIG"
elif test "${cql_config}" != "no" ; then
CQL_CONFIG="${cql_config}"
@@ -1532,6 +1534,8 @@ AC_CONFIG_FILES([Makefile
src/lib/config/tests/Makefile
src/lib/config/tests/data_def_unittests_config.h
src/lib/config/tests/testdata/Makefile
src/lib/config_backend/Makefile
src/lib/config_backend/tests/Makefile
src/lib/cryptolink/Makefile
src/lib/cryptolink/tests/Makefile
src/lib/database/Makefile
@@ -1597,6 +1601,8 @@ AC_CONFIG_FILES([Makefile
src/lib/util/threads/Makefile
src/lib/util/threads/tests/Makefile
src/lib/util/unittests/Makefile
src/lib/yang/Makefile
src/lib/yang/tests/Makefile
src/share/Makefile
src/share/database/Makefile
src/share/database/scripts/Makefile

View File

@@ -810,6 +810,7 @@ INPUT = ../src/bin/agent \
../src/lib/util/random \
../src/lib/util/threads \
../src/lib/util/unittests \
../src/lib/yang \
devel
# This tag can be used to specify the character encoding of the source files

View File

@@ -41,6 +41,7 @@
* - @subpage unitTestsIntroduction
* - @subpage unitTestsEnvironmentVariables
* - @subpage unitTestsDatabaseConfig
* - @subpage unitTestsSysrepo
*
* @section performance Performance
* - @subpage benchmarks
@@ -129,6 +130,7 @@
* - @subpage libprocess
* - @subpage cpl
* - @subpage cplSignals
* - @subpage libyang
*
* @section miscellaneousTopics Miscellaneous Topics
* - @subpage terminology

View File

@@ -38,12 +38,12 @@ $ sudo apt-get install git cmake build-essential bison flex libpcre3-dev libev-d
</para>
<para>STEP 2. Install libyang. Download libyang from
https://github.com/CESNET/libyang/releases. As of writing this document, the latest
version was 0.15-r1.
https://github.com/CESNET/libyang.git. Checkout the devel branch.
<screen>
tar zxvf libyang-0.15-r1.tar.gz
cd libyang-0.15-r1/
git clone https://github.com/CESNET/libyang.git
cd libyang
git checkout devel
mkdir build
cd build
cmake ..
@@ -53,12 +53,13 @@ $ sudo apt-get install git cmake build-essential bison flex libpcre3-dev libev-d
For detailed build instructions, see https://github.com/CESNET/libyang/.</para>
<para>STEP 3. Install syrepo. Download sysrepo from https://github.com/sysrepo/sysrepo/releases.
As of writing this document, the 0.7.4 as the latest version.
<para>STEP 3. Install syrepo. Download sysrepo from
https://github.com/sysrepo/sysrepo.git. Checkout the last devel branch.
<screen>
tar zxvf sysrepo-0.7.4.tar.gz
cd sysrepo-0.7.4
git clone https://github.com/sysrepo/sysrepo.git
cd sysrepo
git checkout devel
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Debug -DGEN_LANGUAGE_BINDINGS=ON -DGEN_CPP_BINDINGS=ON\

View File

@@ -245,7 +245,7 @@ mysql_upgrade_schema_to_version() {
mysql_upgrade_test() {
test_start "mysql.host_reservation-upgrade"
test_start "mysql.upgrade"
# Let's wipe the whole database
mysql_wipe
@@ -469,11 +469,126 @@ EOF
ERRCODE=$?
assert_eq 0 $ERRCODE "logs table is missing or broken. (expected status code %d, returned %d)"
# table: modification (upgrade 6.0 -> 7.0)
qry="select id, modification_type from modification"
run_statement "modification" "$qry"
# table: modification table should have 3 entries (upgrade 6.0 -> 7.0)
qry="select count(*) from modification"
run_statement "modification count" "$qry" 3
# table: dhcp4_server
qry="select id, tag, description, modification_ts from dhcp4_server"
run_statement "dhcp4_server" "$qry"
# table: dhcp4_audit
qry="select id, object_type, object_id, modification_type, modification_ts, log_message from dhcp4_audit"
run_statement "dhcp4_audit" "$qry"
# table: dhcp4_global_parameter
qry="select id, name, value, modification_ts from dhcp4_global_parameter"
run_statement "dhcp4_global_parameter" "$qry"
# table: dhcp4_global_parameter_server
qry="select parameter_id, server_id, modification_ts from dhcp4_global_parameter_server"
run_statement "dhcp4_global_parameter_server" "$qry"
# table: dhcp4_option_def
qry="select id, code, space, modification_ts, array, encapsulate, record_types, user_context from dhcp4_option_def"
run_statement "dhcp4_option_def" "$qry"
# table: dhcp4_option_def_server
qry="select option_def_id, server_id, modification_ts from dhcp4_option_def_server"
run_statement "dhcp4_option_def_server" "$qry"
# table: dhcp4_shared_network
qry="select id, name, client_class, interface, match_client_id, modification_ts, rebind_timer, relay, renew_timer, require_client_classes, reservation_mode, server_hostname, user_context, valid_lifetime from dhcp4_shared_network"
run_statement "dhcp4_shared_network" "$qry"
# table: dhcp4_shared_network_server
qry="select shared_network_id, server_id, modification_ts from dhcp4_shared_network_server"
run_statement "dhcp4_shared_network_server" "$qry"
# table: dhcp4_subnet
qry="select subnet_prefix, 4o6_interface, 4o6_interface_id, 4o6_subnet, boot_file_name, client_class, interface, match_client_id, modification_ts, next_server, rebind_timer, relay, renew_timer, require_client_classes, reservation_mode, server_hostname, shared_network_name, subnet_id, user_context, valid_lifetime from dhcp4_subnet"
run_statement "dhcp4_subnet" "$qry"
# table: dhcp4_pool
qry="select id, start_address, end_address, subnet_id, modification_ts from dhcp4_pool"
run_statement "dhcp4_pool" "$qry"
# table: dhcp4_subnet_server
qry="select subnet_id, server_id, modification_ts from dhcp4_subnet_server"
run_statement "dhcp4_subnet_server" "$qry"
# table: dhcp4_options (should include three new columns)
qry="select shared_network_name, pool_id, modification_ts from dhcp4_options"
run_statement "dhcp4_options" "$qry"
# table: dhcp4_options_server
qry="select option_id, server_id, modification_ts from dhcp4_options_server"
run_statement "dhcp4_options_server" "$qry"
# table: dhcp6_server
qry="select id, tag, description, modification_ts from dhcp6_server"
run_statement "dhcp6_server" "$qry"
# table: dhcp6_audit
qry="select id, object_type, object_id, modification_type, modification_ts, log_message from dhcp6_audit"
run_statement "dhcp6_audit" "$qry"
# table: dhcp6_global_parameter
qry="select id, name, value, modification_ts from dhcp6_global_parameter"
run_statement "dhcp6_global_parameter" "$qry"
# table: dhcp6_global_parameter_server
qry="select parameter_id, server_id, modification_ts from dhcp6_global_parameter_server"
run_statement "dhcp6_global_parameter_server" "$qry"
# table: dhcp6_option_def
qry="select id, code, space, modification_ts, array, encapsulate, record_types, user_context from dhcp6_option_def"
run_statement "dhcp6_option_def" "$qry"
# table: dhcp6_option_def_server
qry="select option_def_id, server_id, modification_ts from dhcp6_option_def_server"
run_statement "dhcp6_option_def_server" "$qry"
# table: dhcp6_shared_network
qry="select id, name, client_class, interface, modification_ts, preferred_lifetime, rapid_commit, rebind_timer, relay, renew_timer, require_client_classes, reservation_mode, server_hostname, user_context, valid_lifetime from dhcp6_shared_network"
run_statement "dhcp6_shared_network" "$qry"
# table: dhcp6_shared_network_server
qry="select shared_network_id, server_id, modification_ts from dhcp6_shared_network_server"
run_statement "dhcp6_shared_network" "$qry"
# table: dhcp6_subnet
qry="select subnet_prefix, client_class, interface, modification_ts, preferred_lifetime, rapid_commit, rebind_timer, relay, renew_timer, require_client_classes, reservation_mode, shared_network_name, subnet_id, user_context, valid_lifetime from dhcp6_subnet"
run_statement "dhcp6_subnet" "$qry"
# table: dhcp6_subnet_server
qry="select subnet_id, server_id, modification_ts from dhcp6_subnet_server"
run_statement "dhcp6_subnet_server" "$qry"
# table: dhcp6_pd_pool
qry="select id, prefix_length, delegated_prefix_length, dhcp6_subnet_id, modification_ts from dhcp6_pd_pool"
run_statement "dhcp6_pd_pool" "$qry"
# table: dhcp6_pool
qry="select id, start_address, end_address, dhcp6_subnet_id, modification_ts from dhcp6_pool"
run_statement "dhcp6_pool" "$qry"
# table: dhcp6_options (should include four new columns)
qry="select shared_network_name, pool_id, pd_pool_id, modification_ts from dhcp6_options"
run_statement "dhcp6_options" "$qry"
# table: dhcp6_options_server
qry="select option_id, server_id, modification_ts from dhcp6_options_server"
run_statement "dhcp6_options_server" "$qry"
# Verify upgraded schema reports version 7.0
version=$(${keaadmin} lease-version mysql -u $db_user -p $db_password -n $db_name -d $db_scripts_dir)
assert_str_eq "7.0" ${version} "Expected kea-admin to return %s, returned value was %s"
# Let's wipe the whole database
mysql_wipe

View File

@@ -57,7 +57,7 @@ sbin_PROGRAMS = kea-netconf
kea_netconf_SOURCES = main.cc
kea_netconf_LDADD = libnetconf.la
kea_netconf_LDADD += $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la
kea_netconf_LDADD += $(top_builddir)/src/lib/process/libkea-process.la
kea_netconf_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
kea_netconf_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
kea_netconf_LDADD += $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS) $(BOOST_LIBS) $(SYSREPO_LIBS)

View File

@@ -8,7 +8,7 @@
#include <netconf/netconf_log.h>
#include <exceptions/exceptions.h>
#include <dhcpsrv/daemon.h>
#include <process/daemon.h>
#include <iostream>
#include <fstream>
#include <unistd.h>
@@ -36,7 +36,7 @@ usage() {
exit(EXIT_FAILURE);
}
/// @name Temporary code until isc::dhcp::Daemon is used.
/// @name Temporary code until isc::process::Daemon is used.
///
/// @{
const char* PID_FILENAME = "kea-netconf.test_config.pid";
@@ -46,7 +46,7 @@ volatile bool SHUTDOWN_FLAG = false;
void
createPIDFile(int pid) {
// This is not a real implemented. We will soon use the one coming
// from isc::dhcp::Daemon AFTER it's moved to libprocess.
// from isc::process::Daemon AFTER it's moved to libprocess.
ofstream file(PID_FILENAME, ios::trunc);
file << pid;
@@ -101,7 +101,7 @@ main(int argc, char* argv[]) {
int ret = EXIT_SUCCESS;
try {
// Temporary code. This will be replaced with isc::dhcp::Daemon
// Temporary code. This will be replaced with isc::process::Daemon
// once it is migrated to libprocess. We DO NOT want to bring
// the whole libdhcpsrv into netconf.
createPIDFile(getpid());
@@ -111,7 +111,7 @@ main(int argc, char* argv[]) {
// Initialize logging. If verbose, we'll use maximum verbosity.
bool verbose_mode = true;
isc::dhcp::Daemon::loggerInit(NETCONF_LOGGER_NAME, verbose_mode);
isc::process::Daemon::loggerInit(NETCONF_LOGGER_NAME, verbose_mode);
LOG_INFO(netconf_logger, NETCONF_STARTING).arg(VERSION).arg(getpid());
Connection conn("kea-netconf");

View File

@@ -13,5 +13,10 @@ if HAVE_CQL
SUBDIRS += cql
endif
SUBDIRS += testutils hooks dhcp config stats asiodns dhcp_ddns eval \
cfgrpt process dhcpsrv http
SUBDIRS += config_backend testutils hooks dhcp config stats
if HAVE_SYSREPO
SUBDIRS += yang
endif
SUBDIRS += asiodns dhcp_ddns eval cfgrpt process dhcpsrv http

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2012-2015,2017 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2012-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
@@ -21,8 +21,6 @@ namespace dhcp {
/// Example: For 2001:db8:1\::deaf:beef and length /120 the function will return
/// 2001:db8:1\::dead:be00. See also @ref lastAddrInPrefix.
///
/// @todo It currently works for v6 only and will throw if v4 address is passed.
///
/// @param prefix and address that belongs to a prefix
/// @param len prefix length
///
@@ -35,8 +33,6 @@ isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefi
/// Example: For 2001:db8:1\::deaf:beef and length /112 the function will return
/// 2001:db8:1\::dead:ffff. See also @ref firstAddrInPrefix.
///
/// @todo It currently works for v6 only and will throw if v4 address is passed.
///
/// @param prefix and address that belongs to a prefix
/// @param len prefix length
///

View File

@@ -0,0 +1,23 @@
SUBDIRS = . tests
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CXXFLAGS = $(KEA_CXXFLAGS)
EXTRA_DIST = base_config_backend.h
EXTRA_DIST += base_config_backend_mgr.h
EXTRA_DIST += base_config_backend_pool.h
# The message file should be in the distribution.
#EXTRA_DIST += config_backend.dox
CLEANFILES = *.gcno *.gcda
# Specify the headers for copying into the installation directory tree.
libkea_cb_includedir = $(pkgincludedir)/config_backend
libkea_cb_include_HEADERS = \
base_config_backend.h \
base_config_backend_mgr.h \
base_config_backend_pool.h

View File

@@ -0,0 +1,67 @@
// 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 BASE_CONFIG_BACKEND_H
#define BASE_CONFIG_BACKEND_H
#include <boost/shared_ptr.hpp>
#include <cstdint>
#include <set>
#include <string>
namespace isc {
namespace cb {
/// @brief Interface for Kea server specific configuration backend
/// implementations.
///
/// Each Kea server (e.g. DHCPv4 server) needs to implement its own
/// interface to store and fetch its configuration from the databases.
/// This is because each Kea server uses a different set of
/// configuration information. This is a base interface which should
/// be implemented (and extended) by respective Kea servers to provide
/// API to store and fetch configuration information from a database.
/// Such implementation is called configuration backend. Each
/// configuration backend faciliates a single database type, e.g. MySQL
/// database. In order to support multiple database types, i.e. MySQL,
/// Posrgres, Cassandra, each Kea server will have to implement
/// 3 separate configuration backends, one for each database type.
class BaseConfigBackend {
public:
/// @brief Virtual destructor.
virtual ~BaseConfigBackend() { }
/// @brief Returns backend type in the textual format.
///
/// @return Name of the storage for configurations, e.g. "mysql",
/// "pgsql" and so forth.
virtual std::string getType() const = 0;
/// @brief Returns backend host.
///
/// This is used by the @c BaseConfigBackendPool to select backend
/// when @c BackendSelector is specified.
///
/// @return host on which the database is located.
virtual std::string getHost() const = 0;
/// @brief Returns backend port number.
///
/// This is used by the @c BaseConfigBackendPool to select backend
/// when @c BackendSelector is specified.
///
/// @return Port number on which database service is available.
virtual uint16_t getPort() const = 0;
};
/// @brief Shared pointer to the @c BaseConfigBackend.
typedef boost::shared_ptr<BaseConfigBackend> BaseConfigBackendPtr;
} // end of namespace isc::cb
} // end of namespace isc
#endif // BASE_CONFIG_BACKEND_H

View File

@@ -0,0 +1,175 @@
// 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 BASE_CONFIG_BACKEND_MGR_H
#define BASE_CONFIG_BACKEND_MGR_H
#include <config_backend/base_config_backend.h>
#include <database/database_connection.h>
#include <exceptions/exceptions.h>
#include <boost/shared_ptr.hpp>
#include <functional>
#include <map>
#include <string>
namespace isc {
namespace cb {
/// @brief Base class for Configuration Backend Managers (CBM).
///
/// Each Kea server supporting Configuration Backend feature implements
/// a "manager" class which holds information about supported and
/// configured backends and provides access to the backends. This is
/// similar to @c HostMgr and @c LeaseMgr singletons being used by the
/// DHCP servers.
///
/// The Config Backend Managers are typically implemented as singletons
/// which can be accessed from any place within the server code. This
/// includes server configuration, data fetching during normal server
/// operation and data management, including processing of control
/// commands implemented within hooks libraries.
///
/// The @c BaseConfigBackendMgr is a base class for all CBMs implemented
/// for respective Kea servers. It includes mechanisms to register config
/// backend factory functions and to create instances of the backends using
/// those factory functions as a result of server configuration. The mechanism
/// of factory functions registration is useful in cases when the config
/// backend is implemented within the hook library. Such hook library
/// registers factory function in its @c load function and the server
/// simply calls this function to create the instance of this backend when
/// instructed to do so via configuration. Similar mechanism exists in
/// DHCP @c HostMgr.
///
/// Unlike @c HostMgr, the CBMs do not directly expose API to fetch and
/// manipulate the data in the database. This is done via, so called,
/// Configuration Backends Pools. See @c BaseConfigBackendPool for
/// details. The @c BaseConfigBackendMgr is provided with the pool type
/// via class template parameter. Respective CBM implementations
/// use their own pools, which provide APIs appropriate for those
/// implementation.
///
/// @tparam ConfgBackendPoolType Type of the configuration backend pool
/// to be used by the manager. It must derive from @c BaseConfigBackendPool
/// template class.
template<typename ConfigBackendPoolType>
class BaseConfigBackendMgr {
public:
/// @brief Pointer to the configuration backend pool.
typedef boost::shared_ptr<ConfigBackendPoolType> ConfigBackendPoolPtr;
/// @brief Type of the backend factory function.
///
/// Factory function returns a pointer to the instance of the configuration
/// backend created.
typedef std::function<typename ConfigBackendPoolType::ConfigBackendTypePtr
(const db::DatabaseConnection::ParameterMap&)> Factory;
/// @brief Constructor.
BaseConfigBackendMgr()
: factories_(), pool_(new ConfigBackendPoolType()) {
}
/// @brief Registers new backend factory function for a given backend type.
///
/// The typical usage of this function is to make the CBM aware of a
/// configuration backend implementation. This implementation may exist
/// in a hooks library. In such case, this function should be called from
/// the @c load function in this library. When the backend is registered,
/// the server will use it when required by the configuration, i.e. a
/// user includes configuration backend of that type in the
/// "config-databases" list.
///
/// If the backend of the given type has already been registered, perhaps
/// by another hooks library, the CBM will refuse to register another
/// backend of the same type.
///
/// @param db_type Backend type, e.g. "mysql".
/// @param factory Pointer to the backend factory function.
///
/// @return true if the backend has been successfully registered, false
/// if another backend of this type already exists.
bool registerBackendFactory(const std::string& db_type,
const Factory& factory) {
// Check if this backend has been already registered.
if (factories_.count(db_type)) {
return (false);
}
// Register the new backend.
factories_.insert(std::make_pair(db_type, factory));
return (true);
}
/// @brief Create an instance of a configuration backend.
///
/// This method uses provided @c dbaccess string representing database
/// connection information to create an instance of the database
/// backend. If the specified backend type is not supported, i.e. there
/// is no relevant factory function registered, an exception is thrown.
///
/// @param dbaccess Database access string being a collection of
/// key=value pairs.
///
/// @throw InvalidParameter if access string lacks database type value.
/// @throw db::InvalidType if the type of the database backend is not
/// supported.
/// @throw Unexpected if the backend factory function returned NULL.
void addBackend(const std::string& dbaccess) {
// Parse the access string into a map of parameters.
db::DatabaseConnection::ParameterMap parameters =
db::DatabaseConnection::parse(dbaccess);
// Get the database type to locate a factory function.
db::DatabaseConnection::ParameterMap::iterator it = parameters.find("type");
if (it == parameters.end()) {
isc_throw(InvalidParameter, "Config backend specification lacks the "
"'type' keyword");
}
std::string db_type = it->second;
auto index = factories_.find(db_type);
// No match?
if (index == factories_.end()) {
isc_throw(db::InvalidType, "The type of the configuration backend: '" <<
db_type << "' is not supported");
}
// Call the factory and push the pointer on sources.
auto backend = index->second(parameters);
if (!backend) {
isc_throw(Unexpected, "Config database " << db_type <<
" factory returned NULL");
}
// Backend instance created successfully.
pool_->addBackend(backend);
}
/// @brief Removes all backends from the pool.
void delAllBackends() {
pool_->delAllBackends();
}
/// @brief Returns underlying config backend pool.
ConfigBackendPoolPtr getPool() const {
return (pool_);
}
protected:
/// @brief A map holding registered backend factory functions.
std::map<std::string, Factory> factories_;
/// @brief Pointer to the configuration backends pool.
ConfigBackendPoolPtr pool_;
};
} // end of namespace isc::cb
} // end of namespace isc
#endif // BASE_CONFIG_BACKEND_MGR_H

View File

@@ -0,0 +1,504 @@
// 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 BASE_CONFIG_BACKEND_POOL_H
#define BASE_CONFIG_BACKEND_POOL_H
#include <cc/data.h>
#include <config_backend/base_config_backend.h>
#include <database/backend_selector.h>
#include <database/db_exceptions.h>
#include <database/server_selector.h>
#include <util/optional_value.h>
#include <functional>
#include <list>
#include <string>
namespace isc {
namespace cb {
/// @brief Base class for configuration backend pools.
///
/// Each Kea server supporting databases as a configuration repository can
/// use multiple database instances simultaneously. A pool is a collection
/// of database backends used by a particular server. Different Kea servers
/// use different pools because they store and fetch different configuration
/// information. For example: DHCPv4 server stores and fetches IPv4 subnets,
/// and DHCPv6 server stores and fetches IPv6 subnets. Therefore, each pool
/// type will expose a different API calls.
///
/// This template class is a base class for all pools used by various servers.
/// It implements mechanisms for managing multiple backends and for forwarding
/// API calls to one or many database backends depending on the selections
/// made via @c BackendSelector class.
///
/// @tparam ConfigBackendType Type of the configuration backend. This must
/// be a class deriving from @c BaseConfigBackend class. It is a class
/// dedicated to a particular server type, e.g. DHCPv4 server, and from
/// which database specific backends derive.
template<typename ConfigBackendType>
class BaseConfigBackendPool {
public:
/// @brief Shared pointer to the Configuration Backend used.
typedef boost::shared_ptr<ConfigBackendType> ConfigBackendTypePtr;
/// @brief Virtual destructor.
virtual ~BaseConfigBackendPool() { }
/// @brief Adds a backend to the pool.
///
/// @param backend Pointer to a backend to be added.
void addBackend(ConfigBackendTypePtr backend) {
backends_.push_back(backend);
}
/// @brief Deletes all backends from the pool.
void delAllBackends() {
backends_.clear();
}
protected:
/// @brief Retrieve a single configuration property from the pool.
///
/// This is common method for retrieving a single configuration property
/// from the databases. The server specific backends call this method to
/// retrieve a single object. For example, the DHCPv4 configuration backend
/// pool may use this function to implement a @c getSubnet4 method:
///
/// @code
/// Subnet4Ptr getSubnet4(const SubnetID& subnet_id,
/// const BackendSelector& backend_selector,
/// const ServerSelector& server_selector) const {
/// Subnet4Ptr subnet;
/// getPropertyPtrConst<Subnet4Ptr, const SubnetID&>
/// (&ConfigBackendDHCPv4::getSubnet4, backend_selector,
/// server_selector, subnet, subnet_id);
/// return (subnet);
/// }
/// @endcode
///
/// where @c ConfigBackendDHCPv4::getSubnet4 has the following signature:
///
/// @code
/// Subnet4Ptr getSubnet4(const ServerSelector&, const SubnetID&) const;
/// @endcode
///
/// If the backend selector is set to "unspecified", this method will iterate
/// over the existing backends and call the @c MethodPointer method on each
/// backend. It will return the first non-null (or non-zero) value returned
/// by this call. For example: if the first backend returns non-null value,
/// this value is returned via @c property argument and the calls for the
/// rest of the backends are skipped.
///
/// @tparam PropertyType Type of the object returned by the backend call.
/// @tparam FnPtrArgs Parameter pack holding argument types of the backend
/// method to be invoked.
/// @tparam Args Parameter pack holding types of the arguments provided
/// in the call to this method.
///
/// @param MethodPointer Pointer to the backend method to be called.
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param [out] property Reference to the shared pointer where retrieved
/// property should be assigned.
/// @param input Values to be used as input to the backend call.
///
/// @throw db::NoSuchDatabase if no database matching the given selector
/// was found.
template<typename PropertyType, typename... FnPtrArgs, typename... Args>
void getPropertyPtrConst(PropertyType (ConfigBackendType::*MethodPointer)
(const db::ServerSelector&, FnPtrArgs...) const,
const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
PropertyType& property,
Args... input) const {
// If no particular backend is selected, call each backend and return
// the first non-null (non zero) value.
if (backend_selector.amUnspecified()) {
for (auto backend : backends_) {
property = ((*backend).*MethodPointer)(server_selector, input...);
if (property) {
break;
}
}
} else {
// Backend selected, find the one that matches selection.
auto backends = selectBackends(backend_selector);
if (!backends.empty()) {
for (auto backend : backends) {
property = ((*backend).*MethodPointer)(server_selector, input...);
if (property) {
break;
}
}
} else {
isc_throw(db::NoSuchDatabase, "no such database found for selector: "
<< backend_selector.toText());
}
}
}
/// @brief Retrieve a single value encapsulated in the @c OptionalValue
/// template.
///
/// This is common method for retrieving a single configuration property
/// from the databases. The property is encapsulated in the @c OptionalValue
/// class. The value is set to "unspecified" if it is null in the database.
/// The following is the example implementation of the method retrieving
/// global conifguration value:
///
/// @code
/// OptionalValue<std::string>
/// getGlobalParameter4(const std::string& parameter_name,
/// const BackendSelector& backend_selector,
/// const ServerSelector& server_selector) const {
/// std::string parameter;
/// getOptionalPropertyConst<std::string, const std::string&>
/// (&ConfigBackendDHCPv4::getGlobalParameter4, backend_selector,
/// server_selector, parameter, parameter_name);
/// return (parameter);
/// }
/// @endcode
///
/// where @c ConfigBackendDHCPv4::getGlobalParameter has the following signature:
///
/// @code
/// std::string getGlobalParameter4(const ServerSelector&, const std::string&) const;
/// @endcode
///
///
/// @tparam PropertyType Type of the object returned by the backend call.
/// @tparam FnPtrArgs Parameter pack holding argument types of the backend
/// method to be invoked.
/// @tparam Args Parameter pack holding types of the arguments provided
/// in the call to this method.
///
/// @param MethodPointer Pointer to the backend method to be called.
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param [out] property Reference to the shared pointer where retrieved
/// property should be assigned.
/// @param input Values to be used as input to the backend call.
///
/// @throw db::NoSuchDatabase if no database matching the given selector
/// was found.
template<typename PropertyType, typename... FnPtrArgs, typename... Args>
void getOptionalPropertyConst(util::OptionalValue<PropertyType>
(ConfigBackendType::*MethodPointer)
(const db::ServerSelector&, FnPtrArgs...) const,
const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
util::OptionalValue<PropertyType>& property,
Args... input) const {
// If no particular backend is selected, call each backend and return
// the first non-null (non zero) value.
if (backend_selector.amUnspecified()) {
for (auto backend : backends_) {
property = ((*backend).*MethodPointer)(server_selector, input...);
if (property.isSpecified()) {
break;
}
}
} else {
// Backend selected, find the one that matches selection.
auto backends = selectBackends(backend_selector);
if (!backends.empty()) {
for (auto backend : backends) {
property = ((*backend).*MethodPointer)(server_selector, input...);
if (property.isSpecified()) {
break;
}
}
} else {
isc_throw(db::NoSuchDatabase, "no such database found for selector: "
<< backend_selector.toText());
}
}
}
/// @brief Retrieve multiple configuration properties from the pool.
///
/// This is a common method for retrieving multiple configuration properties
/// from the databases. The server specific backends call this method to
/// retrieve multiple objects of the same type. For example, the DHCPv6
/// configuration backend pool may use this function to implement a
/// @c getSubnets6 method:
///
/// @code
/// Subnet6Collection getModifiedSubnets6(const BackendSelector& backend_selector,
/// const ServerSelector& server_selector,
/// const ptime& modification_time) const {
/// Subnet6Collection subnets;
/// getMultiplePropertiesConst<Subnet6Collection, const ptime&>
/// (&ConfigBackendDHCPv6::getSubnets6, backend_selector, server_selector,
/// subnets, modification_time);
/// return (subnets);
/// }
/// @endcode
///
/// where @c ConfigBackendDHCPv6::getSubnets6 has the following signature:
///
/// @code
/// Subnet6Collection getSubnets6(const ServerSelector&, const ptime&) const;
/// @endcode
///
/// If the backend selector is set to "unspecified", this method will iterate
/// over existing backends and call the @c MethodPointer method on each
/// backend. It will return the first non-empty list returned by one of the
/// backends.
///
/// @tparam PropertyCollectionType Type of the container into which the
/// properties are stored.
/// @tparam FnPtrArgs Parameter pack holding argument types of the backend
/// method to be invoked.
/// @tparam Args Parameter pack holding types of the arguments provided
/// in the call to this method.
///
/// @param MethodPointer Pointer to the backend method to be called.
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param [out] properties Reference to the collection of retrieved properties.
/// @param input Values to be used as input to the backend call.
///
/// @throw db::NoSuchDatabase if no database matching the given selector
/// was found.
template<typename PropertyCollectionType, typename... FnPtrArgs, typename... Args>
void getMultiplePropertiesConst(PropertyCollectionType (ConfigBackendType::*MethodPointer)
(const db::ServerSelector&, FnPtrArgs...) const,
const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
PropertyCollectionType& properties,
Args... input) const {
if (backend_selector.amUnspecified()) {
for (auto backend : backends_) {
properties = ((*backend).*MethodPointer)(server_selector, input...);
if (!properties.empty()) {
break;
}
}
} else {
auto backends = selectBackends(backend_selector);
if (!backends.empty()) {
for (auto backend : backends) {
properties = ((*backend).*MethodPointer)(server_selector, input...);
if (!properties.empty()) {
break;
}
}
} else {
isc_throw(db::NoSuchDatabase, "no database found for selector: "
<< backend_selector.toText());
}
}
}
/// @brief Retrieve all configuration properties from the pool.
///
/// This is a common method for retrieving all configuration properties
/// from the databases. The server specific backends call this method
/// to retrieve all objects of the same type. For example, the DHCPv4
/// configuration backend pool may use this function to implement a
/// @c getAllSubnets4 method:
///
/// @code
/// Subnet4Collection getAllSubnets4(const BackendSelector&, const ServerSelector&) const {
/// Subnet4Collection subnets;
/// getAllPropertiesConst<Subnet6Collection>
/// (&ConfigBackendDHCPv4::getAllSubnets4, subnets, backend_selector,
/// server_selector);
/// return (subnets);
/// }
/// @endcode
///
/// where @c ConfigBackendDHCPv4::getAllSubnets4 has the following signature:
///
/// @code
/// Subnet4Collection getAllSubnets4(const ServerSelector&) const;
/// @endcode
///
/// If the backend selector is set to "unspecified", this method will iterate
/// over existing backends and call the @c MethodPointer method on each
/// backend. It will return the first non-empty list returned by one of the
/// backends.
///
/// @tparam PropertyCollectionType Type of the container into which the
/// properties are stored.
///
/// @param MethodPointer Pointer to the backend method to be called.
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param [out] properties Reference to the collection of retrieved properties.
///
/// @throw db::NoSuchDatabase if no database matching the given selector
/// was found.
template<typename PropertyCollectionType>
void getAllPropertiesConst(PropertyCollectionType (ConfigBackendType::*MethodPointer)
(const db::ServerSelector&) const,
const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
PropertyCollectionType& properties) const {
if (backend_selector.amUnspecified()) {
for (auto backend : backends_) {
properties = ((*backend).*MethodPointer)(server_selector);
if (!properties.empty()) {
break;
}
}
} else {
auto backends = selectBackends(backend_selector);
if (!backends.empty()) {
for (auto backend : backends) {
properties = ((*backend).*MethodPointer)(server_selector);
if (!properties.empty()) {
break;
}
}
} else {
isc_throw(db::NoSuchDatabase, "no database found for selector: "
<< backend_selector.toText());
}
}
}
/// @brief Add, update or delete property from the backend.
///
/// This is a common method for storing a single configuration property in
/// a database, updating an existing property or deleting the property.
/// The server specific backends call this method. For example,
/// the DHCPv6 configuration backend pool may use this function to implement
/// a @c createUpdateSubnet6 method:
///
/// @code
/// void createUpdateSubnet6(const Subnet6Ptr& subnet,
/// const BackendSelector& backend_selector,
/// const ServerSelector& server_selector) {
/// createUpdateDeleteProperty<const Subnet6Ptr&>
/// (&ConfigBackendDHCPv6::createUpdateSubnet6, backend_selector,
/// server_selector, subnet, selector);
/// }
/// @endcode
///
/// where @c ConfigBackendDHCPv6::createUpdateSubnet6 has the following
/// signature:
///
/// @code
/// void createUpdateSubnet6(const ServerSelector&, const Subnet6Ptr&);
/// @endcode
///
/// The backend selector must point to exactly one backend. If more than one
/// backend is selected, an exception is thrown. If no backend is selected
/// an exception is thrown either.
///
/// @tparam FnPtrArgs Parameter pack holding argument types of the backend
/// method to be invoked.
/// @tparam Args Parameter pack holding types of the arguments provided
/// in the call to this method.
///
/// @param MethodPointer Pointer to the backend method to be called.
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param input Objects used as arguments to the backend method to be
/// called.
///
/// @throw db::NoSuchDatabase if no database matching the given selector
/// was found.
/// @throw db::AmbiguousDatabase if multiple databases matching the selector
/// were found.
template<typename... FnPtrArgs, typename... Args>
void createUpdateDeleteProperty(void (ConfigBackendType::*MethodPointer)
(const db::ServerSelector&, FnPtrArgs...),
const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
Args... input) {
auto backends = selectBackends(backend_selector);
if (backends.empty()) {
isc_throw(db::NoSuchDatabase, "no database found for selector: "
<< backend_selector.toText());
} else if (backends.size() > 1) {
isc_throw(db::AmbiguousDatabase, "more than 1 database found for "
"selector: " << backend_selector.toText());
}
(*(*(backends.begin())).*MethodPointer)(server_selector, input...);
}
/// @brief Selects existing backends matching the selector.
///
/// This method selects backends matching the selector. If the selector is
/// "unspecified" or there is no backend in the pool, an empty list is returned.
///
/// @param selector Selector for which matching backends should be selected.
std::list<ConfigBackendTypePtr>
selectBackends(const db::BackendSelector& backend_selector) const {
std::list<ConfigBackendTypePtr> selected;
// In case there is only one backend and the caller hasn't specified
// any particular backend, simply return it.
if ((backends_.size() == 1) && backend_selector.amUnspecified()) {
selected.push_back(*backends_.begin());
return (selected);
}
// For other cases we return empty list.
if (backends_.empty() || backend_selector.amUnspecified()) {
return (selected);
}
// Go over all backends.
for (auto backend : backends_) {
// If backend type is specified and it is not matching,
// do not select this backend.
if ((backend_selector.getBackendType() != db::BackendSelector::Type::UNSPEC) &&
(backend_selector.getBackendType() !=
db::BackendSelector::stringToBackendType(backend->getType()))) {
continue;
}
// If the host has been specified by the backend's host is not
// matching, do not select this backend.
if ((!backend_selector.getBackendHost().empty()) &&
(backend_selector.getBackendHost() != backend->getHost())) {
continue;
}
// If the port has been specified by the backend's port is not
// matching, do not select this backend.
if ((backend_selector.getBackendPort() != 0) &&
(backend_selector.getBackendPort() != backend->getPort())) {
continue;
}
// Passed all checks, so the backend is matching. Add it to the list.
selected.push_back(backend);
}
return (selected);
}
/// @brief Holds configuration backends belonging to the pool.
std::list<ConfigBackendTypePtr> backends_;
};
} // end of namespace isc::cb
} // end of namespace isc
#endif // BASE_CONFIG_BACKEND_POOL_H

View File

@@ -0,0 +1 @@
/libcb_unittests

View File

@@ -0,0 +1,39 @@
SUBDIRS = .
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/config/tests\"
AM_CXXFLAGS = $(KEA_CXXFLAGS)
if USE_STATIC_LINK
AM_LDFLAGS = -static
endif
CLEANFILES = *.gcno *.gcda
TESTS_ENVIRONMENT = \
$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
TESTS =
if HAVE_GTEST
TESTS += libcb_unittests
libcb_unittests_SOURCES = config_backend_mgr_unittest.cc
libcb_unittests_SOURCES += run_unittests.cc
libcb_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
libcb_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
libcb_unittests_LDADD = $(top_builddir)/src/lib/database/libkea-database.la
libcb_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
libcb_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
libcb_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
libcb_unittests_LDADD += $(top_builddir)/src/lib/util/threads/libkea-threads.la
libcb_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
libcb_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
libcb_unittests_LDADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS) $(GTEST_LDADD)
endif
noinst_PROGRAMS = $(TESTS)

View File

@@ -0,0 +1,529 @@
// 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 <config_backend/base_config_backend_mgr.h>
#include <config_backend/base_config_backend_pool.h>
#include <config_backend/base_config_backend.h>
#include <database/database_connection.h>
#include <boost/shared_ptr.hpp>
#include <gtest/gtest.h>
#include <list>
#include <string>
#include <utility>
using namespace isc;
using namespace isc::cb;
using namespace isc::db;
namespace {
/// @brief Defines list of properties retrieved by @c TestConfigBackend.
///
/// A single property is a name/value pair, where value is an integer.
typedef std::list<std::pair<std::string, int> > PropertiesList;
/// @brief Implements configuration backend used in tests.
///
/// @c BaseConfigBackend is an abstract class that must be implemented
/// in order to allow us to test mechanisms implemented in
/// @c BaseConfigBackendMgr and @c BaseConfigBackendPool.
///
/// Normally, a class derived directly from the @c BaseConfigBackend
/// will merely provide an interface for server specific operations,
/// e.g. DHCPv4 specific operations, and the database specific classes
/// will implement this interface. However, for the test purposes it
/// is convenient to implement them here and let the derivations
/// only provide the implementations of the @c getType, @c getHost
/// and @c getPort functions. That way, the logic for adding and
/// retrieving the data from the backend is implemented only once and
/// is common accross all test backends.
///
/// This class provides a logic for managing test data being a collection
/// of name/value pairs, i.e. "properties". It contains a list of
/// properties and the functions for adding the properties, retrieving
/// a single property and retrieving a collection of properties.
class TestConfigBackend : public BaseConfigBackend {
public:
/// @brief Retrieves first property having a given name.
///
/// @param property_name Name of the property to be retrieved.
/// @return Value of the property or 0 if property doesn't exist.
virtual int getProperty(const ServerSelector&,
const std::string& property_name) const {
for (auto property : properties_) {
if (property.first == property_name) {
return (property.second);
}
}
return (0);
}
/// @brief Retrieves first property matching the name and value.
///
/// @param property_name Name of the property to be retrieved.
/// @param property_value Value of the property to be retrieved.
/// @return Value of the property or 0 if the property doesn't exist.
virtual int getProperty(const ServerSelector&,
const std::string& property_name,
const int property_value) const {
for (auto property : properties_) {
if ((property.first == property_name) &&
(property.second == property_value)) {
return (property.second);
}
}
return (0);
}
/// @brief Retrieves all properties having a given name.
///
/// @param property_name Name of the properties to be retrieved.
/// @return List of the properties having a given name. This list is
/// empty if no property was found.
virtual PropertiesList getProperties(const ServerSelector&,
const std::string& property_name) const {
PropertiesList properties;
for (auto property : properties_) {
if (property.first == property_name) {
properties.push_back(property);
}
}
return (properties);
}
/// @brief Retrieves all properties.
///
/// @return List of all properties held in the backend.
virtual PropertiesList getAllProperties(const ServerSelector&) const {
return (properties_);
}
/// @brief Creates new property.
///
/// @param new_property Property to be added to the backend.
virtual void createProperty(const ServerSelector&,
const std::pair<std::string, int>& new_property) {
properties_.push_back(new_property);
}
protected:
/// @brief Holds list of properties (simulates database).
PropertiesList properties_;
};
/// @brief Shared pointer to the @c TestConfigBackend.
typedef boost::shared_ptr<TestConfigBackend> TestConfigBackendPtr;
/// @brief First implementation of the test config backend.
///
/// It simulates being a MySQL backend installed on the
/// "mysql-host" host and running on port 2345.
class TestConfigBackendImpl1 : public TestConfigBackend {
public:
/// @brief Returns backend type.
///
/// @return "mysql".
virtual std::string getType() const {
return (std::string("mysql"));
}
/// @brief Returns backend host.
///
/// @return "mysql-host".
virtual std::string getHost() const {
return (std::string("mysql-host"));
}
/// @brief Returns backend port.
///
/// @return Port number 2345.
virtual uint16_t getPort() const {
return (2345);
}
};
/// @brief Shared pointer to the @c TestConfigBackendImpl1.
typedef boost::shared_ptr<TestConfigBackendImpl1> TestConfigBackendImpl1Ptr;
/// @brief Second implementation of the test config backend.
///
/// It simulates being a Postgres backend installed on the
/// "pgsql-host" host and running on port 1234.
class TestConfigBackendImpl2 : public TestConfigBackend {
public:
/// @brief Returns backend type.
///
/// @return "pgsql".
virtual std::string getType() const {
return (std::string("pgsql"));
}
/// @brief Returns backend host.
///
/// @return "pgsql-host".
virtual std::string getHost() const {
return (std::string("pgsql-host"));
}
/// @brief Returns backend port.
///
/// @return Port number 1234.
virtual uint16_t getPort() const {
return (1234);
}
};
/// @brief Shared pointer to the @c TestConfigBackendImpl2.
typedef boost::shared_ptr<TestConfigBackendImpl2> TestConfigBackendImpl2Ptr;
/// @brief Implements test pool of configuration backends.
///
/// @c BaseConfigBackendPool template provides mechanics for managing the data
/// stored in multiple backends. Server specific pools must extend this class
/// with methods for managing the data appropriate for the server types.
/// This class provides an example pool implementation for managing the
/// "properties" being name/value pairs. It extends the base class with
/// new methods to retrieve a single property and multiple properties. It
/// also adds a method to create new property. Those methods correspond to
/// the ones implemented in the @c TestConfigBackend, but also each of
/// them includes a "database selector" used to indicate the backend to
/// be used.
class TestConfigBackendPool : public BaseConfigBackendPool<TestConfigBackend> {
public:
/// @brief Retrieves a value of the property.
///
/// @param property_name Name of the property which value should be returned.
/// @param backend_selector Backend selector. The default value of the selector
/// is @c UNSPEC which means that the property will be searched in all backends
/// and the first value found will be returned.
/// @param server_selector Server selector. The default value is set to @c ALL,
/// which means that the property for all servers will be returned.
virtual int getProperty(const std::string& property_name,
const BackendSelector& backend_selector =
BackendSelector::UNSPEC(),
const ServerSelector& server_selector =
ServerSelector::ALL()) const {
int property;
// If the selector is specified, this method will pick the appropriate
// backend and will call getProperty method on this backend. If the
// selector is not specified, this method will iterate over existing
// backends and call getProperty on them. It will return after finding
// the first non-zero value of the property. For example, if the first
// backend contains a non-zero value this value will be returned and
// the value held in the second backend (if any) won't be fetched.
// The template arguments specify the returned value type and the
// argument of the getProperty method.
getPropertyPtrConst<int, const std::string&>
(&TestConfigBackend::getProperty, backend_selector, server_selector,
property, property_name);
return (property);
}
/// @brief Retrieves value of the property.
///
/// @param property_name Name of the property which value should be returned.
/// @param property_value Value of the property to be retrieved.
/// @param backend_selector Backend selector. The default value of the selector
/// is @c UNSPEC which means that the property will be searched in all backends
/// and the first value found will be returned.
/// @param server_selector Server selector. The default value is set to @c ALL,
/// which means that the property for all servers will be returned.
virtual int getProperty(const std::string& property_name,
const int property_value,
const BackendSelector& backend_selector =
BackendSelector::UNSPEC(),
const ServerSelector& server_selector =
ServerSelector::ALL()) const {
int property;
getPropertyPtrConst<int, const std::string&, int>
(&TestConfigBackend::getProperty, backend_selector, server_selector,
property, property_name, property_value);
return (property);
}
/// @brief Retrieves multiple properties.
///
/// @param property_name Name of the properties which should be retrieved.
/// @param backend_selector Backend selector. The default value of the selector
/// is @c UNSPEC which means that the properties will be searched in all
/// backends and the first non-empty list will be returned.
/// @param server_selector Server selector. The default value is set to @c ALL,
/// which means that the properties for all servers will be returned.
virtual PropertiesList getProperties(const std::string& property_name,
const BackendSelector& backend_selector =
BackendSelector::UNSPEC(),
const ServerSelector& server_selector =
ServerSelector::ALL()) const {
PropertiesList properties;
// If the selector is specified, this method will pick the appropriate
// backend and will call getProperties method on this backend. If the
// selector is not specified, this method will iterate over existing
// backends and call getProperties on them. It will return after finding
// the first non-empty list of properties in one of the backends.
// The template arguments specify the type of the list of properties
// and the argument of the getProperties method.
getMultiplePropertiesConst<PropertiesList, const std::string&>
(&TestConfigBackend::getProperties, backend_selector, server_selector,
properties, property_name);
return (properties);
}
/// @brief Retrieves all properties.
///
/// @param backend_selector Backend selector. The default value of the selector
/// is @c UNSPEC which means that the properties will be searched in all
/// backends and the first non-empty list will be returned.
/// @param server_selector Server selector. The default value is set to @c ALL,
/// which means that the properties for all servers will be returned.
virtual PropertiesList getAllProperties(const BackendSelector& backend_selector =
BackendSelector::UNSPEC(),
const ServerSelector& server_selector =
ServerSelector::ALL()) const {
PropertiesList properties;
// This method is similar to getMultiplePropertiesConst but it lacks
// an argument and it simply returns all properties.
getAllPropertiesConst<PropertiesList>
(&TestConfigBackend::getAllProperties, backend_selector, server_selector,
properties);
return (properties);
}
/// @brief Creates new property in a selected backend.
///
/// @param new_property New property to be added to a backend.
/// @param backend_selector Backend selector. It has no default value.
/// @param server_selector The default value is @c ALL which means that
/// new property is going to be shared by all servers.
virtual void createProperty(const std::pair<std::string, int>& new_property,
const BackendSelector& backend_selector,
const ServerSelector& server_selector =
ServerSelector::ALL()) {
createUpdateDeleteProperty<const std::pair<std::string, int>&>
(&TestConfigBackend::createProperty, backend_selector, server_selector,
new_property);
}
};
using TestConfigBackendMgr = BaseConfigBackendMgr<TestConfigBackendPool>;
/// @brief Test fixture class for testing @c ConfigBackendMgr.
class ConfigBackendMgrTest : public ::testing::Test {
public:
/// @brief Constructor.
ConfigBackendMgrTest()
: config_mgr_() {
}
/// @brief Destructor.
///
/// Removes backend instances.
~ConfigBackendMgrTest() {
config_mgr_.delAllBackends();
}
/// @brief Creates example database backend called "mysql".
///
/// It uses @c TestConfigBackendImpl1.
void addTestMySQLBackend() {
config_mgr_.registerBackendFactory("mysql", [](const DatabaseConnection::ParameterMap&)
-> TestConfigBackendPtr {
return (TestConfigBackendImpl1Ptr(new TestConfigBackendImpl1()));
});
config_mgr_.addBackend("type=mysql");
}
/// @brief Creates example database backend called "pgsql".
///
/// It uses @c TestConfigBackendImpl2.
void addTestPgSQLBackend() {
config_mgr_.registerBackendFactory("pgsql", [](const DatabaseConnection::ParameterMap&)
-> TestConfigBackendPtr {
return (TestConfigBackendImpl2Ptr(new TestConfigBackendImpl2()));
});
// Actually create the backends.
config_mgr_.addBackend("type=pgsql");
}
/// @brief Creates two example database backends called "mysql" and "pgsql".
///
/// It uses @c TestConfigBackendImpl1 and @c TestConfigBackendImpl2.
void addTestBackends() {
addTestMySQLBackend();
addTestPgSQLBackend();
}
/// @brief Adds test data into the backends.
void addTestData() {
// Add two properties with different names into the first backend.
config_mgr_.getPool()->createProperty(std::make_pair("dogs", 1),
BackendSelector(BackendSelector::Type::MYSQL));
config_mgr_.getPool()->createProperty(std::make_pair("wolves", 3),
BackendSelector(BackendSelector::Type::MYSQL));
// Add two properties into the second backend. Both properties share the
// name so as we can test retrieving multiple records from the same backend.
config_mgr_.getPool()->createProperty(std::make_pair("cats", 2),
BackendSelector(BackendSelector::Type::PGSQL));
config_mgr_.getPool()->createProperty(std::make_pair("cats", 4),
BackendSelector(BackendSelector::Type::PGSQL));
}
/// Instance of the test configuration manager.
TestConfigBackendMgr config_mgr_;
};
// Test that selector can be left "unspecified" if there is only one backend,
// when manipulating the data.
TEST_F(ConfigBackendMgrTest, createPropertySingleBackendUnspec) {
addTestMySQLBackend();
ASSERT_NO_THROW(
config_mgr_.getPool()->createProperty(std::make_pair("dogs", 1),
BackendSelector(BackendSelector::Type::UNSPEC))
);
// We should be able to retrieve stored value without having to specify
// the backend.
EXPECT_EQ(1, config_mgr_.getPool()->getProperty("dogs"));
}
// Test that selector must be specified if there is more than one backend,
// when manipulating the data.
TEST_F(ConfigBackendMgrTest, createPropertyTwoBackendsUnspec) {
addTestBackends();
// Backend must be selected if there is more than one backend present.
EXPECT_THROW(
config_mgr_.getPool()->createProperty(std::make_pair("dogs", 1),
BackendSelector(BackendSelector::Type::UNSPEC)),
NoSuchDatabase
);
}
// Test that exception is thrown when multiple backends are selected.
TEST_F(ConfigBackendMgrTest, createPropertyAmbiguousSelection) {
addTestBackends();
// Add another MySQL backend to cause the selection to give ambiguous
// result.
config_mgr_.addBackend("type=mysql");
EXPECT_THROW(
config_mgr_.getPool()->createProperty(std::make_pair("dogs", 1),
BackendSelector(BackendSelector::Type::MYSQL)),
AmbiguousDatabase
);
}
// Test that a single property can be retrieved from a selected backend.
TEST_F(ConfigBackendMgrTest, getSingleProperty) {
addTestBackends();
addTestData();
// Backend is not specified, so it should find the dogs in first one and
// cats in the second one.
EXPECT_EQ(1, config_mgr_.getPool()->getProperty("dogs"));
EXPECT_EQ(2, config_mgr_.getPool()->getProperty("cats"));
// No dogs in the pgsql backend and no cats in mysql backend.
EXPECT_EQ(0, config_mgr_.getPool()->getProperty("dogs",
BackendSelector(BackendSelector::Type::PGSQL)));
EXPECT_EQ(0, config_mgr_.getPool()->getProperty("cats",
BackendSelector(BackendSelector::Type::MYSQL)));
// If the selectors are pointing to the right databases, the dogs and cats
// should be returned properly.
EXPECT_EQ(1, config_mgr_.getPool()->getProperty("dogs",
BackendSelector(BackendSelector::Type::MYSQL)));
EXPECT_EQ(2, config_mgr_.getPool()->getProperty("cats",
BackendSelector(BackendSelector::Type::PGSQL)));
// Also make sure that the variant of getProperty function taking two arguments
// would return the value.
EXPECT_EQ(1, config_mgr_.getPool()->getProperty("dogs", 1,
BackendSelector(BackendSelector::Type::MYSQL)));
// If the value is not matching it should return 0.
EXPECT_EQ(0, config_mgr_.getPool()->getProperty("dogs", 2,
BackendSelector(BackendSelector::Type::MYSQL)));
// Try to use the backend that is not present.
EXPECT_THROW(config_mgr_.getPool()->getProperty("cats",
BackendSelector(BackendSelector::Type::CQL)),
NoSuchDatabase);
}
// Test that multiple properties can be retrieved with filtering.
TEST_F(ConfigBackendMgrTest, getMultipleProperties) {
addTestBackends();
addTestData();
// There is one dogs entry in mysql.
PropertiesList mysql_list =
config_mgr_.getPool()->getProperties("dogs",
BackendSelector(BackendSelector::Type::MYSQL));
ASSERT_EQ(1, mysql_list.size());
// There is also one wolves entry in mysql.
mysql_list = config_mgr_.getPool()->getProperties("wolves",
BackendSelector(BackendSelector::Type::MYSQL));
ASSERT_EQ(1, mysql_list.size());
// There are two cats entries in pgsql.
PropertiesList pgsql_list =
config_mgr_.getPool()->getProperties("cats",
BackendSelector(BackendSelector::Type::PGSQL));
ASSERT_EQ(2, pgsql_list.size());
// Try to use the backend that is not present.
EXPECT_THROW(config_mgr_.getPool()->getProperties("cats",
BackendSelector(BackendSelector::Type::CQL)),
NoSuchDatabase);
}
// Test that all properties can be retrieved from each backend.
TEST_F(ConfigBackendMgrTest, getAllProperties) {
addTestBackends();
addTestData();
// The mysql backend holds two entries.
PropertiesList mysql_list =
config_mgr_.getPool()->getAllProperties(BackendSelector(BackendSelector::Type::MYSQL));
ASSERT_EQ(2, mysql_list.size());
// The pgsql backends also holds two entries.
PropertiesList pgsql_list =
config_mgr_.getPool()->getAllProperties(BackendSelector(BackendSelector::Type::PGSQL));
ASSERT_EQ(2, pgsql_list.size());
// Try to use the backend that is not present.
EXPECT_THROW(config_mgr_.getPool()->getProperties("cats",
BackendSelector(BackendSelector::Type::CQL)),
NoSuchDatabase);
}
}

View File

@@ -0,0 +1,19 @@
// 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 <log/logger_support.h>
#include <gtest/gtest.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
int result = RUN_ALL_TESTS();
return (result);
}

View File

@@ -23,10 +23,12 @@ EXTRA_DIST = db_messages.mes
CLEANFILES = *.gcno *.gcda db_messages.h db_messages.cc s-messages
lib_LTLIBRARIES = libkea-database.la
libkea_database_la_SOURCES = database_connection.cc database_connection.h
libkea_database_la_SOURCES = backend_selector.cc backend_selector.h
libkea_database_la_SOURCES += database_connection.cc database_connection.h
libkea_database_la_SOURCES += dbaccess_parser.h dbaccess_parser.cc
libkea_database_la_SOURCES += db_exceptions.h
libkea_database_la_SOURCES += db_log.cc db_log.h
libkea_database_la_SOURCES += server_selector.h
nodist_libkea_database_la_SOURCES = db_messages.cc db_messages.h
@@ -43,6 +45,7 @@ libkea_database_la_LDFLAGS = -no-undefined -version-info 0:0:0
# Specify the headers for copying into the installation directory tree.
libkea_database_includedir = $(pkgincludedir)/database
libkea_database_include_HEADERS = \
backend_selector.h \
database_connection.h \
db_exceptions.h \
db_log.h

View File

@@ -0,0 +1,152 @@
// 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 <database/backend_selector.h>
#include <exceptions/exceptions.h>
#include <climits>
#include <sstream>
using namespace isc::data;
namespace isc {
namespace db {
BackendSelector::BackendSelector()
: backend_type_(BackendSelector::Type::UNSPEC),
host_(), port_(0) {
}
BackendSelector::BackendSelector(const Type& backend_type)
: backend_type_(backend_type),
host_(), port_(0) {
}
BackendSelector::BackendSelector(const std::string& host,
const uint16_t port)
: backend_type_(BackendSelector::Type::UNSPEC),
host_(host), port_(port) {
validate();
}
BackendSelector::BackendSelector(const data::ConstElementPtr& access_map)
: backend_type_(BackendSelector::Type::UNSPEC),
host_(), port_(0) {
if (access_map->getType() != Element::map) {
isc_throw(BadValue, "database access information must be a map");
}
ConstElementPtr t = access_map->get("type");
if (t) {
if (t->getType() != Element::string) {
isc_throw(BadValue, "'type' parameter must be a string");
}
backend_type_ = stringToBackendType(t->stringValue());
}
ConstElementPtr h = access_map->get("host");
if (h) {
if (h->getType() != Element::string) {
isc_throw(BadValue, "'host' parameter must be a string");
}
host_ = h->stringValue();
}
ConstElementPtr p = access_map->get("port");
if (p) {
if ((p->getType() != Element::integer) ||
(p->intValue() < 0) ||
(p->intValue() > std::numeric_limits<uint16_t>::max())) {
isc_throw(BadValue, "'port' parameter must be a number in range from 0 "
"to " << std::numeric_limits<uint16_t>::max());
}
port_ = static_cast<uint16_t>(p->intValue());
}
validate();
}
const BackendSelector&
BackendSelector::BackendSelector::UNSPEC() {
static BackendSelector selector;
return (selector);
}
bool
BackendSelector::amUnspecified() const {
return ((backend_type_ == BackendSelector::Type::UNSPEC) &&
(host_.empty()) &&
(port_ == 0));
}
std::string
BackendSelector::toText() const {
std::ostringstream s;
if (amUnspecified()) {
s << "unspecified";
} else {
if (backend_type_ != BackendSelector::Type::UNSPEC) {
s << "type=" << backendTypeToString(backend_type_) << ",";
}
if (!host_.empty()) {
s << "host=" << host_ << ",";
if (port_ > 0) {
s << "port=" << port_ << ",";
}
}
}
std::string text = s.str();
if ((!text.empty() && (text.back() == ','))) {
text.pop_back();
}
return (text);
}
BackendSelector::Type
BackendSelector::stringToBackendType(const std::string& type) {
if (type == "mysql") {
return (BackendSelector::Type::MYSQL);
} else if (type == "pgsql") {
return (BackendSelector::Type::PGSQL);
} else if (type == "cql") {
return (BackendSelector::Type::CQL);
} else {
isc_throw(BadValue, "unsupported configuration backend type '" << type << "'");
}
}
std::string
BackendSelector::backendTypeToString(const BackendSelector::Type& type) {
switch (type) {
case BackendSelector::Type::MYSQL:
return ("mysql");
case BackendSelector::Type::PGSQL:
return ("pgsql");
case BackendSelector::Type::CQL:
return ("cql");
default:
;
}
return (std::string());
}
void
BackendSelector::validate() const {
if ((port_ != 0) && (host_.empty())) {
isc_throw(BadValue, "'host' must be specified along with 'port' parameter");
}
}
} // end of namespace isc::db
} // end of namespace isc

View File

@@ -0,0 +1,208 @@
// 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 BACKEND_SELECTOR_H
#define BACKEND_SELECTOR_H
#include <cc/data.h>
#include <cstdint>
#include <string>
namespace isc {
namespace db {
/// @brief Config Backend selector.
///
/// Each Kea server using database as a configuration respository
/// may use multiple configuration backends simultaneously. The most
/// typical case is to use a single configuration backend per server,
/// but there are use cases when configuration information is distributed
/// accross multiple database instances. In the future, there may be
/// also caching mechanisms implemented, which will allow for storing
/// results of certain database queries in memory.
///
/// From the server perspective, the most common use of the configuration
/// backend is to fetch entire configuration information from the
/// databases (upon startup) or fetch the latest updates to the
/// configuration, e.g. new subnet added, DHCP option modified etc.
/// In those cases, it is not so important from the server which backend
/// this data come from. Therefore, the server would fetch this information
/// from all available backends.
///
/// When the server administrator wants to insert some new data into
/// the database, modify existing data or simply wants to check the
/// contents of one of the database instance, he would specify which
/// database backend he wants to direct queries to.
///
/// The @c BackendSelector class provides means to specify whether
/// the queries should be directed to any backend (see server case
/// above) or to a specific backend (data insertion case above).
/// In addition, the @c BackendSelector allows for using various
/// criteria for selecting a backend to use. Currently those criteria
/// are: database type (e.g. mysql), database host and database port.
/// In order to use a specific port, the database host must also be
/// specified. Note that in a general case multiple backends of the
/// same type can be used simultaneously, e.g. multiple MySQL backends.
/// In that case, it may be necessary to specify host (and port) to
/// issue a query to the right one.
///
/// The @c BackendSelector class may be extended in the future to provide
/// additional backend selection criteria.
class BackendSelector {
public:
/// @brief Supported database types.
///
/// The @c UNSPEC indicates that the database type is not specified
/// as selection criteria.
enum class Type {
MYSQL,
PGSQL,
CQL,
UNSPEC
};
/// @brief Default constructor.
///
/// It sets the selector to "unspecified". When this selector is used
/// the backend pool will use "any" backend. This has a different meaning
/// for each type of query. See the @c BaseConfigBackendPool for details.
explicit BackendSelector();
/// @brief Constructor specifying backend type as a selection criteria.
///
/// @param backend_type Type of the backend to be selected.
explicit BackendSelector(const Type& backend_type);
/// @brief Constructor for specifying host and optionally port as a
/// selection criteria.
///
/// @param host Hostname to be used for selecting a backend.
/// @param port Port number to be used for selecting a backend. This value
/// is optional and is ignored when set to 0. It must be used on conjuction
/// with hostname.
explicit BackendSelector(const std::string& host, const uint16_t port = 0);
/// @brief Constructor for selecting a backend using JSON access map.
///
/// The provided access map must have the same structure as an element
/// of the "config-databases" configuration parameter. However, it merely
/// takes into account: "type", "host" and "port" parameters. In addition,
/// these parameters are optional. The following are valid combinations:
///
/// @code
/// {
/// "type": "mysql"
/// }
/// @endcode
///
/// @code
/// {
/// "host": "somehost.example.org"
/// }
/// @endcode
///
/// @code
/// {
/// "host": "somehost.example.org",
/// "port": 1234
/// }
/// @endcode
///
/// @code
/// {
/// "type": "mysql"
/// "host": "somehost.example.org",
/// }
/// @endcode
///
/// @code
/// {
/// "type": "mysql"
/// "host": "somehost.example.org",
/// "port": 1234
/// }
/// @endcode
///
/// where "type" can be any of the supported backend types.
///
/// This constructor is useful for creating backend selectors from the
/// received control commands.
///
/// @param access_map Access map as provided above.
explicit BackendSelector(const data::ConstElementPtr& access_map);
/// @brief Returns instance of the "unspecified" backend selector.
static const BackendSelector& UNSPEC();
/// @brief Checks if selector is "unspecified".
///
/// @return true if backend type is @c UNSPEC, hostname is empty and
/// port number 0, false otherwise.
bool amUnspecified() const;
/// @brief Returns backend type selected.
Type getBackendType() const {
return (backend_type_);
}
/// @brief Returns host selected.
///
/// @return host if specified or empty string if host is not
/// specified.
std::string getBackendHost() const {
return (host_);
}
/// @brief Returns port selected.
///
/// @return port number of the selected backend or 0 if port number
/// is not specified.
uint16_t getBackendPort() const {
return (port_);
}
/// @brief Returns selections as text.
///
/// @return Collection of comma separated selections, e.g.
/// "type=mysql,host=somehost.example.org,port=1234".
std::string toText() const;
/// @brief Converts string to backend type.
///
/// @param type Backend type as string.
static Type stringToBackendType(const std::string& type);
/// @brief Converts backend type to string.
///
/// @param type Backend type to be converted.
static std::string backendTypeToString(const Type& type);
private:
/// @brief Checks if the specified selector is valid.
///
/// It checks if the port number is specified in conjuction with
/// host.
/// @throw BadValue if selector validation fails.
void validate() const;
/// @brief Backend type selected.
Type backend_type_;
/// @brief Host selected.
std::string host_;
/// @brief Port number selected.
uint16_t port_;
};
} // end of namespace isc::db
} // end of namespace isc
#endif

View File

@@ -74,6 +74,22 @@ public:
isc::Exception(file, line, what) {}
};
/// @brief Error when specified database could not be found in the server
/// configuration.
class NoSuchDatabase : public Exception {
public:
NoSuchDatabase(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) {}
};
/// @brief Specification of the database backend to be used yields
/// multiple results.
class AmbiguousDatabase : public Exception {
public:
AmbiguousDatabase(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) {}
};
} // namespace isc
} // namespace db

View File

@@ -0,0 +1,126 @@
// 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 SERVER_SELECTOR_H
#define SERVER_SELECTOR_H
#include <set>
#include <string>
namespace isc {
namespace db {
/// @brief Server selector for associating objects in a database with
/// specific servers.
///
/// Configuration information stored in the configuration backends can be
/// associated with selected servers, all servers or no particular server.
/// For example: a particular subnet definition in the database may be
/// associated with one server or can be shared by multiple servers.
/// In the latter case, a subnet may be associated with a subset of
/// servers or all servers. An administrator may also add the
/// configuration data into the database and do not associate this data
/// with any patrticular server.
///
/// When fetching the configuration data from a databse or when storing
/// data in the database there is a need to specify which servers this
/// data is associated with. The @c ServerSelector class represents
/// such associations.
///
/// It includes three modes of selection: UNASSIGNED, ALL and SUBSET and
/// several factory functions making associations described above.
///
/// The @c ServerSelector class should be used in objects derived from
/// @c BaseConfigBackendPool and in objects derived from
/// @c BaseConfigBackend to indicate which servers the specific calls
/// exposed by these objects refer to.
class ServerSelector {
public:
/// @brief Type of the server selection.
enum class Type {
UNASSIGNED,
ALL,
SUBSET
};
/// @brief Factory returning "unassigned" server selector.
static ServerSelector& UNASSIGNED() {
static ServerSelector selector(Type::UNASSIGNED);
return (selector);
}
/// @brief Factory returning "all servers" selector.
static ServerSelector& ALL() {
static ServerSelector selector(Type::ALL);
return (selector);
}
/// @brief Factory returning selector of one server.
///
/// @param server_tag tag of the single server to be selected.
static ServerSelector& ONE(const std::string& server_tag) {
static ServerSelector selector(server_tag);
return (selector);
}
/// @brief Factory returning "multiple servers" selector.
///
/// @param server_tags set of server tags to be selected.
static ServerSelector& MULTIPLE(const std::set<std::string>& server_tags) {
static ServerSelector selector(server_tags);
return (selector);
}
/// @brief Returns type of the selector.
Type getType() const {
return (type_);
}
/// @brief Returns tags associated with the selector.
///
/// @return server tags for mutliple selections and for one server,
/// empty set for all servers and and unassigned.
std::set<std::string> getTags() const {
return (tags_);
}
private:
/// @brief Constructor used for "unassigned" and "all" slection types.
///
/// @param type selector type.
explicit ServerSelector(const Type& type)
: type_(type), tags_() {
}
/// @brief Constructor used for selecting a single server.
///
/// @param server_tag tag of the server to be selected.
explicit ServerSelector(const std::string& server_tag)
: type_(Type::SUBSET), tags_() {
tags_.insert(server_tag);
}
/// @brief Constructor used for selecting multiple servers.
///
/// @param server_tags set of server tags.
explicit ServerSelector(const std::set<std::string>& server_tags)
: type_(Type::SUBSET), tags_(server_tags) {
}
/// @brief Selection type used.
Type type_;
/// @brief Holds tags of explicitly selected servers.
std::set<std::string> tags_;
};
} // end of namespace isc::db
} // end of namespace isc
#endif

View File

@@ -19,9 +19,11 @@ TESTS =
if HAVE_GTEST
TESTS += libdatabase_unittests
libdatabase_unittests_SOURCES = database_connection_unittest.cc
libdatabase_unittests_SOURCES = backend_selector_unittest.cc
libdatabase_unittests_SOURCES += database_connection_unittest.cc
libdatabase_unittests_SOURCES += dbaccess_parser_unittest.cc
libdatabase_unittests_SOURCES += run_unittests.cc
libdatabase_unittests_SOURCES += server_selector_unittest.cc
libdatabase_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
libdatabase_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)

View File

@@ -0,0 +1,174 @@
// 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 <database/backend_selector.h>
#include <boost/scoped_ptr.hpp>
#include <gtest/gtest.h>
using namespace isc;
using namespace isc::db;
using namespace isc::data;
namespace {
// Verifies defaults of the backend selector.
TEST(BackendSelectorTest, defaults) {
BackendSelector sel;
EXPECT_EQ(BackendSelector::Type::UNSPEC, sel.getBackendType());
EXPECT_TRUE(sel.getBackendHost().empty());
EXPECT_EQ(0, sel.getBackendPort());
EXPECT_TRUE(sel.amUnspecified());
EXPECT_EQ("unspecified", sel.toText());
}
// Verifies that the backend selector can be set to "unspecified".
TEST(BackendSelectorTest, unspec) {
BackendSelector sel = BackendSelector::UNSPEC();
EXPECT_EQ(BackendSelector::Type::UNSPEC, sel.getBackendType());
EXPECT_TRUE(sel.getBackendHost().empty());
EXPECT_EQ(0, sel.getBackendPort());
EXPECT_TRUE(sel.amUnspecified());
EXPECT_EQ("unspecified", sel.toText());
}
// Verifies that it is possible to select backend by type.
TEST(BackendSelectorTest, backendTypeSpec) {
boost::scoped_ptr<BackendSelector> sel;
ASSERT_NO_THROW(
sel.reset(new BackendSelector(BackendSelector::Type::MYSQL))
);
EXPECT_EQ(BackendSelector::Type::MYSQL, sel->getBackendType());
EXPECT_TRUE(sel->getBackendHost().empty());
EXPECT_EQ(0, sel->getBackendPort());
EXPECT_FALSE(sel->amUnspecified());
EXPECT_EQ("type=mysql", sel->toText());
}
// Verifies that backend can be selected by host and port.
TEST(BackendSelectorTest, backendHostPortSpec) {
boost::scoped_ptr<BackendSelector> sel;
ASSERT_NO_THROW(
sel.reset(new BackendSelector("myhost", 1234))
);
EXPECT_EQ(BackendSelector::Type::UNSPEC, sel->getBackendType());
EXPECT_EQ("myhost", sel->getBackendHost());
EXPECT_EQ(1234, sel->getBackendPort());
EXPECT_FALSE(sel->amUnspecified());
EXPECT_EQ("host=myhost,port=1234", sel->toText());
}
// Verifies that backend can be selected by host.
TEST(BackendSelectorTest, backendHostSpec) {
boost::scoped_ptr<BackendSelector> sel;
ASSERT_NO_THROW(
sel.reset(new BackendSelector("otherhost"))
);
EXPECT_EQ(BackendSelector::Type::UNSPEC, sel->getBackendType());
EXPECT_EQ("otherhost", sel->getBackendHost());
EXPECT_EQ(0, sel->getBackendPort());
EXPECT_FALSE(sel->amUnspecified());
EXPECT_EQ("host=otherhost", sel->toText());
}
// Verifies that backend becomes unspecified if the access
// map is empty.
TEST(BackendSelectorTest, accessMapTypeUnSpec) {
ElementPtr access_map = Element::createMap();
boost::scoped_ptr<BackendSelector> sel;
ASSERT_NO_THROW(
sel.reset(new BackendSelector(access_map))
);
EXPECT_EQ(BackendSelector::Type::UNSPEC, sel->getBackendType());
EXPECT_TRUE(sel->getBackendHost().empty());
EXPECT_EQ(0, sel->getBackendPort());
EXPECT_TRUE(sel->amUnspecified());
EXPECT_EQ("unspecified", sel->toText());
}
// Verifies that backend can be selected by type using access map.
TEST(BackendSelectorTest, accessMapTypeSpec) {
ElementPtr access_map = Element::createMap();
access_map->set("type", Element::create("mysql"));
boost::scoped_ptr<BackendSelector> sel;
ASSERT_NO_THROW(
sel.reset(new BackendSelector(access_map))
);
EXPECT_EQ(BackendSelector::Type::MYSQL, sel->getBackendType());
EXPECT_TRUE(sel->getBackendHost().empty());
EXPECT_EQ(0, sel->getBackendPort());
EXPECT_FALSE(sel->amUnspecified());
EXPECT_EQ("type=mysql", sel->toText());
}
// Verifies that backend can be selected by host and port using
// access map.
TEST(BackendSelectorTest, accessMapHostPortSpec) {
ElementPtr access_map = Element::createMap();
access_map->set("host", Element::create("myhost"));
access_map->set("port", Element::create(int64_t(1234)));
boost::scoped_ptr<BackendSelector> sel;
ASSERT_NO_THROW(
sel.reset(new BackendSelector(access_map))
);
EXPECT_EQ(BackendSelector::Type::UNSPEC, sel->getBackendType());
EXPECT_EQ("myhost", sel->getBackendHost());
EXPECT_EQ(1234, sel->getBackendPort());
EXPECT_FALSE(sel->amUnspecified());
EXPECT_EQ("host=myhost,port=1234", sel->toText());
}
// Verifies that the backend can be selected by host using access
// map.
TEST(BackendSelectorTest, accessMapHostSpec) {
ElementPtr access_map = Element::createMap();
access_map->set("host", Element::create("myhost"));
boost::scoped_ptr<BackendSelector> sel;
ASSERT_NO_THROW(
sel.reset(new BackendSelector(access_map))
);
EXPECT_EQ(BackendSelector::Type::UNSPEC, sel->getBackendType());
EXPECT_EQ("myhost", sel->getBackendHost());
EXPECT_EQ(0, sel->getBackendPort());
EXPECT_FALSE(sel->amUnspecified());
EXPECT_EQ("host=myhost", sel->toText());
}
// Verifies that selecting backend by port only is not possible.
TEST(BackendSelectorTest, accessMapPortSpec) {
ElementPtr access_map = Element::createMap();
access_map->set("port", Element::create(int64_t(1234)));
boost::scoped_ptr<BackendSelector> sel;
EXPECT_THROW(sel.reset(new BackendSelector(access_map)),
BadValue);
}
// Tests conversions of strings to backend types.
TEST(BackendSelectorTest, stringToBackendType) {
EXPECT_EQ(BackendSelector::Type::MYSQL,
BackendSelector::stringToBackendType("mysql"));
EXPECT_EQ(BackendSelector::Type::PGSQL,
BackendSelector::stringToBackendType("pgsql"));
EXPECT_EQ(BackendSelector::Type::CQL,
BackendSelector::stringToBackendType("cql"));
EXPECT_THROW(BackendSelector::stringToBackendType("unsupported"),
BadValue);
}
// Tests conversions of backend types to strings.
TEST(BackendSelectorTest, backendTypeToString) {
EXPECT_EQ("mysql",
BackendSelector::backendTypeToString(BackendSelector::Type::MYSQL));
EXPECT_EQ("pgsql",
BackendSelector::backendTypeToString(BackendSelector::Type::PGSQL));
EXPECT_EQ("cql",
BackendSelector::backendTypeToString(BackendSelector::Type::CQL));
}
}

View File

@@ -0,0 +1,51 @@
// 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 <database/server_selector.h>
#include <gtest/gtest.h>
using namespace isc::db;
namespace {
// Check that server selector can be set to UNASSIGNED.
TEST(ServerSelectorTest, unassigned) {
ServerSelector selector = ServerSelector::UNASSIGNED();
EXPECT_EQ(ServerSelector::Type::UNASSIGNED, selector.getType());
EXPECT_TRUE(selector.getTags().empty());
}
// Check that server selector can be set to ALL.
TEST(ServerSelectorTest, all) {
ServerSelector selector = ServerSelector::ALL();
EXPECT_EQ(ServerSelector::Type::ALL, selector.getType());
EXPECT_TRUE(selector.getTags().empty());
}
// Check that a single server can be selected.
TEST(ServerSelectorTest, one) {
ServerSelector selector = ServerSelector::ONE("some-tag");
EXPECT_EQ(ServerSelector::Type::SUBSET, selector.getType());
std::set<std::string> tags = selector.getTags();
ASSERT_EQ(1, tags.size());
EXPECT_EQ(1, tags.count("some-tag"));
}
// Check that multiple servers can be selected.
TEST(ServerSelectorTest, multiple) {
ServerSelector selector = ServerSelector::MULTIPLE({ "tag1", "tag2", "tag3" });
EXPECT_EQ(ServerSelector::Type::SUBSET, selector.getType());
std::set<std::string> tags = selector.getTags();
ASSERT_EQ(3, tags.size());
EXPECT_EQ(1, tags.count("tag1"));
EXPECT_EQ(1, tags.count("tag2"));
EXPECT_EQ(1, tags.count("tag3"));
}
}

View File

@@ -31,8 +31,6 @@ AM_CXXFLAGS = $(KEA_CXXFLAGS)
EXTRA_DIST =
EXTRA_DIST += parsers/client_class_def_parser.cc
EXTRA_DIST += parsers/client_class_def_parser.h
EXTRA_DIST += parsers/dbaccess_parser.cc
EXTRA_DIST += parsers/dbaccess_parser.h
EXTRA_DIST += parsers/dhcp_parsers.cc
EXTRA_DIST += parsers/dhcp_parsers.h
EXTRA_DIST += parsers/expiration_config_parser.cc
@@ -110,11 +108,12 @@ libkea_dhcpsrv_la_SOURCES += cfg_subnets6.cc cfg_subnets6.h
libkea_dhcpsrv_la_SOURCES += cfg_mac_source.cc cfg_mac_source.h
libkea_dhcpsrv_la_SOURCES += cfgmgr.cc cfgmgr.h
libkea_dhcpsrv_la_SOURCES += client_class_def.cc client_class_def.h
libkea_dhcpsrv_la_SOURCES += config_backend_dhcp4.h
libkea_dhcpsrv_la_SOURCES += config_backend_pool_dhcp4.cc config_backend_pool_dhcp4.h
libkea_dhcpsrv_la_SOURCES += csv_lease_file4.cc csv_lease_file4.h
libkea_dhcpsrv_la_SOURCES += csv_lease_file6.cc csv_lease_file6.h
libkea_dhcpsrv_la_SOURCES += d2_client_cfg.cc d2_client_cfg.h
libkea_dhcpsrv_la_SOURCES += d2_client_mgr.cc d2_client_mgr.h
libkea_dhcpsrv_la_SOURCES += db_exceptions.h
libkea_dhcpsrv_la_SOURCES += db_type.h
libkea_dhcpsrv_la_SOURCES += dhcp4o6_ipc.cc dhcp4o6_ipc.h
libkea_dhcpsrv_la_SOURCES += dhcpsrv_exceptions.h
@@ -199,7 +198,7 @@ libkea_dhcpsrv_la_CXXFLAGS = $(AM_CXXFLAGS)
libkea_dhcpsrv_la_CPPFLAGS = $(AM_CPPFLAGS)
libkea_dhcpsrv_la_LIBADD = $(top_builddir)/src/lib/eval/libkea-eval.la
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la
#libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/process/libkea-process.la
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/process/libkea-process.la
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/stats/libkea-stats.la
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
@@ -273,6 +272,8 @@ libkea_dhcpsrv_include_HEADERS = \
cfg_subnets6.h \
cfgmgr.h \
client_class_def.h \
config_backend_dhcp4.h \
config_backend_pool_dhcp4.h \
csv_lease_file4.h \
csv_lease_file6.h \
dhcpsrv_exceptions.h \

View File

@@ -0,0 +1,323 @@
// 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 CONFIG_BACKEND_DHCP4_H
#define CONFIG_BACKEND_DHCP4_H
#include <config_backend/base_config_backend.h>
#include <database/server_selector.h>
#include <dhcp/option.h>
#include <dhcp/option_definition.h>
#include <dhcpsrv/shared_network.h>
#include <dhcpsrv/subnet.h>
#include <util/optional_value.h>
#include <boost/date_time/posix_time/ptime.hpp>
#include <map>
#include <string>
namespace isc {
namespace dhcp {
/// @brief Interface implemented by DHCPv4 configuration backends.
class ConfigBackendDHCPv4 : public cb::BaseConfigBackend {
public:
/// @brief Virtual destructor.
virtual ~ConfigBackendDHCPv4() { }
/// @brief Retrieves a single subnet by subnet_prefix.
///
/// @param selector Server selector.
/// @param subnet_prefix Prefix of the subnet to be retrieved.
/// @return Pointer to the retrieved subnet or NULL if not found.
virtual Subnet4Ptr
getSubnet4(const db::ServerSelector& selector,
const std::string& subnet_prefix) const = 0;
/// @brief Retrieves a single subnet by subnet identifier.
///
/// @param selector Server selector.
/// @param subnet_id Identifier of a subnet to be retrieved.
/// @return Pointer to the retrieved subnet or NULL if not found.
virtual Subnet4Ptr
getSubnet4(const db::ServerSelector& selector, const SubnetID& subnet_id) const = 0;
/// @brief Retrieves all subnets.
///
/// @param selector Server selector.
/// @return Collection of subnets or empty collection if no subnet found.
virtual Subnet4Collection
getAllSubnets4(const db::ServerSelector& selector) const = 0;
/// @brief Retrieves subnets modified after specified time.
///
/// @param selector Server selector.
/// @param modification_time Lower bound subnet modification time.
/// @return Collection of subnets or empty collection if no subnet found.
virtual Subnet4Collection
getModifiedSubnets4(const db::ServerSelector& selector,
const boost::posix_time::ptime& modification_time) const = 0;
/// @brief Retrieves shared network by name.
///
/// @param selector Server selector.
/// @param name Name of the shared network to be retrieved.
/// @return Pointer to the shared network or NULL if not found.
virtual SharedNetwork4Ptr
getSharedNetwork4(const db::ServerSelector& selector,
const std::string& name) const = 0;
/// @brief Retrieves all shared networks.
///
/// @param selector Server selector.
/// @return Collection of shared network or empty collection if
/// no shared network found.
virtual SharedNetwork4Collection
getAllSharedNetworks4(const db::ServerSelector& selector) const = 0;
/// @brief Retrieves shared networks modified after specified time.
///
/// @param selector Server selector.
/// @param modification_time Lower bound shared network modification time.
/// @return Collection of shared network or empty collection if
/// no shared network found.
virtual SharedNetwork4Collection
getModifiedSharedNetworks4(const db::ServerSelector& selector,
const boost::posix_time::ptime& modification_time) const = 0;
/// @brief Retrieves single option definition by code and space.
///
/// @param selector Server selector.
/// @param code Code of the option to be retrieved.
/// @param space Option space of the option to be retrieved.
/// @return Pointer to the option definition or NULL if not found.
virtual OptionDefinitionPtr
getOptionDef4(const db::ServerSelector& selector, const uint16_t code,
const std::string& space) const = 0;
/// @brief Retrieves all option definitions.
///
/// @param selector Server selector.
/// @return Collection of option definitions or empty collection if
/// no option definition found.
virtual OptionDefContainer
getAllOptionDefs4(const db::ServerSelector& selector) const = 0;
/// @brief Retrieves option definitions modified after specified time.
///
/// @param selector Server selector.
/// @param modification_time Lower bound option definition modification
/// time.
/// @return Collection of option definitions or empty collection if
/// no option definition found.
virtual OptionDefContainer
getModifiedOptionDefs4(const db::ServerSelector& selector,
const boost::posix_time::ptime& modification_time) const = 0;
/// @brief Retrieves global string parameter value.
///
/// @param selector Server selector.
/// @param name Name of the global parameter to be retrieved.
/// @return Value of the global string parameter.
virtual util::OptionalValue<std::string>
getGlobalStringParameter4(const db::ServerSelector& selector,
const std::string& name) const = 0;
/// @brief Retrieves global number parameter.
///
/// @param selector Server selector.
/// @param name Name of the parameter to be retrieved.
virtual util::OptionalValue<int64_t>
getGlobalNumberParameter4(const db::ServerSelector& selector,
const std::string& name) const = 0;
/// @brief Retrieves all global parameters as strings.
///
/// @param selector Server selector.
virtual std::map<std::string, std::string>
getAllGlobalParameters4(const db::ServerSelector& selector) const = 0;
/// @brief Creates or updates a subnet.
///
/// @param selector Server selector.
/// @param subnet Subnet to be added or updated.
virtual void
createUpdateSubnet4(const db::ServerSelector& selector,
const Subnet4Ptr& subnet) = 0;
/// @brief Creates or updates a shared network.
///
/// @param selector Server selector.
/// @param shared_network Shared network to be added or updated.
virtual void
createUpdateSharedNetwork4(const db::ServerSelector& selector,
const SharedNetwork4Ptr& shared_network) = 0;
/// @brief Creates or updates an option definition.
///
/// @param selector Server selector.
/// @param option_def Option definition to be added or updated.
virtual void
createUpdateOptionDef4(const db::ServerSelector& selector,
const OptionDefinitionPtr& option_def) = 0;
/// @brief Creates or updates global option.
///
/// @param selector Server selector.
/// @param option Option to be added or updated.
virtual void
createUpdateOption4(const db::ServerSelector& selector,
const OptionPtr& option) = 0;
/// @brief Creates or updates subnet level option.
///
/// @param selector Server selector.
/// @param subnet_id Identifier of a subnet to which option belongs.
/// @param option Option to be added or updated.
virtual void
createUpdateOption4(const db::ServerSelector& selector,
const SubnetID& subnet_id,
const OptionPtr& option) = 0;
/// @brief Creates or updates pool level option.
///
/// @param selector Server selector.
/// @param pool_start_address Lower bound address of the pool to which
/// the option belongs.
/// @param pool_end_address Upper bound address of the pool to which the
/// option belongs.
/// @param option Option to be added or updated.
virtual void
createUpdateOption4(const db::ServerSelector& selector,
const asiolink::IOAddress& pool_start_address,
const asiolink::IOAddress& pool_end_address,
const OptionPtr& option) = 0;
/// @brief Creates or updates global string parameter.
///
/// @param selector Server selector.
/// @param name Name of the global parameter.
/// @param value Value of the global parameter.
virtual void
createUpdateGlobalParameter4(const db::ServerSelector& selector,
const std::string& name,
const std::string& value) = 0;
/// @brief Creates or updates global number parameter.
///
/// @param selector Server selector.
/// @param name Name of the global parameter.
/// @param value Value of the global parameter.
virtual void
createUpdateGlobalParameter4(const db::ServerSelector& selector,
const std::string& name,
const int64_t value) = 0;
/// @brief Deletes subnet by prefix.
///
/// @param selector Server selector.
/// @param subnet_prefix Prefix of the subnet to be deleted.
virtual void
deleteSubnet4(const db::ServerSelector& selector,
const std::string& subnet_prefix) = 0;
/// @brief Deletes subnet by identifier.
///
/// @param selector Server selector.
/// @param subnet_id Identifier of the subnet to be deleted.
virtual void
deleteSubnet4(const db::ServerSelector& selector, const SubnetID& subnet_id) = 0;
/// @brief Deletes all subnets.
///
/// @param selector Server selector.
virtual void
deleteAllSubnets4(const db::ServerSelector& selector) = 0;
/// @brief Deletes shared network by name.
///
/// @param selector Server selector.
/// @param name Name of the shared network to be deleted.
virtual void
deleteSharedNetwork4(const db::ServerSelector& selector,
const std::string& name) = 0;
/// @brief Deletes all shared networks.
///
/// @param selector Server selector.
virtual void
deleteAllSharedNetworks4(const db::ServerSelector& selector) = 0;
/// @brief Deletes option definition.
///
/// @param selector Server selector.
/// @param code Code of the option to be deleted.
/// @param space Option space of the option to be deleted.
virtual void
deleteOptionDef4(const db::ServerSelector& selector, const uint16_t code,
const std::string& space) = 0;
/// @brief Deletes all option definitions.
///
/// @param selector Server selector.
virtual void
deleteAllOptionDefs4(const db::ServerSelector& selector) = 0;
/// @brief Deletes global option.
///
/// @param selector Server selector.
/// @param code Code of the option to be deleted.
/// @param space Option space of the option to be deleted.
virtual void
deleteOption4(const db::ServerSelector& selector, const uint16_t code,
const std::string& space) = 0;
/// @brief Deletes subnet level option.
///
/// @param selector Server selector.
/// @param subnet_id Identifier of the subnet to which deleted option
/// belongs.
/// @param code Code of the deleted option.
/// @param space Option space of the deleted option.
virtual void
deleteOption4(const db::ServerSelector& selector, const SubnetID& subnet_id,
const uint16_t code, const std::string& space) = 0;
/// @brief Deletes pool level option.
///
/// @param selector Server selector.
/// @param pool_start_address Lower bound address of the pool to which
/// deleted option belongs.
/// @param pool_end_address Upper bound address of the pool to which the
/// deleted option belongs.
/// @param code Code of the deleted option.
/// @param space Option space of the deleted option.
virtual void
deleteOption4(const db::ServerSelector& selector,
const asiolink::IOAddress& pool_start_address,
const asiolink::IOAddress& pool_end_address,
const uint16_t code,
const std::string& space) = 0;
/// @brief Deletes global parameter.
///
/// @param selector Server selector.
/// @param name Name of the global parameter to be deleted.
virtual void
deleteGlobalParameter4(const db::ServerSelector& selector,
const std::string& name) = 0;
/// @brief Deletes all global parameters.
///
/// @param selector Server selector.
virtual void
deleteAllGlobalParameters4(const db::ServerSelector& selector) = 0;
};
} // end of namespace isc::dhcp
} // end of namespace isc
#endif // CONFIG_BACKEND_DHCP4_H

View File

@@ -0,0 +1,346 @@
// 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 <dhcpsrv/config_backend_pool_dhcp4.h>
using namespace isc::asiolink;
using namespace isc::db;
using namespace isc::util;
namespace isc {
namespace dhcp {
Subnet4Ptr
ConfigBackendPoolDHCPv4::getSubnet4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const std::string& subnet_prefix) const {
Subnet4Ptr subnet;
getPropertyPtrConst<Subnet4Ptr, const std::string&>
(&ConfigBackendDHCPv4::getSubnet4, backend_selector, server_selector,
subnet, subnet_prefix);
return (subnet);
}
Subnet4Ptr
ConfigBackendPoolDHCPv4::getSubnet4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const SubnetID& subnet_id) const {
Subnet4Ptr subnet;
getPropertyPtrConst<Subnet4Ptr, const SubnetID&>
(&ConfigBackendDHCPv4::getSubnet4, backend_selector, server_selector,
subnet, subnet_id);
return (subnet);
}
Subnet4Collection
ConfigBackendPoolDHCPv4::getAllSubnets4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector) const {
Subnet4Collection subnets;
getAllPropertiesConst<Subnet4Collection>
(&ConfigBackendDHCPv4::getAllSubnets4, backend_selector, server_selector,
subnets);
return (subnets);
}
Subnet4Collection
ConfigBackendPoolDHCPv4::getModifiedSubnets4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const boost::posix_time::ptime& modification_time) const {
Subnet4Collection subnets;
getMultiplePropertiesConst<Subnet4Collection, const boost::posix_time::ptime&>
(&ConfigBackendDHCPv4::getModifiedSubnets4, backend_selector, server_selector,
subnets, modification_time);
return (subnets);
}
SharedNetwork4Ptr
ConfigBackendPoolDHCPv4::getSharedNetwork4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const std::string& name) const {
SharedNetwork4Ptr shared_network;
getPropertyPtrConst<SharedNetwork4Ptr, const std::string&>
(&ConfigBackendDHCPv4::getSharedNetwork4, backend_selector, server_selector,
shared_network, name);
return (shared_network);
}
SharedNetwork4Collection
ConfigBackendPoolDHCPv4::getAllSharedNetworks4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector) const {
SharedNetwork4Collection shared_networks;
getAllPropertiesConst<SharedNetwork4Collection>
(&ConfigBackendDHCPv4::getAllSharedNetworks4, backend_selector, server_selector,
shared_networks);
return (shared_networks);
}
SharedNetwork4Collection
ConfigBackendPoolDHCPv4::
getModifiedSharedNetworks4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const boost::posix_time::ptime& modification_time) const {
SharedNetwork4Collection shared_networks;
getMultiplePropertiesConst<SharedNetwork4Collection, const boost::posix_time::ptime&>
(&ConfigBackendDHCPv4::getModifiedSharedNetworks4, backend_selector, server_selector,
shared_networks, modification_time);
return (shared_networks);
}
OptionDefinitionPtr
ConfigBackendPoolDHCPv4::getOptionDef4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const uint16_t code,
const std::string& space) const {
OptionDefinitionPtr option_def;
getPropertyPtrConst<OptionDefinitionPtr, uint16_t, const std::string&>
(&ConfigBackendDHCPv4::getOptionDef4, backend_selector, server_selector,
option_def, code, space);
return (option_def);
}
OptionDefContainer
ConfigBackendPoolDHCPv4::getAllOptionDefs4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector) const {
OptionDefContainer option_defs;
getAllPropertiesConst<OptionDefContainer>
(&ConfigBackendDHCPv4::getAllOptionDefs4, backend_selector, server_selector,
option_defs);
return (option_defs);
}
OptionDefContainer
ConfigBackendPoolDHCPv4::getModifiedOptionDefs4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const boost::posix_time::ptime& modification_time) const {
OptionDefContainer option_defs;
getMultiplePropertiesConst<OptionDefContainer, const boost::posix_time::ptime&>
(&ConfigBackendDHCPv4::getModifiedOptionDefs4, backend_selector, server_selector,
option_defs, modification_time);
return (option_defs);
}
util::OptionalValue<std::string>
ConfigBackendPoolDHCPv4::getGlobalStringParameter4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const std::string& name) const {
OptionalValue<std::string> parameter;
getOptionalPropertyConst<std::string, const std::string&>
(&ConfigBackendDHCPv4::getGlobalStringParameter4, backend_selector,
server_selector, parameter, name);
return (parameter);
}
util::OptionalValue<int64_t>
ConfigBackendPoolDHCPv4::getGlobalNumberParameter4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const std::string& name) const {
OptionalValue<int64_t> parameter;
getOptionalPropertyConst<int64_t, const std::string&>
(&ConfigBackendDHCPv4::getGlobalNumberParameter4, backend_selector,
server_selector, parameter, name);
return (parameter);
}
std::map<std::string, std::string>
ConfigBackendPoolDHCPv4::getAllGlobalParameters4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector) const {
std::map<std::string, std::string> parameters;
getAllPropertiesConst<std::map<std::string, std::string> >
(&ConfigBackendDHCPv4::getAllGlobalParameters4, backend_selector,
server_selector, parameters);
return (parameters);
}
void
ConfigBackendPoolDHCPv4::createUpdateSubnet4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const Subnet4Ptr& subnet) {
createUpdateDeleteProperty<const Subnet4Ptr&>
(&ConfigBackendDHCPv4::createUpdateSubnet4, backend_selector,
server_selector, subnet);
}
void
ConfigBackendPoolDHCPv4::createUpdateSharedNetwork4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const SharedNetwork4Ptr& shared_network) {
createUpdateDeleteProperty<const SharedNetwork4Ptr&>
(&ConfigBackendDHCPv4::createUpdateSharedNetwork4, backend_selector,
server_selector, shared_network);
}
void
ConfigBackendPoolDHCPv4::createUpdateOptionDef4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const OptionDefinitionPtr& option_def) {
createUpdateDeleteProperty<const OptionDefinitionPtr&>
(&ConfigBackendDHCPv4::createUpdateOptionDef4, backend_selector,
server_selector, option_def);
}
void
ConfigBackendPoolDHCPv4::createUpdateOption4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const OptionPtr& option) {
createUpdateDeleteProperty<const OptionPtr&>
(&ConfigBackendDHCPv4::createUpdateOption4, backend_selector,
server_selector, option);
}
void
ConfigBackendPoolDHCPv4::createUpdateOption4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const SubnetID& subnet_id,
const OptionPtr& option) {
createUpdateDeleteProperty<const SubnetID&, const OptionPtr&>
(&ConfigBackendDHCPv4::createUpdateOption4, backend_selector,
server_selector, subnet_id, option);
}
void
ConfigBackendPoolDHCPv4::createUpdateOption4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const IOAddress& pool_start_address,
const IOAddress& pool_end_address,
const OptionPtr& option) {
createUpdateDeleteProperty<const IOAddress&, const IOAddress&, const OptionPtr&>
(&ConfigBackendDHCPv4::createUpdateOption4, backend_selector,
server_selector, pool_start_address, pool_end_address, option);
}
void
ConfigBackendPoolDHCPv4::createUpdateGlobalParameter4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const std::string& name,
const std::string& value) {
createUpdateDeleteProperty<const std::string&, const std::string&>
(&ConfigBackendDHCPv4::createUpdateGlobalParameter4, backend_selector,
server_selector, name, value);
}
void
ConfigBackendPoolDHCPv4::createUpdateGlobalParameter4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const std::string& name,
const int64_t value) {
createUpdateDeleteProperty<const std::string&, int64_t>
(&ConfigBackendDHCPv4::createUpdateGlobalParameter4, backend_selector,
server_selector, name, value);
}
void
ConfigBackendPoolDHCPv4::deleteSubnet4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const std::string& subnet_prefix) {
createUpdateDeleteProperty<const std::string&>
(&ConfigBackendDHCPv4::deleteSubnet4, backend_selector, server_selector,
subnet_prefix);
}
void
ConfigBackendPoolDHCPv4::deleteSubnet4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const SubnetID& subnet_id) {
createUpdateDeleteProperty<const SubnetID&>
(&ConfigBackendDHCPv4::deleteSubnet4, backend_selector, server_selector,
subnet_id);
}
void
ConfigBackendPoolDHCPv4::deleteAllSubnets4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector) {
createUpdateDeleteProperty<>
(&ConfigBackendDHCPv4::deleteAllSubnets4, backend_selector, server_selector);
}
void
ConfigBackendPoolDHCPv4::deleteSharedNetwork4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const std::string& name) {
createUpdateDeleteProperty<const std::string&>
(&ConfigBackendDHCPv4::deleteSharedNetwork4, backend_selector,
server_selector, name);
}
void
ConfigBackendPoolDHCPv4::deleteAllSharedNetworks4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector) {
createUpdateDeleteProperty<>
(&ConfigBackendDHCPv4::deleteAllSharedNetworks4, backend_selector, server_selector);
}
void
ConfigBackendPoolDHCPv4::deleteOptionDef4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const uint16_t code,
const std::string& space) {
createUpdateDeleteProperty<uint16_t, const std::string&>
(&ConfigBackendDHCPv4::deleteOptionDef4, backend_selector,
server_selector, code, space);
}
void
ConfigBackendPoolDHCPv4::deleteAllOptionDefs4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector) {
createUpdateDeleteProperty<>
(&ConfigBackendDHCPv4::deleteAllOptionDefs4, backend_selector, server_selector);
}
void
ConfigBackendPoolDHCPv4::deleteOption4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const uint16_t code,
const std::string& space) {
createUpdateDeleteProperty<uint16_t, const std::string&>
(&ConfigBackendDHCPv4::deleteOption4, backend_selector, server_selector,
code, space);
}
void
ConfigBackendPoolDHCPv4::deleteOption4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const SubnetID& subnet_id,
const uint16_t code,
const std::string& space) {
createUpdateDeleteProperty<const SubnetID&, uint16_t, const std::string&>
(&ConfigBackendDHCPv4::deleteOption4, backend_selector, server_selector,
subnet_id, code, space);
}
void
ConfigBackendPoolDHCPv4::deleteOption4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const asiolink::IOAddress& pool_start_address,
const asiolink::IOAddress& pool_end_address,
const uint16_t code,
const std::string& space) {
createUpdateDeleteProperty<const IOAddress&, const IOAddress&, uint16_t,
const std::string&>
(&ConfigBackendDHCPv4::deleteOption4, backend_selector, server_selector,
pool_start_address, pool_end_address, code, space);
}
void
ConfigBackendPoolDHCPv4::deleteGlobalParameter4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const std::string& name) {
createUpdateDeleteProperty<const std::string&>
(&ConfigBackendDHCPv4::deleteGlobalParameter4, backend_selector,
server_selector, name);
}
void
ConfigBackendPoolDHCPv4::deleteAllGlobalParameters4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector) {
createUpdateDeleteProperty<>
(&ConfigBackendDHCPv4::deleteAllGlobalParameters4, backend_selector,
server_selector);
}
} // end of namespace isc::dhcp
} // end of namespace isc

View File

@@ -0,0 +1,394 @@
// 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 CONFIG_BACKEND_POOL_DHCP4_H
#define CONFIG_BACKEND_POOL_DHCP4_H
#include <config_backend/base_config_backend_pool.h>
#include <database/backend_selector.h>
#include <database/server_selector.h>
#include <dhcp/option.h>
#include <dhcp/option_definition.h>
#include <dhcpsrv/config_backend_dhcp4.h>
#include <dhcpsrv/shared_network.h>
#include <dhcpsrv/subnet.h>
#include <util/optional_value.h>
#include <boost/date_time/posix_time/ptime.hpp>
#include <string>
namespace isc {
namespace dhcp {
/// @brief Implementation of the Configuration Backend Pool for DHCPv4.
class ConfigBackendPoolDHCPv4 : cb::BaseConfigBackendPool<ConfigBackendDHCPv4> {
public:
/// @brief Retrieves a single subnet by subnet_prefix.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param subnet_prefix Prefix of the subnet to be retrieved.
/// @return Pointer to the retrieved subnet or NULL if not found.
virtual Subnet4Ptr
getSubnet4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const std::string& subnet_prefix) const;
/// @brief Retrieves a single subnet by subnet identifier.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param subnet_id Identifier of a subnet to be retrieved.
/// @return Pointer to the retrieved subnet or NULL if not found.
virtual Subnet4Ptr
getSubnet4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const SubnetID& subnet_id) const;
/// @brief Retrieves all subnets.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @return Collection of subnets or empty collection if no subnet found.
virtual Subnet4Collection
getAllSubnets4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector) const;
/// @brief Retrieves subnets modified after specified time.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param modification_time Lower bound subnet modification time.
/// @return Collection of subnets or empty collection if no subnet found.
virtual Subnet4Collection
getModifiedSubnets4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const boost::posix_time::ptime& modification_time) const;
/// @brief Retrieves shared network by name.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param name Name of the shared network to be retrieved.
/// @return Pointer to the shared network or NULL if not found.
virtual SharedNetwork4Ptr
getSharedNetwork4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const std::string& name) const;
/// @brief Retrieves all shared networks.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @return Collection of shared network or empty collection if
/// no shared network found.
virtual SharedNetwork4Collection
getAllSharedNetworks4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector) const;
/// @brief Retrieves shared networks modified after specified time.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param modification_time Lower bound shared network modification time.
/// @return Collection of shared network or empty collection if
/// no shared network found.
virtual SharedNetwork4Collection
getModifiedSharedNetworks4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const boost::posix_time::ptime& modification_time) const;
/// @brief Retrieves single option definition by code and space.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param code Code of the option to be retrieved.
/// @param space Option space of the option to be retrieved.
/// @return Pointer to the option definition or NULL if not found.
virtual OptionDefinitionPtr
getOptionDef4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const uint16_t code,
const std::string& space) const;
/// @brief Retrieves all option definitions.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @return Collection of option definitions or empty collection if
/// no option definition found.
virtual OptionDefContainer
getAllOptionDefs4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector) const;
/// @brief Retrieves option definitions modified after specified time.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param modification_time Lower bound option definition modification
/// time.
/// @return Collection of option definitions or empty collection if
/// no option definition found.
virtual OptionDefContainer
getModifiedOptionDefs4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const boost::posix_time::ptime& modification_time) const;
/// @brief Retrieves global string parameter value.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param name Name of the global parameter to be retrieved.
/// @return Value of the global string parameter.
virtual util::OptionalValue<std::string>
getGlobalStringParameter4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const std::string& name) const;
/// @brief Retrieves global number parameter.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param name Name of the parameter to be retrieved.
virtual util::OptionalValue<int64_t>
getGlobalNumberParameter4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const std::string& name) const;
/// @brief Retrieves all global parameters as strings.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
virtual std::map<std::string, std::string>
getAllGlobalParameters4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector) const;
/// @brief Creates or updates a subnet.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param subnet Subnet to be added or updated.
virtual void
createUpdateSubnet4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const Subnet4Ptr& subnet);
/// @brief Creates or updates a shared network.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param shared_network Shared network to be added or updated.
virtual void
createUpdateSharedNetwork4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const SharedNetwork4Ptr& shared_network);
/// @brief Creates or updates an option definition.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param option_def Option definition to be added or updated.
virtual void
createUpdateOptionDef4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const OptionDefinitionPtr& option_def);
/// @brief Creates or updates global option.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param option Option to be added or updated.
virtual void
createUpdateOption4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const OptionPtr& option);
/// @brief Creates or updates subnet level option.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param subnet_id Identifier of a subnet to which option belongs.
/// @param option Option to be added or updated.
virtual void
createUpdateOption4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const SubnetID& subnet_id,
const OptionPtr& option);
/// @brief Creates or updates pool level option.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param pool_start_address Lower bound address of the pool to which
/// the option belongs.
/// @param pool_end_address Upper bound address of the pool to which the
/// option belongs.
/// @param option Option to be added or updated.
virtual void
createUpdateOption4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const asiolink::IOAddress& pool_start_address,
const asiolink::IOAddress& pool_end_address,
const OptionPtr& option);
/// @brief Creates or updates global string parameter.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param name Name of the global parameter.
/// @param value Value of the global parameter.
virtual void
createUpdateGlobalParameter4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const std::string& name,
const std::string& value);
/// @brief Creates or updates global number parameter.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param name Name of the global parameter.
/// @param value Value of the global parameter.
virtual void
createUpdateGlobalParameter4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const std::string& name,
const int64_t value);
/// @brief Deletes subnet by prefix.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param subnet_prefix Prefix of the subnet to be deleted.
virtual void
deleteSubnet4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const std::string& subnet_prefix);
/// @brief Deletes subnet by identifier.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param subnet_id Identifier of the subnet to be deleted.
virtual void
deleteSubnet4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const SubnetID& subnet_id);
/// @brief Deletes all subnets.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
virtual void
deleteAllSubnets4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector);
/// @brief Deletes shared network by name.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param name Name of the shared network to be deleted.
virtual void
deleteSharedNetwork4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const std::string& name);
/// @brief Deletes all shared networks.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
virtual void
deleteAllSharedNetworks4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector);
/// @brief Deletes option definition.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param code Code of the option to be deleted.
/// @param space Option space of the option to be deleted.
virtual void
deleteOptionDef4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const uint16_t code,
const std::string& space);
/// @brief Deletes all option definitions.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
virtual void
deleteAllOptionDefs4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector);
/// @brief Deletes global option.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param code Code of the option to be deleted.
/// @param space Option space of the option to be deleted.
virtual void
deleteOption4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const uint16_t code,
const std::string& space);
/// @brief Deletes subnet level option.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param subnet_id Identifier of the subnet to which deleted option
/// belongs.
/// @param code Code of the deleted option.
/// @param space Option space of the deleted option.
virtual void
deleteOption4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const SubnetID& subnet_id,
const uint16_t code, const std::string& space);
/// @brief Deletes pool level option.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param pool_start_address Lower bound address of the pool to which
/// deleted option belongs.
/// @param pool_end_address Upper bound address of the pool to which the
/// deleted option belongs.
/// @param code Code of the deleted option.
/// @param space Option space of the deleted option.
virtual void
deleteOption4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const asiolink::IOAddress& pool_start_address,
const asiolink::IOAddress& pool_end_address,
const uint16_t code,
const std::string& space);
/// @brief Deletes global parameter.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
/// @param name Name of the global parameter to be deleted.
virtual void
deleteGlobalParameter4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const std::string& name);
/// @brief Deletes all global parameters.
///
/// @param backend_selector Backend selector.
/// @param server_selector Server selector.
virtual void
deleteAllGlobalParameters4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector);
};
} // end of namespace isc::dhcp
} // end of namespace isc
#endif // CONFIG_BACKEND_POOL_DHCP4_H

31
src/lib/yang/Makefile.am Normal file
View File

@@ -0,0 +1,31 @@
SUBDIRS = . tests
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES) $(SYSREPO_CPPFLAGS)
AM_CXXFLAGS = $(KEA_CXXFLAGS)
lib_LTLIBRARIES = libkea-yang.la
libkea_yang_la_SOURCES = adaptor.cc adaptor.h
libkea_yang_la_SOURCES += sysrepo_error.h
libkea_yang_la_SOURCES += translator.cc translator.h
libkea_yang_la_LIBADD = $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
libkea_yang_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la
libkea_yang_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la
libkea_yang_la_LIBADD += $(top_builddir)/src/lib/util/threads/libkea-threads.la
libkea_yang_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la
libkea_yang_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
libkea_yang_la_LIBADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS) $(SYSREPO_LIBS)
libkea_yang_la_LDFLAGS = -no-undefined -version-info 0:0:0
# Specify the headers for copying into the installation directory tree.
libkea_yang_includedir = $(pkgincludedir)/yang
libkea_yang_include_HEADERS = \
adaptor.h \
sysrepo_error.h \
translator.h
EXTRA_DIST = yang.dox
CLEANFILES = *.gcno *.gcda

306
src/lib/yang/adaptor.cc Normal file
View File

@@ -0,0 +1,306 @@
// 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 <boost/foreach.hpp>
#include <iostream>
using namespace std;
using namespace isc::data;
namespace isc {
namespace yang {
Adaptor::Adaptor() {
}
Adaptor::~Adaptor() {
}
ConstElementPtr
Adaptor::getContext(ConstElementPtr parent)
{
ConstElementPtr context = parent->get("user-context");
ConstElementPtr comment = parent->get("comment");
if (!comment) {
return (context);
}
ElementPtr result;
if (context) {
result = copy(context);
} else {
result = Element::createMap();
}
result->set("comment", comment);
return (result);
}
void
Adaptor::fromParent(const string& name, ConstElementPtr parent,
ConstElementPtr list) {
ConstElementPtr param = parent->get(name);
if (!param) {
return;
}
BOOST_FOREACH(ElementPtr item, list->listValue()) {
// don't override. Skip this entry if it already has the parameter.
if (item->contains(name)) {
continue;
}
item->set(name, param);
}
}
void
Adaptor::toParent(const string& name, ElementPtr parent,
ConstElementPtr list) {
ConstElementPtr param;
bool first = true;
BOOST_FOREACH(ElementPtr item, list->listValue()) {
if (first) {
first = false;
param = item->get(name);
} else if ((!param && item->contains(name)) ||
(param && !item->contains(name)) ||
(param && item->contains(name) &&
!param->equals(*item->get(name)))) {
isc_throw(BadValue,
"inconsistent value of " << name
<< " in " << list->str());
}
}
if (!first && param) {
BOOST_FOREACH(ElementPtr item, list->listValue()) {
if (param) {
item->remove(name);
}
}
parent->set(name, param);
}
}
namespace {
/// @brief Apply insert.
///
/// This function applies an insert action at the given scope:
/// - in a map it adds the value at the key when the key does not exist
/// - in a list it appends the value
/// - in other scope type it does nothing
/// @note a copy of the value is used to avoid unwanted sharing/side effects.
///
/// @param key The key of the modification.
/// @param value The value of the modification.
/// @param scope The place to apply the insert.
void applyInsert(ConstElementPtr key, ConstElementPtr value,
ElementPtr scope) {
if (scope->getType() == Element::map) {
if (!key || !value || (key->getType() != Element::string)) {
return;
}
string name = key->stringValue();
if (!name.empty() && !scope->contains(name)) {
scope->set(name, copy(value));
}
} else if (scope->getType() == Element::list) {
if (value) {
scope->add(copy(value));
}
}
}
/// @brief Apply replace.
///
/// This function works only on map scopes and acts as insert at the
/// exception the new value is set even if the key already exists.
///
/// @param key The key of the modification.
/// @param value The value of the modification.
/// @param scope The place to apply the replace.
void applyReplace(ConstElementPtr key, ConstElementPtr value,
ElementPtr scope) {
if ((scope->getType() != Element::map) ||
!key || !value || (key->getType() != Element::string)) {
return;
}
string name = key->stringValue();
if (!name.empty()) {
scope->set(name, copy(value));
}
}
/// @brief Apply delete.
///
/// This function deletes the value designed by the key from the given scope:
/// - in a map the key is the key of the value to remove
/// - in a list the key is:
/// * when it is an integer the index of the value to remove
/// * when it is a map the key / value pair which belongs to the value
/// to remove
/// - in other scope type it does nothing
///
/// @param key The key item of the path.
/// @param scope The place to apply the delete.
void applyDelete(ConstElementPtr key, ElementPtr scope) {
if (scope->getType() == Element::map) {
if (!key || (key->getType() != Element::string)) {
return;
}
string name = key->stringValue();
if (!name.empty()) {
scope->remove(name);
}
} else if (scope->getType() == Element::list) {
if (!key) {
return;
} else if (key->getType() == Element::integer) {
int index = key->intValue();
if ((index >= 0) && (index < scope->size())) {
scope->remove(index);
}
} else if (key->getType() == Element::map) {
ConstElementPtr entry = key->get("key");
ConstElementPtr value = key->get("value");
if (!entry || !value || (entry->getType() != Element::string)) {
return;
}
string name = entry->stringValue();
if (name.empty()) {
return;
}
for (int i = 0; i < scope->size(); ++i) {
ConstElementPtr item = scope->get(i);
if (!item || (item->getType() != Element::map)) {
continue;
}
ConstElementPtr compare = item->get(name);
if (compare && value->equals(*compare)) {
scope->remove(i);
return;
}
}
}
}
}
/// @brief Apply action.
///
/// This function applies one action (insert, replace or delete) using
/// applyInsert, applyReplace or applyDelete.
///
/// @param actions The action list.
/// @param scope The current scope.
/// @param next The index of the next action.
void applyAction(ConstElementPtr actions, ElementPtr scope, size_t next) {
if (next == actions->size()) {
return;
}
ConstElementPtr action = actions->get(next);
++next;
if (!action || (action->getType() != Element::map) ||
!action->contains("action")) {
applyAction(actions, scope, next);
return;
}
string name = action->get("action")->stringValue();
if (name == "insert") {
applyInsert(action->get("key"), action->get("value"), scope);
} else if (name == "replace") {
applyReplace(action->get("key"), action->get("value"), scope);
} else if (name == "delete") {
applyDelete(action->get("key"), scope);
}
applyAction(actions, scope, next);
}
/// @brief Apply recursively actions following the path and going down
/// in the to-be-modified structure.
///
/// The recursive call when the end of the path is not reached is done:
/// - in a map on the value at the key
/// - in a list the next path item is:
/// * if it is a positive integer on the value at the index
/// * if it is -1 on all elements of the list
/// * if a map on the element where the key / value pair belongs to
///
/// @param path The search list.
/// @param actions The action list.
/// @param scope The current scope.
/// @param next The index of the next item to use in the path.
void applyDown(ConstElementPtr path, ConstElementPtr actions, ElementPtr scope,
size_t next) {
if (!scope) {
return;
}
if (next == path->size()) {
applyAction(actions, scope, 0);
return;
}
ConstElementPtr step = path->get(next);
++next;
if (scope->getType() == Element::map) {
if (!step || (step->getType() != Element::string)) {
return;
}
string name = step->stringValue();
if (name.empty() || !scope->contains(name)) {
return;
}
ElementPtr down = boost::const_pointer_cast<Element>(scope->get(name));
if (down) {
applyDown(path, actions, down, next);
}
} else if (scope->getType() == Element::list) {
if (!step) {
return;
}
auto downs = scope->listValue();
if (step->getType() == Element::map) {
ConstElementPtr key = step->get("key");
ConstElementPtr value = step->get("value");
if (!key || !value || (key->getType() != Element::string)) {
return;
}
string name = key->stringValue();
if (name.empty()) {
return;
}
for (ElementPtr down : downs) {
if (!down || (down->getType() != Element::map)) {
continue;
}
ConstElementPtr compare = down->get(name);
if (compare && value->equals(*compare)) {
applyDown(path, actions, down, next);
return;
}
}
} else if (step->getType() != Element::integer) {
return;
}
int index = step->intValue();
if (index == -1) {
for (ElementPtr down : downs) {
applyDown(path, actions, down, next);
}
} else if ((index >= 0) && (index < scope->size())) {
applyDown(path, actions, scope->getNonConst(index), next);
}
}
}
} // end of anonymous namespace
/// Apply recursively starting at the beginning of the path.
void
Adaptor::modify(ConstElementPtr path, ConstElementPtr actions,
ElementPtr config) {
applyDown(path, actions, config, 0);
}
}; // end of namespace isc::yang
}; // end of namespace isc

133
src/lib/yang/adaptor.h Normal file
View File

@@ -0,0 +1,133 @@
// 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_H
#define ISC_ADAPTOR_H 1
#include <exceptions/exceptions.h>
#include <cc/data.h>
namespace isc {
namespace yang {
/// @brief Missing key error.
class MissingKey : public isc::Exception {
public:
MissingKey(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what)
{}
};
/// @brief JSON adaptor between canonical Kea and Yang models.
///
/// An adaptor slightly modifies a JSON configuration between canonical Kea
/// what required or rendered by a Yang model, e.g. moving a parameter
/// to/from a parent.
/// The basic adaptor provides a set of tools.
class Adaptor {
public:
/// @brief Constructor.
Adaptor();
/// @brief Destructor.
virtual ~Adaptor();
/// @brief Get user context.
///
/// Get user-context and/or comment and return it with the comment
/// if exists moved inside the user-context (without checking if
/// there is already a comment as it should never be the case).
///
/// This behavior is used to handle comments. For historical purposes
/// Kea allows to define comments in some scopes. Once the user-context
/// has been introduced, the comment (as a separate text field)
/// disappeared and was moved to comment key within user-context.
/// Nevertheless, the old syntax is still supported.
static isc::data::ConstElementPtr
getContext(isc::data::ConstElementPtr parent);
/// @brief Moves a parameter from parent to a list of children.
///
/// Move a parameter from the parent to each item in a list.
/// If the parameter exists in a child, it is skipped for this
/// particular child, not overridden.
///
/// @param name The parameter name.
/// @param parent The parent element.
/// @param list The children list.
static void fromParent(const std::string& name,
isc::data::ConstElementPtr parent,
isc::data::ConstElementPtr list);
/// @brief Moves a parameter to a parent.
///
/// Move a parameter from children to the parent. All children
/// on the list must have the parameter specified and it has to have
/// the same value.
///
/// @param name The parameter name.
/// @param parent The parent element.
/// @param list The children list.
static void toParent(const std::string& name,
isc::data::ElementPtr parent,
isc::data::ConstElementPtr list);
/// @brief Modify a configuration in its JSON element format.
///
/// Smart merging tool, e.g. completing a from yang configuration.
///
/// A modification is a path and actions:
/// - path item can be:
/// * a string: current scope is a map, go down following the string
/// as a key.
/// * a number: current scope is a list, go down the number as an index.
/// * special value -1: current scope is a list, apply to all items.
/// * map { "<key>": <value> }: current scope is a list, go down to
/// the item using the key / value pair.
/// - an action can be: insert, replace or delete:
/// * insert adds a value at a key:
/// . in a map the key is the new entry key
/// . in a list an integer key is the new element index
/// . in a list a map key is the key / value pair the to-be-modified
/// element contains
/// * replace adds or replaces if it already exists a value at
/// an entry key in a map.
/// * delete removes a value designed by a key
///
/// For instance to add a control-socket entry in a configuration
/// from a generic (vs Kea) model:
/// @code
/// path := [ ]
/// actions := [ {
/// "action": "insert",
/// "key": "Dhcp4",
/// "value":
/// {
/// "control-socket":
/// {
/// "socket-type": "unix",
/// "socket-name": "/tmp/kea4-ctrl-socket"
/// }
/// }
/// }
/// @endcode
///
/// @param path The search list to follow down to the place to
/// apply the action list.
/// @param actions The action list
/// @param config The configuration (JSON map) to modify.
/// @note modify does not throw but returns on unexpected conditions.
static void modify(isc::data::ConstElementPtr path,
isc::data::ConstElementPtr actions,
isc::data::ElementPtr config);
};
}; // end of namespace isc::yang
}; // end of namespace isc
#endif // ISC_ADAPTOR_H

View File

@@ -1,243 +0,0 @@
module ietf-dhcpv4-options {
yang-version 1.1;
namespace "urn:ietf:params:xml:ns:yang:ietf-dhcpv4-options";
prefix "dhcpv4-options";
import ietf-inet-types {
prefix inet;
}
import ietf-yang-types {
prefix yang;
}
import ietf-dhcpv4-types {
prefix dhcpv4-types;
}
organization "DHC WG";
contact
"piotr.strzyzewski@polsl.pl";
description "This model defines a YANG data model that can be
used to configure DHCPv4 options.";
revision 2018-07-14 {
description "Initial revision";
reference "";
}
/*
* Features
*/
// features for server options
feature router-op {
description "Support for Router Option";
}
feature time-server-op {
description "Support for Time Server Option";
}
feature domain-server-op {
description "Support for Domain Server Option";
}
feature log-server-op {
description "Support for Log Server Option";
}
feature hostname-op {
description "Support for Hostname Option";
}
feature domain-name-op {
description "Support for Domain Name Option";
}
feature broadcast-op {
description "Support for Broadcast Address Option";
}
feature ntp-server-op {
description "Support for NTP Servers Option";
}
feature server-name-op {
description "Support for Server-Name Option";
}
feature bootfile-name-op {
description "Support for Bootfile-Name Option";
}
/*
* Groupings
*/
grouping server-option-definitions {
description "Contains definitions for options configured on the
DHCPv4 server which will be supplied to clients.";
container router-option {
// if-feature router-op
// presence "Enable this option";
description "Router (3) Router addresses";
reference "RFC2132: DHCP Options and BOOTP Vendor Extensions";
list router {
key router-id;
description "Router info";
leaf router-id {
type uint8;
mandatory true;
description "Router list entry ID";
}
leaf router-addr {
type inet:ipv4-address;
mandatory true;
description "Router address";
}
}
}
container time-server-option {
// if-feature time-server-op
// presence "Enable this option";
description "Time Server (4) Timeserver addresses";
reference "RFC2132: DHCP Options and BOOTP Vendor Extensions";
list time-server {
key time-server-id;
description "Time Server info";
leaf time-server-id {
type uint8;
mandatory true;
description "Time Server list entry ID";
}
leaf time-server-addr {
type inet:ipv4-address;
mandatory true;
description "Time Server address";
}
}
}
container domain-server-option {
// if-feature domain-server-op
// presence "Enable this option";
description "Domain Server (6) DNS Server addresses";
reference "RFC2132: DHCP Options and BOOTP Vendor Extensions";
list domain-server {
key domain-server-id;
description "DNS Server info";
leaf domain-server-id {
type uint8;
mandatory true;
description "DNS Server list entry ID";
}
leaf domain-server-addr {
type inet:ipv4-address;
mandatory true;
description "DNS Server address";
}
}
}
container log-server-option {
// if-feature log-server-op
// presence "Enable this option";
description "Log Server (7) Logging Server addresses";
reference "RFC2132: DHCP Options and BOOTP Vendor Extensions";
list log-server {
key log-server-id;
description "Logging Server info";
leaf log-server-id {
type uint8;
mandatory true;
description "Logging Server list entry ID";
}
leaf log-server-addr {
type inet:ipv4-address;
mandatory true;
description "Logging Server address";
}
}
}
container hostname-option {
// if-feature hostname-op;
// presence "Enable this option";
description "Hostname (12) Hostname string";
reference "RFC2132: DHCP Options and BOOTP Vendor Extensions";
leaf hostname {
type string;
description "Hostname";
}
}
container domain-name-option {
// if-feature domain-name-op;
// presence "Enable this option";
description "Domain Name (15) The DNS domain name of the client";
reference "RFC2132: DHCP Options and BOOTP Vendor Extensions";
leaf domain-name {
type string;
description "Domain Name";
}
}
container broadcast-option {
// if-feature broadcast-op
// presence "Enable this option";
description "Broadcast Address (28) Broadcast Address";
reference "RFC2132: DHCP Options and BOOTP Vendor Extensions";
leaf broadcast-addr {
type inet:ipv4-address;
description "Broadcast address";
}
}
container ntp-server-option {
// if-feature ntp-server-op
// presence "Enable this option";
description "NTP Servers (42) NTP Server addresses";
reference "RFC2132: DHCP Options and BOOTP Vendor Extensions";
list ntp-server {
key ntp-server-id;
description "NTP Server info";
leaf ntp-server-id {
type uint8;
mandatory true;
description "NTP Server list entry ID";
}
leaf ntp-server-addr {
type inet:ipv4-address;
mandatory true;
description "NTP Server address";
}
}
}
container server-name-option {
// if-feature server-name-op;
// presence "Enable this option";
description "Server-Name (66) TFTP Server Name";
reference "RFC2132: DHCP Options and BOOTP Vendor Extensions";
leaf server-name {
type string;
description "TFTP Server Name";
}
}
container bootfile-name-option {
// if-feature bootfile-name-op;
// presence "Enable this option";
description "Bootfile-Name (67) Boot File Name";
reference "RFC2132: DHCP Options and BOOTP Vendor Extensions";
leaf bootfile-name {
type string;
description "Boot File Name";
}
}
container domain-search-option {
// if-feature domain-search-op;
// presence "Enable this option";
description "Domain Search (119) DNS domain search list";
reference "RFC3397: Dynamic Host Configuration Protocol
(DHCP) Domain Search Option";
list domain-search {
key domain-search-id;
description "Domain Search info";
leaf domain-search-id {
type uint8;
mandatory true;
description "Domain Search entry ID";
}
leaf domain-search-entry {
type string;
mandatory true;
description "Domain Search list entry";
}
}
}
}
}

View File

@@ -1,64 +0,0 @@
module ietf-dhcpv4-types {
yang-version 1.1;
namespace "urn:ietf:params:xml:ns:yang:ietf-dhcpv4-types";
prefix "dhcpv4-types";
import ietf-inet-types {
prefix inet;
}
import ietf-yang-types {
prefix yang;
}
organization "DHC WG";
contact
"piotr.strzyzewski@polsl.pl";
description "This model defines a YANG data model that can be
used to define some commonly used DHCPv4 types";
revision 2018-07-14 {
description "Initial revision";
reference "";
}
/*
* Grouping
*/
grouping vendor-infor {
description "Vendor information.";
container vendor-info {
description "";
leaf ent-num {
type uint32;
description "enterprise number";
}
leaf-list data {
type string;
description "specific vendor info";
}
}
}
grouping portset-para {
description "portset parameters";
container port-parameter {
description "port parameter";
leaf offset {
type uint8;
mandatory true;
description "offset in a port set";
}
leaf psid-len {
type uint8;
mandatory true;
description "length of a psid";
}
leaf psid {
type uint16;
mandatory true;
description "psid value";
}
}
}
}

View File

@@ -0,0 +1,87 @@
module kea-ctrl-agent {
yang-version 1.1;
namespace "urn:ietf:params:xml:ns:yang:kea-ctrl-agent";
prefix "kea-ctrl-agent";
import ietf-inet-types {
prefix inet;
}
import kea-types {
prefix kea;
}
import kea-logging {
prefix logging;
}
organization "Internet Systems Consortium";
contact "kea-dev@lists.isc.org";
description "This model defines a YANG data model that can be
used to configure and manage a Kea control agent.";
revision 2018-08-20 {
description "Initial revision";
reference "";
}
/*
* Data Nodes
*/
container config {
// config true;
description "Contains control agent configuration.";
leaf http-host {
type inet:ip-address;
default "127.0.0.1";
description "IP address to which HTTP service will be bound.";
}
leaf http-port {
type uint16;
default 8000;
description "Port to which HTTP service will be bound.";
}
container control-sockets {
description "Control sockets.";
list socket {
key server-type;
description "List of server control socket.";
leaf server-type {
type enumeration {
enum "dhcp4" {
description "kea-dhcp4 server";
}
enum "dhcp6" {
description "kea-dhcp6 server";
}
enum "d2" {
description "kea-dhcp-ddns server";
}
}
mandatory true;
description "Server type.";
}
container control-socket {
description "Control socket information.";
uses kea:control-socket;
}
}
}
uses kea:hooks-libraries;
leaf user-context {
type kea:user-context;
description "Control agent user context.";
}
}
container logging {
// config true;
description "Logging";
uses logging:configuration;
}
}

View File

@@ -0,0 +1,185 @@
module kea-dhcp-ddns {
yang-version 1.1;
namespace "urn:ietf:params:xml:ns:yang:kea-dhcp-ddns";
prefix "kea-dhcp-ddns";
import ietf-inet-types {
prefix inet;
}
import kea-types {
prefix kea;
}
import kea-logging {
prefix logging;
}
organization "Internet Systems Consortium";
contact "kea-dev@lists.isc.org";
description "This model defines a YANG data model that can be
used to configure and manage a Kea DHCP-DDNS server.";
revision 2018-08-20 {
description "Initial revision";
reference "";
}
/*
* Groupings
*/
grouping managed-domains {
description "Contains parameters for forward or reverse DDNS managed
domains.";
container ddns-domains {
description "DDNS domains.";
list ddns-domain {
key name;
description "List of DDNS domains.";
leaf name {
type string;
mandatory true;
description "DDNS domain name.";
}
leaf key-name {
type string;
description "TSIG key to use. Blank means no TSIG.";
}
container dns-servers {
description "DNS servers.";
list server {
key ip-address;
description "List of DNS servers.";
leaf hostname {
type string;
description "DNS server hostname.";
}
leaf ip-address {
type inet:ip-address;
mandatory true;
description "DNS server IP address.";
}
leaf port {
type uint16;
default 53;
description "DNS server port.";
}
leaf user-context {
type kea:user-context;
description "DNS server user context.";
}
}
}
leaf user-context {
type kea:user-context;
description "DDNS domain user context.";
}
}
}
}
/*
* Data Nodes
*/
container config {
// config true;
description "Contains DHCP-DDNS server configuration.";
leaf ip-address {
type inet:ip-address;
default "127.0.0.1";
description "IP address on which the server listens for requests.";
}
leaf port {
type uint16;
default 53001;
description "Port on which the server listens for requests.";
}
leaf dns-server-timeout {
type uint32;
units "milliseconds";
description "Maximum amount of time that the server will wait for
a response from a DNS server to a single DNS update message.";
}
leaf ncr-protocol {
type enumeration {
enum "UDP" {
description "UDP transport";
}
enum "TCP" {
description "TCP transport";
}
}
default "UDP";
description "Protocol to use when sending requests to the server.";
}
leaf ncr-format {
type enumeration {
enum "JSON" {
description "JSON format";
}
}
default "JSON";
description "Packet format to use when sending requests to the server.";
}
container forward-ddns {
description "Forward DNS zones.";
uses managed-domains;
}
container reverse-ddns {
description "Reverse DNS zones.";
uses managed-domains;
}
container tsig-keys {
description "Keys to use with TSIG.";
list key {
key name;
description "List of TSIG keys.";
leaf name {
type string;
mandatory true;
description "Key name.";
}
leaf algorithm {
type string;
mandatory true;
description "Hashing algorithm to use with the key.";
}
leaf digest-bits {
type uint16;
units "bits";
default 0;
description "Minimum truncated length. 0 means no truncation.";
}
leaf secret {
type string;
mandatory true;
description "Shared secret for the key.";
}
leaf user-context {
type kea:user-context;
description "Key user context.";
}
}
}
leaf user-context {
type kea:user-context;
description "DHCP-DDNS server user context.";
}
}
container logging {
// config true;
description "Logging";
uses logging:configuration;
}
}

View File

@@ -0,0 +1,826 @@
module kea-dhcp-types {
yang-version 1.1;
namespace "urn:ietf:params:xml:ns:yang:kea-dhcp-types";
prefix "kea-dhcp-types";
import ietf-inet-types {
prefix inet;
}
import kea-types {
prefix kea;
}
organization "Internet Systems Consortium";
contact "kea-dev@lists.isc.org";
description "This file defines some commonly used Kea DHCP types and
groupings.";
revision 2018-08-20 {
description "Initial revision";
reference "";
}
/*
* Typedef
*/
typedef host-reservation-mode {
type enumeration {
enum "disabled" {
description "Host reservation support is disabled.";
}
enum "out-of-pool" {
description "Allows only out of pool host reservations.";
}
enum "all" {
description "Allows both in pool and out of pool host reservations.";
}
enum "global" {
description "Allows only global host reservations.";
}
}
default "all";
description "Host reservation mode.";
}
typedef lease-state {
type enumeration {
enum "default" {
description "Active/default";
}
enum "declined" {
description "Declined";
}
enum "expired-reclaimed" {
description "Expired-reclaimed";
}
}
default "default";
description "Defines state of the lease.";
}
/*
* Grouping
*/
grouping valid-lifetime {
description "Valid lifetime grouping.";
leaf valid-lifetime {
type uint32;
units "seconds";
description "Valid lifetime entry.";
}
}
grouping renew-timer {
description "Renew timer grouping.";
leaf renew-timer {
type uint32;
units "seconds";
description "Renew timer entry.";
}
}
grouping rebind-timer {
description "Rebind timer grouping.";
leaf rebind-timer {
type uint32;
units "seconds";
description "Rebind timer entry.";
}
}
grouping database {
description "Database grouping.";
leaf database-type {
type string;
mandatory true;
description "Database type (e.g. mysql).";
}
leaf user {
type string;
description "Database user name.";
}
leaf password {
type string;
description "Database user password.";
}
leaf host {
type string;
description "Database host.";
}
leaf name {
type string;
description "Database name.";
}
leaf persist {
type boolean;
default true;
description "Write lease to disk file. This parameter applies only to
memfile backend.";
}
leaf port {
type uint16;
description "Database port.";
}
leaf lfc-interval {
type uint32;
units "seconds";
default 3600;
description "Interval between two lease file cleanups.";
}
leaf readonly {
type boolean;
default false;
description "If set to true, the database will be connected in
read-only mode. This does not make sense for lease
databases, only for host reservations and possibly
for upcoming config backend.";
}
leaf connect-timeout {
type uint32;
units "milliseconds";
description "Database connection timeout.";
}
leaf contact-points {
type string;
description "Cassandra database contact points, a coma separated list of
IP addresses.";
}
leaf keyspace {
type string;
description "Cassandra database keyspace (this is Cassandra's equivalent
of a database name).";
}
leaf max-reconnect-tries {
type uint32;
default 0;
description "Maximum of recovery attempts before exit.";
}
leaf reconnect-wait-time {
type uint32;
units "milliseconds";
default 0;
description "Waiting delay between two recovery attempts.";
}
leaf request-timeout {
type uint32;
units "milliseconds";
description "Timeout waiting for a response.";
}
leaf tcp-keepalive {
type uint32;
units "seconds";
default 0;
description "TCP keepalive for the database connection.";
}
leaf tcp-nodelay {
type boolean;
default true;
description "TCP nodelay for the database connection.";
}
uses kea:user-context {
refine user-context {
description "Database user context. Arbitrary JSON data can be
stored here.";
}
}
}
grouping databases {
description "Databases grouping.";
container lease-database {
presence "Have lease database.";
description "Lease database.";
uses database;
}
container hosts-databases {
description "Hosts databases.";
list hosts-database {
key database-type;
description "List of databases.";
uses database;
}
}
}
grouping expired-leases-processing {
description "Expired leases processing grouping.";
container expired-leases-processing {
description "Expired leases processing setup.";
leaf reclaim-timer-wait-time {
type uint32;
units "seconds";
default 10;
description "Interval between reclamation cycles.";
}
leaf flush-reclaimed-timer-wait-time {
type uint32;
units "seconds";
default 25;
description "Interval between reclaimed leases collection.";
}
leaf hold-reclaimed-time {
type uint32;
units "seconds";
default 3600;
description "Hold timer for re-assignment.";
}
leaf max-reclaim-leases {
type uint32;
default 100;
description "Maximum number of reclaimed leases per cycle.";
}
leaf max-reclaim-time {
type uint32;
units "milliseconds";
default 250;
description "Maximum duration of a reclamation cycle.";
}
leaf unwarned-reclaim-cycles {
type uint32;
default 5;
description "Maximum numbers of uncomplete cycles before warning.";
}
}
}
grouping control-socket {
description "Control socket grouping.";
container control-socket {
presence "Have control socket.";
description "Control socket container.";
uses kea:control-socket;
}
}
grouping dhcp-ddns {
description "DHCP-DDNS grouping.";
container dhcp-ddns {
description "DHCP-DDNS client setup.";
leaf enable-updates {
type boolean;
default false;
description "Enable DHCP-DDNS updates.";
}
leaf qualifying-suffix {
type string;
description "DHCP-DDNS qualifying suffix.";
}
leaf server-ip {
type inet:ip-address;
default "127.0.0.1";
description "DHCP-DDNS server IP address.";
}
leaf server-port {
type uint16;
default 53001;
description "DHCP-DDNS server port.";
}
leaf sender-ip {
type inet:ip-address;
description "DHCP-DDNS sender IP address.";
}
leaf sender-port {
type uint16;
description "DHCP-DDNS sender port.";
}
leaf max-queue-size {
type uint32;
default 1024;
description "Maximum DHCP-DDNS queue size.";
}
leaf ncr-protocol {
type enumeration {
enum "UDP" {
description "UDP transport";
}
enum "TCP" {
description "TCP transport";
}
}
default "UDP";
description "Protocol to use for DHCP-DDNS communication.
Currently only UDP is supported.";
}
leaf ncr-format {
type enumeration {
enum "JSON" {
description "JSON format";
}
}
default "JSON";
description "Packet format to use for DHCP-DDNS.";
}
leaf always-include-fqdn {
type boolean;
description "???";
}
leaf override-no-update {
type boolean;
default false;
description "Ignore client request and send update.";
}
leaf override-client-update {
type boolean;
default false;
description "Ignore client delegation.";
}
leaf replace-client-name {
type enumeration {
enum "when-present" {
description "When the client sent a name.";
}
enum "never" {
description "Never replace or generate a name.";
}
enum "always" {
description "Always replace or generate a name.";
}
enum "when-not-present" {
description "When the client did not send a name.";
}
}
default "never";
description "Replace the name provided by the client.";
}
leaf generated-prefix {
type string;
default "myhost";
description "DHCP-DDNS generated prefix.";
}
leaf hostname-char-set {
type string;
description "A regex defining invalid characters. If detected, those
will be replaced by hostname-char-replacement.";
}
leaf hostname-char-replacement {
type string;
description "Replacement for invalid charaters. See
hostname-char-set.";
}
uses kea:user-context {
refine user-context {
description "DHCP-DDNS user context. Arbitrary JSON data can
be stored here.";
}
}
}
}
grouping sanity-checks {
description "Sanity checks grouping.";
container sanity-checks {
description "Sanity checks container.";
leaf lease-checks {
type enumeration {
enum "none" {
description "No checks.";
}
enum "warn" {
description "When a check fails print a warning and accept the
lease.";
}
enum "fix" {
description "When a check fails try to fix it and accept the
lease.";
}
enum "fix-del" {
description "When a check fails try to fix it and reject the
lease if still bad.";
}
enum "del" {
description "When a check fails reject the lease.";
}
}
default "warn";
description "Lease checks: verify subnet-id consistency on memfile
loading.";
}
}
}
grouping client-class {
description "Client class grouping.";
leaf client-class {
type string;
description "Client class entry.";
}
}
grouping pool-client-class {
description "Client class grouping for a pool.";
uses client-class {
refine client-class {
description "Pool client class guard (only clients belonging
to this class will be allowed in this pool).";
}
}
}
grouping subnet-client-class {
description "Client class grouping for a subnet.";
uses client-class {
refine client-class {
description "Subnet client class guard (only clients belonging to this
class will be allowed in this subnet).";
}
}
}
grouping network-client-class {
description "Client class grouping for a shared network.";
uses client-class {
refine client-class {
description "Shared network client class guard (only clients
belonging to this class will be allowed in this
shared network).";
}
}
}
grouping require-client-classes {
description "Require client classes grouping.";
leaf-list require-client-classes {
type string;
description "List of client classes.";
}
}
grouping pool-require-client-classes {
description "Require client classes grouping for a pool.";
uses require-client-classes {
refine require-client-classes {
description "Pool require client classes.";
}
}
}
grouping subnet-require-client-classes {
description "Require client classes grouping for a subnet.";
uses require-client-classes {
refine require-client-classes {
description "Subnet require client classes.";
}
}
}
grouping network-require-client-classes {
description "Require client classes grouping for a shared network.";
uses require-client-classes {
refine require-client-classes {
description "Shared network require client classes.";
}
}
}
grouping interface {
description "Interface grouping.";
leaf interface {
type string;
description "Interface entry.";
}
}
grouping subnet-interface {
description "Interface grouping for a subnet.";
uses interface {
refine interface {
description "Name of the network interface this subnet is directly
accessible with (optional).";
}
}
}
grouping network-interface {
description "Interface grouping for a shared network.";
uses interface {
refine interface {
description "Specifies the network interface this shared network is
directly accessible with. (optional)";
}
}
}
// To move to DHCPv6.
grouping interface-id {
description "Interface ID grouping.";
leaf interface-id {
type string;
description "Interface ID entry.";
}
}
grouping subnet-interface-id {
description "Interface ID grouping for a subnet.";
uses interface-id {
refine interface-id {
description "Subnet interface-id option.";
}
}
}
grouping network-interface-id {
description "Interface ID grouping for a shared network.";
uses interface-id {
refine interface-id {
description "Shared network interface-id option.";
}
}
}
grouping subnet-id {
description "Subnet ID grouping.";
leaf id {
type uint32 {
range 1..max;
}
mandatory true;
description "Subnet ID, a unique identifier used to locate or reference
a subnet.";
}
}
grouping host-identifier {
description "Host identifier grouping.";
leaf identifier {
type string;
description "Host identifier.";
}
}
grouping host-hostname {
description "Host DNS name grouping.";
leaf hostname {
type string;
description "Host DNS name.";
}
}
grouping host-client-classes {
description "Host client classes grouping.";
leaf-list client-classes {
type string;
description "Host client classes (if host identifier matches, a
client's packet will be added to the classes liste
here.)";
}
}
grouping host-subnet-id {
description "Host subnet ID grouping.";
leaf subnet-id {
type uint32;
mandatory true;
description "Host subnet ID.";
}
}
grouping reservation-mode {
description "Reservation mode grouping.";
leaf reservation-mode {
type host-reservation-mode;
description "Reservation mode entry.";
}
}
grouping subnet-reservation-mode {
description "Reservation mode grouping for a subnet.";
uses reservation-mode {
refine reservation-mode {
description "Subnet host reservation mode.";
}
}
}
grouping network-reservation-mode {
description "Reservation mode grouping for a shared network.";
uses reservation-mode {
refine reservation-mode {
description "Shared network host reservation mode.";
}
}
}
grouping interfaces-re-detect {
description "Interfaces re-detect grouping.";
leaf re-detect {
type boolean;
default false;
description "Re-detect interfaces at each reconfiguration.";
}
}
grouping class-name {
description "Client class name grouping.";
leaf name {
type string;
mandatory true;
description "Name of the client class.";
}
}
grouping class-test {
description "Client class test grouping.";
leaf test {
type string;
description "Defines an expression that evaluates every incoming
packet.";
}
}
grouping class-only-if-required {
description "Client class only-if-required grouping.";
leaf only-if-required {
type boolean;
default false;
description "Client class only if required flag.";
}
}
grouping option-def-name {
description "Option definition name grouping.";
leaf name {
type string;
mandatory true;
description "Name of the new option being defined.";
}
}
grouping option-def-type {
description "Option definition type grouping.";
leaf type {
type string;
mandatory true;
description "Type of the new option being defined (such as
string, record or uint8).";
}
}
grouping option-def-record-types {
description "Option definition record types grouping.";
leaf record-types {
type string;
description "Option definition record types.";
}
}
grouping option-def-encapsulate {
description "Option definition encapsulate grouping.";
leaf encapsulate {
type string;
description "Defines option space this new option encapsulates.
Usually empty.";
}
}
grouping option-data-name {
description "Option data name grouping.";
leaf name {
type string;
description "Option name.";
}
}
grouping option-data-data {
description "Option data data grouping.";
leaf data {
type string;
description "Option data.";
}
}
grouping option-data-csv-format {
description "Option data csv-format grouping.";
leaf csv-format {
type boolean;
default true;
description "If true, the option is specified as comma separated
values. If false, it is expected as a hex string.";
}
}
grouping option-data-always-send {
description "Option data always-send grouping.";
leaf always-send {
type boolean;
default false;
description "Defines whether to always send the option,
regardless if the client requested it or not.";
}
}
grouping option-def-array {
description "Option data array grouping.";
leaf array {
type boolean;
default false;
description "Option definition array flag.";
}
}
grouping decline-probation-period {
description "Decline probation period grouping.";
leaf decline-probation-period {
type uint32;
units "seconds";
default 86400;
description "Decline probabation period.";
}
}
grouping network-name {
description "Shared network name grouping.";
leaf name {
type string;
mandatory true;
description "Shared network name.";
}
}
grouping dhcp4o6-port {
description "DHCPv4-over-DHCPv6 port grouping.";
leaf dhcp4o6-port {
type uint16;
description "DHCPv4-over-DHCPv6 interserver port.";
}
}
grouping pool-user-context {
description "User context grouping for a pool.";
uses kea:user-context {
refine user-context {
description "Pool user context. Arbitrary JSON data can be
stored here.";
}
}
}
grouping host-user-context {
description "User context grouping for a host reservation.";
uses kea:user-context {
refine user-context {
description "Host user context. Arbitrary JSON data can be
stored here.";
}
}
}
grouping subnet-user-context {
description "User context grouping for a subnet.";
uses kea:user-context {
refine user-context {
description "Subnet user context. Arbitrary JSON data can be
stored here.";
}
}
}
grouping network-user-context {
description "User context grouping for a shared network.";
uses kea:user-context {
refine user-context {
description "Shared network user context. Arbitrary JSON data can be
stored here.";
}
}
}
grouping interfaces-user-context {
description "User context grouping for interfaces.";
uses kea:user-context {
refine user-context {
description "Interfaces user context. Arbitrary JSON data can
be stored here.";
}
}
}
grouping class-user-context {
description "User context grouping for a client class.";
uses kea:user-context {
refine user-context {
description "Client class user context. Arbitrary JSON data can
be stored here.";
}
}
}
grouping option-def-user-context {
description "User context grouping for an option definition.";
uses kea:user-context {
refine user-context {
description "Option definition user context. Arbitrary JSON data
can be stored here.";
}
}
}
grouping option-data-user-context {
description "User context grouping for an option data.";
uses kea:user-context {
refine user-context {
description "Option user context. Arbitrary JSON data can be
stored here.";
}
}
}
}

View File

@@ -0,0 +1,526 @@
module kea-dhcp4-server {
yang-version 1.1;
namespace "urn:ietf:params:xml:ns:yang:kea-dhcp4-server";
prefix "kea-dhcp4-server";
import ietf-inet-types {
prefix inet;
}
import kea-types {
prefix kea;
}
import kea-dhcp-types {
prefix dhcp;
}
import kea-logging {
prefix logging;
}
organization "Internet Systems Consortium";
contact "kea-dev@lists.isc.org";
description "This model defines a YANG data model that can be
used to configure and manage a Kea DHCPv4 server.";
revision 2018-08-20 {
description "Initial revision";
reference "";
}
/*
* Typedefs
*/
typedef host-identifier-type {
type enumeration {
enum "duid" {
description "DUID";
}
enum "hw-address" {
description "Hardware address";
}
enum "circuit-id" {
description "Circuit-id option";
}
enum "client-id" {
description "Client identifier";
}
enum "flex-id" {
description "Flexible identifier";
}
}
description "Host identifier type.";
}
/*
* Groupings
*/
grouping match-client-id {
description "Match client ID grouping.";
leaf match-client-id {
type boolean;
default true;
description "Use client-id for lease lookups. If set to false, client-id
will be ignored.";
}
}
grouping next-server {
description "Next server address grouping.";
leaf next-server {
type inet:ipv4-address;
description "Next server IPv4 address. If set, this value will be set
in siaddr field.";
}
}
grouping server-hostname {
description "Server hostname grouping.";
leaf server-hostname {
type string;
description "Server hostname (up to 64 bytes).";
}
}
grouping boot-file-name {
description "Boot file name grouping.";
leaf boot-file-name {
type string;
description "Boot file name (up to 128 bytes).";
}
}
grouping relay {
description "Relay grouping.";
leaf-list ip-addresses {
type inet:ipv4-address;
description "IPv4 addresses.";
}
}
grouping subnet4-list {
description "Subnet4 list grouping.";
list subnet4 {
key id;
ordered-by user;
description "List of IPv4 subnets.";
uses dhcp:valid-lifetime;
uses dhcp:renew-timer;
uses dhcp:rebind-timer;
uses option-data-list;
container pools {
description "List of pools.";
list pool {
key "start-address end-address";
ordered-by user;
description "Pool entry.";
leaf prefix {
type inet:ipv4-prefix;
description "Defines a pool of dynamic IPv4 addresses to be managed
by the server.";
}
leaf start-address {
type inet:ipv4-address;
mandatory true;
description "First IPv4 address in a pool.";
}
leaf end-address {
type inet:ipv4-address;
mandatory true;
description "Last IPv4 address in a pool.";
}
uses option-data-list;
uses dhcp:pool-client-class;
uses dhcp:pool-require-client-classes;
uses dhcp:pool-user-context;
}
}
leaf subnet {
type inet:ipv4-prefix;
mandatory true;
description "IPv4 subnet prefix.";
}
uses dhcp:subnet-interface;
uses dhcp:subnet-interface-id;
uses dhcp:subnet-id;
uses dhcp:subnet-client-class;
uses dhcp:subnet-require-client-classes;
container reservations {
description "A container with host reservations specific to
this IPv4 subnet.";
list host {
key "identifier-type identifier";
description "Host reservation entry.";
leaf identifier-type {
type host-identifier-type;
description "Host identifier type.";
}
uses dhcp:host-identifier;
leaf ip-address {
type inet:ipv4-address;
description "Host reserved IPv4 address.";
}
uses dhcp:host-hostname;
uses dhcp:host-client-classes;
uses option-data-list;
uses next-server;
uses server-hostname;
uses boot-file-name;
uses dhcp:host-user-context;
}
}
uses dhcp:subnet-reservation-mode;
container relay {
description "Optional information about relay agent.";
uses relay;
}
uses match-client-id;
uses next-server;
uses server-hostname;
uses boot-file-name;
leaf subnet-4o6-interface {
type string;
description "Subnet DHCPv4-over-DHCPv6 interface.";
}
leaf subnet-4o6-interface-id {
type string;
description "Subnet DHCPv4-over-DHCPv6 interface-id option.";
}
leaf subnet-4o6-subnet {
type inet:ipv6-prefix;
description "Subnet DHCPv4-over-DHCPv6 IPv6 prefix.";
}
uses dhcp:subnet-user-context;
}
}
grouping client-class {
description "Client class grouping.";
uses dhcp:class-name;
uses dhcp:class-test;
uses dhcp:class-only-if-required;
uses option-def-list;
uses option-data-list;
uses next-server;
uses server-hostname;
uses boot-file-name;
uses dhcp:class-user-context;
}
grouping option-def-list {
description "Option definition list grouping.";
container option-def-list {
description "List with custom option definitions.";
list option-def {
key "code space";
description "Option definition entry.";
leaf code {
type uint8;
mandatory true;
description "Option code to be used by the new option definition.";
}
leaf space {
type string;
mandatory true;
description "Option space for the new option (typically dhcp4).";
}
uses dhcp:option-def-name;
uses dhcp:option-def-type;
uses dhcp:option-def-record-types;
uses dhcp:option-def-encapsulate;
uses dhcp:option-def-array;
uses dhcp:option-def-user-context;
}
}
}
grouping option-data-list {
description "Option data list grouping.";
container option-data-list {
description "Option data list.";
list option-data {
key "code space";
description "Option data entry.";
leaf code {
type uint8;
mandatory true;
description "Option code.";
}
leaf space {
type string;
mandatory true;
description "Option space.";
}
uses dhcp:option-data-name;
uses dhcp:option-data-data;
uses dhcp:option-data-csv-format;
uses dhcp:option-data-always-send;
uses dhcp:option-data-user-context;
}
}
}
/*
* Data Nodes
*/
container config {
// config true;
description "Contains DHCPv4 server configuration.";
uses dhcp:valid-lifetime;
uses dhcp:renew-timer;
uses dhcp:rebind-timer;
uses dhcp:decline-probation-period;
container subnet4 {
description "Global list of IPv4 subnets.";
uses subnet4-list;
}
container shared-networks {
description "Defines a list of IPv4 shared networks.";
list shared-network {
key name;
description "List of IPv4 shared networks.";
uses dhcp:network-name;
container subnet4 {
description "List of IPv4 subnets that belong to this shared
network.";
uses subnet4-list;
}
uses dhcp:network-interface;
uses dhcp:renew-timer;
uses dhcp:rebind-timer;
uses option-data-list;
uses match-client-id;
uses next-server;
uses server-hostname;
uses boot-file-name;
container relay {
description "Optional information about relay agent.";
uses relay;
}
uses dhcp:network-reservation-mode;
uses dhcp:network-client-class;
uses dhcp:network-require-client-classes;
uses dhcp:valid-lifetime;
uses dhcp:network-user-context;
}
}
container interfaces-config {
description "Network interfaces configuration.";
leaf-list interfaces {
type string;
description "Name of the interface (e.g. eth0) or name/address
(e.g. eth0/192.168.1.1) or * (use all interfaces).";
}
leaf dhcp-socket-type {
type enumeration {
enum "raw" {
description "DHCP service uses RAW sockets.";
}
enum "udp" {
description "DHCP service uses UDP sockets.";
}
}
default "raw";
description "Type of sockets to use.";
}
leaf outbound-interface {
type enumeration {
enum "same-as-inbound" {
description "Send the response on the interface where the query
was received.";
}
enum "use-routing" {
description "Use kernel routing.";
}
}
default "same-as-inbound";
description "Control the interface used to send a response.";
}
uses dhcp:interfaces-re-detect;
uses dhcp:interfaces-user-context;
}
uses dhcp:databases;
leaf-list host-reservation-identifiers {
type host-identifier-type;
description "Host reservation identifiers.";
}
container client-classes {
description "Client classes.";
list client-class {
key name;
ordered-by user;
description "List of client classes.";
uses client-class;
}
}
uses option-def-list;
uses option-data-list;
uses kea:hooks-libraries;
uses dhcp:expired-leases-processing;
uses dhcp:dhcp4o6-port;
uses dhcp:control-socket;
uses dhcp:dhcp-ddns;
leaf echo-client-id {
type boolean;
default true;
description "Send client-id back when the client sent it. This
is conformant with RFC6842, but some older, buggy
clients have problems with it.";
}
uses match-client-id;
uses next-server;
uses server-hostname;
uses boot-file-name;
uses kea:user-context {
refine user-context {
description "DHCPv4 server user context. Arbitrary JSON data can
be stored here.";
}
}
uses dhcp:sanity-checks;
}
container logging {
// config true;
description "Logging";
uses logging:configuration;
}
/*
* State data
*/
container state {
config false;
description "State of Kea DHCPv4 server.";
container leases {
description "Kea DHCPv4 leases.";
list lease {
key ip-address;
description "List of Kea DHCPv4 leases.";
leaf ip-address {
type inet:ipv4-address;
mandatory true;
description "Lease IP address.";
}
leaf hw-address {
type binary;
mandatory true;
description "Lease hardware address.";
}
leaf client-id {
type binary;
description "Lease client-id.";
}
uses dhcp:valid-lifetime {
refine valid-lifetime {
mandatory true;
}
}
leaf cltt {
type uint32;
units "seconds";
mandatory true;
description "Lease client last transmission time.";
}
leaf subnet-id {
type uint32;
mandatory true;
description "Lease subnet ID.";
}
leaf fqdn-fwd {
type boolean;
default false;
description "Lease FQDN forward flag.";
}
leaf fqdn-rev {
type boolean;
default false;
description "Lease FQDN reverse lag.";
}
leaf hostname {
type string;
default "";
description "Lease hostname.";
}
leaf state {
type dhcp:lease-state;
default "default";
description "Lease state.";
}
leaf user-context {
type kea:user-context;
description "Lease user context.";
}
}
}
container lease-stats {
description "Lease statistics.";
list subnet {
key subnet-id;
description "List of IPv4 subnets.";
leaf subnet-id {
type uint32;
mandatory true;
description "Subnet ID.";
}
leaf total-addresses {
type uint32;
mandatory true;
description "Total addresses counter.";
}
leaf assigned-addresses {
type uint32;
mandatory true;
description "Assigned addresses counter.";
}
leaf declined-addresses {
type uint32;
mandatory true;
description "Declined addresses counter.";
}
}
}
container hosts {
description "Kea DHCPv4 hosts.";
list host {
key "subnet-id identifier-type identifier";
description "List of Kea DHCPv4 hosts.";
leaf identifier-type {
type host-identifier-type;
mandatory true;
description "Host identifier type.";
}
uses dhcp:host-identifier;
uses dhcp:host-subnet-id;
leaf ip-address {
type inet:ipv4-address;
description "Host reserved IP address.";
}
uses dhcp:host-hostname;
uses dhcp:host-client-classes;
uses option-data-list;
uses next-server;
uses server-hostname;
uses boot-file-name;
uses dhcp:host-user-context;
leaf auth-key {
type string;
description "Host authentication key (unused in DHCPv4).";
}
}
}
}
}

View File

@@ -0,0 +1,594 @@
module kea-dhcp6-server {
yang-version 1.1;
namespace "urn:ietf:params:xml:ns:yang:kea-dhcp6-server";
prefix "kea-dhcp6-server";
import ietf-inet-types {
prefix inet;
}
import kea-types {
prefix kea;
}
import kea-dhcp-types {
prefix dhcp;
}
import kea-logging {
prefix logging;
}
organization "Internet Systems Consortium";
contact "kea-dev@lists.isc.org";
description "This model defines a YANG data model that can be
used to configure and manage a Kea DHCPv6 server.";
revision 2018-08-20 {
description "Initial revision";
reference "";
}
/*
* Typedefs
*/
typedef host-identifier-type {
type enumeration {
enum "duid" {
description "DUID";
}
enum "hw-address" {
description "Hardware address";
}
enum "flex-id" {
description "Flexible identifier";
}
}
description "Host identifier type.";
}
/*
* Groupings
*/
grouping preferred-lifetime {
description "Preferred lifetime grouping.";
leaf preferred-lifetime {
type uint32;
units "seconds";
description "Preferred lifetime.";
}
}
grouping relay {
description "Relay grouping.";
leaf-list ip-addresses {
type inet:ipv6-address;
description "IPv6 addresses.";
}
}
grouping rapid-commit {
description "Rapid commit grouping.";
leaf rapid-commit {
type boolean;
default false;
description "Rapid commit entry.";
}
}
grouping subnet6-list {
description "Subnet6 list grouping.";
list subnet6 {
key id;
ordered-by user;
description "List of IPv6 subnets.";
uses preferred-lifetime;
uses dhcp:valid-lifetime;
uses dhcp:renew-timer;
uses dhcp:rebind-timer;
uses option-data-list;
container pools {
description "List of pools.";
list pool {
key "start-address end-address";
ordered-by user;
description "Pool entry.";
leaf prefix {
type inet:ipv6-prefix;
description "Pool prefix.";
}
leaf start-address {
type inet:ipv6-address;
mandatory true;
description "First IPv6 address in a pool.";
}
leaf end-address {
type inet:ipv6-address;
mandatory true;
description "Last IPv6 address in a pool.";
}
uses option-data-list;
uses dhcp:pool-client-class;
uses dhcp:pool-require-client-classes;
uses dhcp:pool-user-context;
}
}
container pd-pools {
description "List of prefix delegation pools.";
list pd-pool {
key prefix;
ordered-by user;
description "Prefix delegation pool entry.";
leaf prefix {
type inet:ipv6-prefix;
mandatory true;
description "IPv6 pool prefix.";
}
leaf delegated-len {
type uint8;
description "Prefix pool delegated length.";
}
uses option-data-list;
uses dhcp:client-class {
refine client-class {
description "Prefix pool client class guard. (only
clients belonging to this class will be
allowed in this pool).";
}
}
uses dhcp:require-client-classes {
refine require-client-classes {
description "Prefix pool require client classes.";
}
}
leaf excluded-prefix {
type inet:ipv6-prefix;
description "Prefix pool excluded prefix.";
}
uses kea:user-context {
refine user-context {
description "Prefix pool user context. Arbitrary JSON data
can be stored here.";
}
}
}
}
leaf subnet {
type inet:ipv6-prefix;
mandatory true;
description "IPv6 subnet prefix.";
}
uses dhcp:subnet-interface;
uses dhcp:subnet-interface-id;
uses dhcp:subnet-id;
uses rapid-commit {
refine rapid-commit {
description "Subnet rapid commit flag.";
}
}
uses dhcp:subnet-client-class;
uses dhcp:require-client-classes;
container reservations {
description "A container with host reservations specific to
this IPv6 subnet.";
list host {
key "identifier-type identifier";
description "Host reservation entry.";
leaf identifier-type {
type host-identifier-type;
mandatory true;
description "Host identifier type.";
}
uses dhcp:host-identifier;
leaf-list ip-addresses {
type inet:ipv6-address;
description "Host reserved IP addresses.";
}
leaf-list prefixes {
type inet:ipv6-prefix;
description "Host reserved IP prefixes.";
}
uses dhcp:host-hostname;
uses dhcp:host-client-classes;
uses option-data-list;
uses dhcp:host-user-context;
}
}
uses dhcp:subnet-reservation-mode;
container relay {
description "Optional information about relay agent.";
uses relay;
}
uses dhcp:subnet-user-context;
}
}
grouping client-class {
description "Client class grouping.";
uses dhcp:class-name;
uses dhcp:class-test;
uses dhcp:class-only-if-required;
uses option-data-list;
uses dhcp:class-user-context;
}
grouping option-def-list {
description "Option definition list grouping.";
container option-def-list {
description "Option definition list.";
list option-def {
key "code space";
description "Option definition entry.";
leaf code {
type uint16;
mandatory true;
description "Option code to be used by the new option definition.";
}
leaf space {
type string;
mandatory true;
description "Option space for the new option (typically dhcp6).";
}
uses dhcp:option-def-name;
uses dhcp:option-def-type;
uses dhcp:option-def-record-types;
uses dhcp:option-def-encapsulate;
uses dhcp:option-def-array;
uses dhcp:option-def-user-context;
}
}
}
grouping option-data-list {
description "Option data list grouping.";
container option-data-list {
description "Option data list.";
list option-data {
key "code space";
description "Option data entry.";
leaf code {
type uint16;
mandatory true;
description "Option code.";
}
leaf space {
type string;
mandatory true;
description "Option space.";
}
uses dhcp:option-data-name;
uses dhcp:option-data-data;
uses dhcp:option-data-csv-format;
uses dhcp:option-data-always-send;
uses dhcp:option-data-user-context;
}
}
}
/*
* Data Nodes
*/
container config {
// config true;
description "Contains DHCPv6 server configuration.";
uses preferred-lifetime;
uses dhcp:valid-lifetime;
uses dhcp:renew-timer;
uses dhcp:rebind-timer;
uses dhcp:decline-probation-period;
container subnet6 {
description "Global subnet6 list.";
uses subnet6-list;
}
container shared-networks {
description "Defines a list of IPv6 shared networks.";
list shared-network {
key name;
uses dhcp:network-name;
description "List of IPv4 shared networks.";
container subnet6 {
description "List of IPv6 subnets that belong to this shared
network.";
uses subnet6-list;
}
uses dhcp:network-interface;
uses dhcp:network-interface-id;
uses dhcp:renew-timer;
uses dhcp:rebind-timer;
uses option-data-list;
container relay {
description "Optional information about relay agent.";
uses relay;
}
uses dhcp:network-reservation-mode;
uses dhcp:network-client-class;
uses dhcp:require-client-classes;
uses preferred-lifetime;
uses rapid-commit {
refine rapid-commit {
description "Shared network rapid commit flag.";
}
}
uses dhcp:valid-lifetime;
uses dhcp:network-user-context;
}
}
container interfaces-config {
description "Network interfaces configuration.";
leaf-list interfaces {
type string;
description "Name of the interface (e.g. eth0) or name/address
(e.g. eth0/2001:db8::1) or * (use all interfaces).";
}
uses dhcp:interfaces-re-detect;
uses dhcp:interfaces-user-context;
}
uses dhcp:databases;
leaf-list relay-supplied-options {
type string;
description "Relay supplied options.";
}
leaf-list mac-sources {
type string;
description "MAC address sources.";
}
leaf-list host-reservation-identifiers {
type host-identifier-type;
description "Host reservation identifiers.";
}
container client-classes {
description "Client classes.";
list client-class {
key name;
ordered-by user;
description "List of client classes.";
uses client-class;
}
}
uses option-def-list;
uses option-data-list;
uses kea:hooks-libraries;
uses dhcp:expired-leases-processing;
container server-id {
description "Server DUID.";
leaf type {
type enumeration {
enum "LLT" {
description "Link-layer address and timestamp.";
}
enum "EN" {
description "Enterprise number.";
}
enum "LL" {
description "Link-layer address.";
}
}
description "Server DIOD type.";
}
leaf identifier {
type string;
description "Server DUID identifier.";
}
leaf time {
type uint32;
description "Server DUID time.";
}
leaf htype {
type uint16;
description "Server DUID hardware type.";
}
leaf enterprise-id {
type uint32;
description "Server DUID enterprise ID.";
}
leaf persist {
type boolean;
default true;
description "Server DUID persist flag.";
}
leaf user-context {
type kea:user-context;
description "Server DUID user context.";
}
}
uses dhcp:dhcp4o6-port;
uses dhcp:control-socket;
uses dhcp:dhcp-ddns;
uses kea:user-context {
refine user-context {
description "DHCPv6 server user context. Arbitrary JSON data can
be stored here.";
}
}
uses dhcp:sanity-checks;
}
container logging {
// config true;
description "Logging";
uses logging:configuration;
}
/*
* State data
*/
container state {
config false;
description "State of Kea DHCPv6 server.";
container leases {
description "Kea DHCPv6 leases.";
list lease {
key ip-address;
description "List of Kea DHCPv6 leases.";
leaf ip-address {
type inet:ipv6-address;
mandatory true;
description "Lease IP address.";
}
leaf duid {
type binary;
mandatory true;
description "Lease DUID.";
}
uses dhcp:valid-lifetime {
refine valid-lifetime {
mandatory true;
}
}
leaf cltt {
type uint32;
units "seconds";
mandatory true;
description "Lease client last transmission time.";
}
leaf subnet-id {
type uint32;
mandatory true;
description "Lease subnet ID.";
}
leaf preferred-lifetime {
type uint32;
units "seconds";
mandatory true;
description "Lease preferred lifetime.";
}
leaf lease-type {
type enumeration {
enum "IA_NA" {
description "Identity association for non-temporary addresses.";
}
enum "IA_TA" {
description "Identity association for temporary addresses.";
}
enum "IA_PD" {
description "Identity association for prefix delegation.";
}
}
mandatory true;
description "Lease IA type.";
}
leaf iaid {
type uint32;
mandatory true;
description "Lease IA ID.";
}
leaf prefix-length {
type uint8 {
range 0..128;
}
description "Lease prefix length.";
}
leaf fqdn-fwd {
type boolean;
default false;
description "Lease FQDN forward flag.";
}
leaf fqdn-rev {
type boolean;
default false;
description "Lease FQDN reverse lag.";
}
leaf hostname {
type string;
default "";
description "Lease hostname.";
}
leaf state {
type dhcp:lease-state;
default "default";
description "Lease state.";
}
leaf user-context {
type kea:user-context;
description "Lease user context.";
}
leaf hw-address {
type string;
description "Lease hardware address.";
}
}
}
container lease-stats {
description "Lease statistics.";
list subnet {
key subnet-id;
description "List of IPv6 subnets.";
leaf subnet-id {
type uint32;
mandatory true;
description "Subnet ID.";
}
leaf total-nas {
type uint32;
mandatory true;
description "Total non-temporary addresses counter.";
}
leaf assigned-nas {
type uint32;
mandatory true;
description "Assigned non-temporary counter.";
}
leaf declined-nas {
type uint32;
mandatory true;
description "Declined non-temporary addresses counter.";
}
leaf total-pds {
type uint32;
mandatory true;
description "Total delegated prefixes counter.";
}
leaf assigned-pds {
type uint32;
mandatory true;
description "Assigned delegated prefixe counter.";
}
}
}
container hosts {
description "Kea DHCPv6 hosts.";
list host {
key "subnet-id identifier-type identifier";
description "List of Kea DHCPv6 hosts.";
leaf identifier-type {
type host-identifier-type;
mandatory true;
description "Host identifier type.";
}
uses dhcp:host-identifier;
uses dhcp:host-subnet-id;
leaf-list ip-addresses {
type inet:ipv6-address;
description "Host reserved IP addresses.";
}
leaf-list prefixes {
type inet:ipv6-prefix;
description "Host reserved prefixes.";
}
uses dhcp:host-hostname;
uses dhcp:host-client-classes;
uses option-data-list;
uses dhcp:host-user-context;
leaf auth-key {
type string;
description "Host authentication key.";
}
}
}
}
}

View File

@@ -1,578 +0,0 @@
module kea-dhcpv4-server {
yang-version 1.1;
namespace "urn:ietf:params:xml:ns:yang:kea-dhcpv4-server";
prefix "dhcpv4-server";
import ietf-inet-types {
prefix inet;
}
import ietf-yang-types {
prefix yang;
}
import ietf-dhcpv4-options {
prefix dhcpv4-options;
}
import ietf-dhcpv4-types {
prefix dhcpv4-types;
}
import ietf-interfaces {
prefix if;
}
organization "DHC WG";
contact
"piotr.strzyzewski@polsl.pl";
description "This model defines a YANG data model that can be
used to configure and manage Kea-dhcp4, a DHCPv4 server from ISC.";
revision 2018-07-14 {
description "Initial revision; mostly based on DHCPv6 version";
reference "";
}
/*
* Typedef
*/
typedef threshold {
type union {
type uint16 {
range 0..100;
}
type enumeration {
enum "disabled" {
description "No threshold";
}
}
}
description "Threshold value in percent";
}
/*
* Data Nodes
*/
container server {
presence "Enables the server";
description "DHCPv4 server portion";
/*
* Configuration data
*/
container server-config {
description "This container contains the configuration data
of a server.";
container serv-attributes {
description
"This container contains basic attributes of a DHCPv4 server
such as IPv4 address, server name and so on. Some optional
functions that can be provided by the server is also included.";
leaf name {
type string;
description "server's name";
}
leaf description {
type string;
description "description of the server.";
}
leaf-list ipv4-address {
type inet:ipv4-address;
description "server's IPv4 address.";
}
leaf-list interfaces-config {
// Note - this should probably be references to
// entries in the ietf-interfaces model
type if:interface-ref;
description "A leaf list to denote which one or more interfaces
the server should listen on. The default value is to listen
on all the interfaces. This node is also used to set a unicast
address for the server to listen with a specific interface.
For example, if someone want the server to listen on a unicast
address with a specific interface, she/he can use the format
like 'eth0/192.0.2.1'.";
}
uses dhcpv4-types:vendor-infor;
}
container option-sets {
description "DHCPv4 employs various options to carry additional
information and parameters in DHCP messages. This container defines
all the possible options that need to be configured at the server
side.";
list option-set {
key option-set-id;
description "A server may allow different option sets to be
configured for different conditions (i.e. different networks,
clients and etc). This 'option-set' list enables various sets of
options being defined and configured in a single server. Different
sets are distinguished by the key called 'option-set-id'. All the
possible options discussed above are defined in the list and each
option is corresponding to a container. Since all the options in
the list are optional, each container in this list has a 'presence'
statement to indicate whether this option (container) will be
included in the current option set or not. In addition, each container
also has a 'if-feature' statement to indicate whether the server
supports this option (container).";
leaf option-set-id {
type uint32;
description "option set id";
}
uses dhcpv4-options:server-option-definitions;
}
}
container network-ranges {
description "This model supports a hierarchy
to achieve dynamic configuration. That is to say we could configure the
server at different levels through this model. The top level is a global
level which is defined as the container 'network-ranges'. The following
levels are defined as sub-containers under it. The 'network-ranges'
contains the parameters (e.g. option-sets) that would be allocated to
all the clients served by this server.";
leaf option-set-id {
type leafref {
path "/server/server-config/option-sets/option-set/option-set-id";
}
description
"The ID field of relevant global option-set to be provisioned to
clients.";
}
list network-range {
key network-range-id;
description
"Under the 'network-ranges' container, a 'network-range' list
is defined to configure the server at a network level which is also
considered as the second level. Different network are identified by the
key 'network-range-id'. This is because a server may have different
configuration parameters (e.g. option sets) for different networks.";
leaf network-range-id {
type uint32;
mandatory true;
description "equivalent to subnet id";
}
leaf network-description {
type string;
description "description of the subnet";
}
leaf network-prefix {
type inet:ipv4-prefix;
mandatory true;
description "subnet prefix";
}
leaf option-set-id {
type leafref {
path "/server/server-config/option-sets/option-set/option-set-id";
}
description "The ID field of relevant option-set to be provisioned to
clients of this network-range.";
}
container address-pools {
description
"A container that describes the DHCPv4 server's
address pools.";
list address-pool {
key pool-id;
description "A DHCPv4 server can be configured with
several address pools. This list defines such address pools
which are distinguished by the key called 'pool-id'.";
leaf pool-id {
type uint32;
mandatory true;
description "pool id";
}
leaf pool-prefix {
type inet:ipv4-prefix;
mandatory true;
description "pool prefix";
}
leaf start-address {
type inet:ipv4-address-no-zone;
mandatory true;
description "start address";
}
leaf end-address {
type inet:ipv4-address-no-zone;
mandatory true;
description "end address";
}
leaf renew-time {
type uint32;
units "seconds";
description "renew time";
}
leaf rebind-time {
type uint32;
units "seconds";
description "rebind time";
}
// leaf rapid-commit {
// type boolean;
// mandatory false;
// description "A boolean value specifies whether the pool
// supports client-server exchanges involving two messages.";
// }
leaf client-class {
type string;
description
"If this leaf is specified, this pool will only serve
the clients belonging to this class.";
}
leaf max-address-count {
type threshold;
description "maximum count of addresses that can
be allocated in this pool. This value may be
less than count of total addresses.";
}
leaf option-set-id {
type leafref {
path "/server/server-config/option-sets/option-set/option-set-id";
}
description "The ID field of relevant option-set to be
provisioned to clients of this address-pool.";
}
}
}
container host-reservations {
description
"This container allows the server to make reservations at host level.";
list host-reservation {
key cli-id;
description "This list allows the server to reserve addresses,
prefixes, hostname and options for different clients.";
leaf cli-id {
type uint32;
mandatory true;
description "client id";
}
choice client-identifier {
description "When making reservations, the server needs to choose a
identifier to identify the client. Currently 'Client ID' and
'hardware address' are supported.";
case client-id {
description "Client ID";
leaf client-ident {
type string;
description "Client ID";
}
}
case hw-address {
description "hardware address";
leaf hardware-address {
type yang:mac-address;
description "MAC address of client";
}
}
}
leaf-list reserv-addr {
type inet:ipv4-address-no-zone;
description "reserved addr";
}
leaf hostname {
type string;
description "reserved hostname";
}
leaf option-set-id {
type leafref {
path "/server/server-config/option-sets/option-set/option-set-id";
}
description "The ID field of relevant option-set to be provisioned
in the host reservation.";
}
}
}
}
}
/*
container relay-opaque-paras {
description "This container contains some opaque values in Relay Agent
options that need to be configured on the server side only for value
match. Such Relay Agent options include Interface-Id option,
Remote-Id option and Subscriber-Id option.";
list relays {
key relay-name;
description "relay agents";
leaf relay-name {
type string;
mandatory true;
description "relay agent name";
}
list interface-info {
key if-name;
description "interface info";
leaf if-name {
type string;
mandatory true;
description "interface name";
}
leaf interface-id {
type string;
mandatory true;
description "interface id";
}
}
list subscribers {
key subscriber;
description "subscribers";
leaf subscriber {
type uint32;
mandatory true;
description "subscriber";
}
leaf subscriber-id {
type string;
mandatory true;
description "subscriber id";
}
}
list remote-host {
key ent-num;
description "remote host";
leaf ent-num {
type uint32;
mandatory true;
description "enterprise number";
}
leaf remote-id {
type string;
mandatory true;
description "remote id";
}
}
}
}
*/
}
/*
* State data
*/
container server-state {
config "false";
description "states of server";
container network-ranges {
description "This model supports a hierarchy to achieve dynamic configuration.
That is to say we could configure the server at different levels through
this model. The top level is a global level which is defined as the container
'network-ranges'. The following levels are defined as sub-containers under it.
The 'network-ranges' contains the parameters (e.g. option-sets) that would be
allocated to all the clients served by this server.";
list network-range {
key network-range-id;
description "The ID field of relevant option-set to be provisioned
to clients of this network-range.";
leaf network-range-id {
type uint32;
mandatory true;
description "equivalent to subnet id";
}
container address-pools {
description "A container that describes the DHCPv4 server's address pools";
list address-pool {
key pool-id;
description "A DHCPv4 server can be configured with
several address pools. This list defines such address pools
which are distinguished by the key called 'pool-id'.";
leaf pool-id {
type uint32;
mandatory true;
description "pool id";
}
leaf total-address-count {
type uint32;
mandatory true;
description "count of total addresses in the pool";
}
leaf allocated-address-conut {
type uint32;
mandatory true;
description "count of allocated addresses in the pool";
}
}
list binding-info {
key cli-id;
description "A list that records a binding information for each DHCPv4
client that has already been allocated IPv4 addresses.";
leaf cli-id {
type uint32;
mandatory true;
description "client id";
}
list cli-hw {
key hw-address;
description "client host id";
leaf hw-address {
type yang:mac-address;
mandatory true;
description "HW address";
}
leaf-list cli-addr {
type inet:ipv4-address;
description "client addr";
}
leaf pool-id {
type uint32;
mandatory true;
description "pool id";
}
}
}
}
container host-reservations {
description "This container provides host reservations in the host level.";
list binding-info {
key cli-id;
description
"A list records a binding information for each DHCPv4
client that has already been alloated IPv4 addresses or prefixes
by host reservations.";
leaf cli-id {
type uint32;
mandatory true;
description "client id";
}
list cli-hw {
key hw-address;
description "client host id";
leaf hw-address {
type yang:mac-address;
mandatory true;
description "HW address";
}
leaf-list cli-addr {
type inet:ipv4-address;
description "client addr";
}
}
}
}
}
}
container packet-stats {
description "A container presents the packet statistics related to
the DHCPv4 server.";
leaf request-count {
type uint32;
mandatory true;
description "request counter";
}
leaf renew-count {
type uint32;
mandatory true;
description "renew counter";
}
leaf rebind-count {
type uint32;
mandatory true;
description "rebind counter";
}
leaf decline-count {
type uint32;
mandatory true;
description "decline count";
}
leaf release-count {
type uint32;
mandatory true;
description "release counter";
}
leaf info-req-count {
type uint32;
mandatory true;
description "information request counter";
}
leaf advertise-count {
type uint32;
mandatory true;
description "advertise counter";
}
leaf confirm-count {
type uint32;
mandatory true;
description "confirm counter";
}
leaf reconfigure-count {
type uint32;
mandatory true;
description "reconfigure counter";
}
leaf relay-forward-count {
type uint32;
mandatory true;
description "relay forward counter";
}
leaf relay-reply-count {
type uint32;
mandatory true;
description "relay reply counter";
}
}
}
}
/*
* Notifications
*/
notification notifications {
description "dhcpv4 server notification module";
container dhcpv4-server-event {
description "dhcpv4 server event";
container address-pool-running-out {
description "Raised when the address pool is going to
run out. A threshold for utilization ratio of the pool has
been defined in the server feature so that it will notify the
administrator when the utilization ratio reaches the
threshold, and such threshold is a settable parameter.";
leaf total-address-count {
type uint32;
mandatory true;
description "Count of total addresses in the pool.";
}
leaf max-address-count {
type uint32;
mandatory true;
description "Maximum count of addresses that can be allocated
in the pool. This value may be less than count of total
addresses.";
}
leaf allocated-address-conut {
type uint32;
mandatory true;
description "Count of allocated addresses in the pool.";
}
leaf serv-name {
type string;
description "server name";
}
leaf pool-name {
type string;
mandatory true;
description "pool name";
}
}
container invalid-client-detected {
description "Raised when the server has found a client which
can be regarded as a potential attacker. Some description
could also be included.";
leaf hw {
type yang:mac-address;
description "HW address";
}
leaf description {
type string;
description "description of the event";
}
}
}
}
}

View File

@@ -0,0 +1,108 @@
module kea-logging {
yang-version 1.1;
namespace "urn:ietf:params:xml:ns:yang:kea-logging";
prefix "kea-logging";
import kea-types {
prefix kea;
}
organization "Internet Systems Consortium";
contact "kea-dev@lists.isc.org";
description "This model defines a YANG data model that can be
used to configure and manage logging of a Kea server.";
revision 2018-08-20 {
description "Initial revision";
reference "";
}
/*
* Groupings
*/
grouping configuration {
description "Contains parameters for logging configuration.";
container loggers {
description "Loggers.";
list logger {
key name;
description "List of loggers.";
leaf name {
type string;
mandatory true;
description "Name of the logger.";
}
container output-options {
description "Output options.";
list option {
key output;
description "List of output options.";
leaf output {
type string;
description "Type of output. Special values are stdout (standard
output), stderr (standard error), syslog (syslog using default
name), syslog:name (syslog using specified name). Any other
value is interpreted as a filename.";
}
leaf flush {
type boolean;
default true;
description "When true flush buffers after each message.";
}
leaf maxsize {
type uint32;
default 10240000;
description "Maximum size of output file before rotation.
Values below 204800 including 0 disable rotation.";
}
leaf maxver {
type uint32 {
range 1..max;
}
default 1;
description "Maximum version to keep a rotated output file.";
}
}
}
leaf debuglevel {
type uint8 {
range 0..99;
}
description "What level of debug messages should be printed.";
}
leaf severity {
type enumeration {
enum "FATAL" {
description "Condition is so serious that the server cannot
continue executing";
}
enum "ERROR" {
description "Error condition. The server will continue
executing, but the results may not be as expected.";
}
enum "WARN" {
description "Out of the ordinary condition. However, the server
will continue executing normally.";
}
enum "INFO" {
description "Information message marking some event.";
}
enum "DEBUG" {
description "For debugging purposes.";
}
enum "NONE" {
description "All messages are inhibited.";
}
}
description "Category of messages logged.";
}
leaf user-context {
type kea:user-context;
description "Logger user context.";
}
}
}
}
}

View File

@@ -0,0 +1,75 @@
module kea-types {
yang-version 1.1;
namespace "urn:ietf:params:xml:ns:yang:kea-types";
prefix "kea-types";
organization "Internet Systems Consortium";
contact "kea-dev@lists.isc.org";
description "This file defines some commonly used Kea types and groupings.";
revision 2018-08-20 {
description "Initial revision";
reference "";
}
/*
* Typedef
*/
typedef user-context {
type string;
description "User context (JSON map).";
}
/*
* Grouping
*/
grouping user-context {
description "User context grouping.";
leaf user-context {
type user-context;
description "User context entry.";
}
}
grouping control-socket {
description "Control socket.";
leaf socket-name {
type string;
mandatory true;
description "Path to the UNIX socket.";
}
leaf socket-type {
type enumeration {
enum "unix" {
description "Unix socket type.";
}
}
mandatory true;
description "Socket type.";
}
leaf user-context {
type user-context;
description "Control socket user context.";
}
}
grouping hooks-libraries {
description "Hooks libraries grouping.";
container hooks-libraries {
description "Hook libraries.";
list hook-library {
key library;
description "List of hook library.";
leaf library {
type string;
mandatory true;
description "Path to the DSO.";
}
leaf parameters {
type string;
description "Parameters (JSON value).";
}
}
}
}
}

View File

@@ -0,0 +1,26 @@
// 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_SYSREPO_ERROR_H
#define ISC_SYSREPO_ERROR_H 1
#include <exceptions/exceptions.h>
namespace isc {
namespace yang {
/// @brief Sysrepo error.
class SysrepoError : public isc::Exception {
public:
SysrepoError(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what)
{}
};
}; // end of namespace isc::yang
}; // end of namespace isc
#endif // ISC_SYSREPO_ERROR_H

View File

@@ -0,0 +1,40 @@
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES) $(SYSREPO_CPPFLAGS)
AM_CPPFLAGS += -DCFG_EXAMPLES=\"$(abs_top_srcdir)/doc/examples\"
AM_CXXFLAGS = $(KEA_CXXFLAGS)
if USE_STATIC_LINK
AM_LDFLAGS = -static
endif
CLEANFILES = *.gcno *.gcda
TESTS_ENVIRONMENT = \
$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
EXTRA_DIST = keatest-module.yang
TESTS =
if HAVE_GTEST
TESTS += run_unittests
run_unittests_SOURCES = adaptor_unittests.cc
run_unittests_SOURCES += translator_unittests.cc
run_unittests_SOURCES += run_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(top_builddir)/src/lib/yang/libkea-yang.la
run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libkea-testutils.la
run_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/threads/libkea-threads.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
run_unittests_LDADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS)
run_unittests_LDADD += $(SYSREPO_LIBS) $(GTEST_LDADD)
endif
noinst_PROGRAMS = $(TESTS)

View File

@@ -0,0 +1,394 @@
// 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/adaptor.h>
#include <boost/scoped_ptr.hpp>
#include <gtest/gtest.h>
using namespace std;
using namespace isc;
using namespace isc::data;
using namespace isc::yang;
namespace {
// Test that get context works as expected i.e. moves a comment into
// the user context creating it if not exists.
TEST(AdaptorTest, getContext) {
// Empty.
string config = "{\n"
"}\n";
ConstElementPtr json = Element::fromJSON(config);
ConstElementPtr context;
ASSERT_NO_THROW(context = Adaptor::getContext(json));
EXPECT_FALSE(context);
// No relevant.
config = "{\n"
" \"foo\": 1\n"
"}\n";
json = Element::fromJSON(config);
ASSERT_NO_THROW(context = Adaptor::getContext(json));
EXPECT_FALSE(context);
// User context.
config = "{\n"
" \"foo\": 1,\n"
" \"user-context\": { \"bar\": 2 }\n"
"}\n";
json = Element::fromJSON(config);
ASSERT_NO_THROW(context = Adaptor::getContext(json));
ASSERT_TRUE(context);
EXPECT_EQ("{ \"bar\": 2 }", context->str());
// Comment.
config = "{\n"
" \"foo\": 1,\n"
" \"comment\": \"a comment\"\n"
"}\n";
json = Element::fromJSON(config);
ASSERT_NO_THROW(context = Adaptor::getContext(json));
ASSERT_TRUE(context);
EXPECT_EQ("{ \"comment\": \"a comment\" }", context->str());
// User context and comment.
config = "{\n"
" \"foo\": 1,\n"
" \"user-context\": { \"bar\": 2 },\n"
" \"comment\": \"a comment\"\n"
"}\n";
json = Element::fromJSON(config);
ASSERT_NO_THROW(context = Adaptor::getContext(json));
ASSERT_TRUE(context);
EXPECT_EQ("{ \"bar\": 2, \"comment\": \"a comment\" }", context->str());
// User context with conflicting comment and comment.
config = "{\n"
" \"foo\": 1,\n"
" \"user-context\": {\n"
" \"bar\": 2,\n"
" \"comment\": \"conflicting\"\n"
" },\n"
" \"comment\": \"a comment\"\n"
"}\n";
json = Element::fromJSON(config);
ASSERT_NO_THROW(context = Adaptor::getContext(json));
ASSERT_TRUE(context);
EXPECT_EQ("{ \"bar\": 2, \"comment\": \"a comment\" }", context->str());
}
// Test that fromParent works as expected i.e. moves parameters from the
// parent to children and not overwrite them.
TEST(AdaptorTest, fromParent) {
string config = "{\n"
" \"param1\": 123,\n"
" \"param2\": \"foo\",\n"
" \"list\": [\n"
" {\n"
" \"param1\": 234\n"
" },{\n"
" \"another\": \"entry\"\n"
" }\n"
" ]\n"
"}\n";
ConstElementPtr json = Element::fromJSON(config);
EXPECT_NO_THROW(Adaptor::fromParent("param1", json, json->get("list")));
EXPECT_NO_THROW(Adaptor::fromParent("param2", json, json->get("list")));
EXPECT_NO_THROW(Adaptor::fromParent("param3", json, json->get("list")));
string expected = "{\n"
" \"param1\": 123,\n"
" \"param2\": \"foo\",\n"
" \"list\": [\n"
" {\n"
" \"param1\": 234,\n"
" \"param2\": \"foo\"\n"
" },{\n"
" \"another\": \"entry\",\n"
" \"param1\": 123,\n"
" \"param2\": \"foo\"\n"
" }\n"
" ]\n"
"}\n";
EXPECT_TRUE(json->equals(*Element::fromJSON(expected)));
}
// Test that toParent works as expected i.e. moves parameters from children
// to the parent throwing when a value differs between two children.
TEST(AdaptorTest, toParent) {
string config = "{\n"
" \"list\": [\n"
" {\n"
" \"param2\": \"foo\",\n"
" \"param3\": 234,\n"
" \"param4\": true\n"
" },{\n"
" \"another\": \"entry\",\n"
" \"param2\": \"foo\",\n"
" \"param3\": 123,\n"
" \"param5\": false\n"
" }\n"
" ]\n"
"}\n";
ElementPtr json = Element::fromJSON(config);
EXPECT_NO_THROW(Adaptor::toParent("param1", json, json->get("list")));
EXPECT_TRUE(json->equals(*Element::fromJSON(config)));
string expected = "{\n"
" \"param2\": \"foo\",\n"
" \"list\": [\n"
" {\n"
" \"param3\": 234,\n"
" \"param4\": true\n"
" },{\n"
" \"another\": \"entry\",\n"
" \"param3\": 123,\n"
" \"param5\": false\n"
" }\n"
" ]\n"
"}\n";
EXPECT_NO_THROW(Adaptor::toParent("param2",json, json->get("list")));
EXPECT_TRUE(json->equals(*Element::fromJSON(expected)));
// param[345] have different values so it should throw.
EXPECT_THROW(Adaptor::toParent("param3",json, json->get("list")),
BadValue);
EXPECT_THROW(Adaptor::toParent("param4",json, json->get("list")),
BadValue);
EXPECT_THROW(Adaptor::toParent("param5",json, json->get("list")),
BadValue);
// And not modify the value.
EXPECT_TRUE(json->equals(*Element::fromJSON(expected)));
}
// Test for modify (maps & insert).
TEST(AdaptorTest, modifyMapInsert) {
string config = "{\n"
" \"foo\": {\n"
" \"bar\": {\n"
"}}}\n";
ElementPtr json;
ASSERT_NO_THROW(json = Element::fromJSON(config));
string spath = "[ \"foo\", \"bar\" ]";
ConstElementPtr path;
ASSERT_NO_THROW(path = Element::fromJSON(spath));
string sactions = "[\n"
"{\n"
" \"action\": \"insert\",\n"
" \"key\": \"test\",\n"
" \"value\": 1234\n"
"}]\n";
ConstElementPtr actions;
ASSERT_NO_THROW(actions = Element::fromJSON(sactions));
string result = "{\n"
" \"foo\": {\n"
" \"bar\": {\n"
" \"test\": 1234\n"
"}}}\n";
ConstElementPtr expected;
ASSERT_NO_THROW(expected = Element::fromJSON(result));
ASSERT_NO_THROW(Adaptor::modify(path, actions, json));
EXPECT_TRUE(expected->equals(*json));
}
// Test for modify (maps & replace).
TEST(AdaptorTest, modifyMapReplace) {
string config = "{\n"
" \"foo\": {\n"
" \"bar\": {\n"
" \"test1\": 1234,\n"
" \"test2\": 1234\n"
"}}}\n";
ElementPtr json;
ASSERT_NO_THROW(json = Element::fromJSON(config));
string spath = "[ \"foo\", \"bar\" ]";
ConstElementPtr path;
ASSERT_NO_THROW(path = Element::fromJSON(spath));
string sactions = "[\n"
"{\n"
" \"action\": \"insert\",\n"
" \"key\": \"test1\",\n"
" \"value\": 5678\n"
"},{\n"
" \"action\": \"replace\",\n"
" \"key\": \"test2\",\n"
" \"value\": 5678\n"
"}]\n";
ConstElementPtr actions;
ASSERT_NO_THROW(actions = Element::fromJSON(sactions));
string result = "{\n"
" \"foo\": {\n"
" \"bar\": {\n"
" \"test1\": 1234,\n"
" \"test2\": 5678\n"
"}}}\n";
ConstElementPtr expected;
ASSERT_NO_THROW(expected = Element::fromJSON(result));
ASSERT_NO_THROW(Adaptor::modify(path, actions, json));
EXPECT_TRUE(expected->equals(*json));
}
// Test for modify (maps & delete).
TEST(AdaptorTest, modifyMapDelete) {
string config = "{\n"
" \"foo\": {\n"
" \"bar\": {\n"
" \"test\": 1234\n"
"}}}\n";
ElementPtr json;
ASSERT_NO_THROW(json = Element::fromJSON(config));
string spath = "[ \"foo\", \"bar\" ]";
ConstElementPtr path;
ASSERT_NO_THROW(path = Element::fromJSON(spath));
string sactions = "[\n"
"{\n"
" \"action\": \"delete\",\n"
" \"key\": \"test\"\n"
"}]\n";
ConstElementPtr actions;
ASSERT_NO_THROW(actions = Element::fromJSON(sactions));
string result = "{\n"
" \"foo\": {\n"
" \"bar\": {\n"
"}}}\n";
ConstElementPtr expected;
ASSERT_NO_THROW(expected = Element::fromJSON(result));
ASSERT_NO_THROW(Adaptor::modify(path, actions, json));
EXPECT_TRUE(expected->equals(*json));
}
// Test for modify (lists & insert).
TEST(AdaptorTest, modifyListInsert) {
string config = "[\n"
"[{\n"
" \"foo\": \"bar\"\n"
"}]]\n";
ElementPtr json;
ASSERT_NO_THROW(json = Element::fromJSON(config));
string spath = "[ 0, { \"key\": \"foo\", \"value\": \"bar\" }]";
ConstElementPtr path;
ASSERT_NO_THROW(path = Element::fromJSON(spath));
string sactions = "[\n"
"{\n"
" \"action\": \"insert\",\n"
" \"key\": \"test\",\n"
" \"value\": 1234\n"
"}]\n";
ConstElementPtr actions;
ASSERT_NO_THROW(actions = Element::fromJSON(sactions));
string result = "[\n"
"[{\n"
" \"foo\": \"bar\",\n"
" \"test\": 1234\n"
"}]]\n";
ConstElementPtr expected;
ASSERT_NO_THROW(expected = Element::fromJSON(result));
ASSERT_NO_THROW(Adaptor::modify(path, actions, json));
EXPECT_TRUE(expected->equals(*json));
}
// Test for modify (list all & insert).
TEST(AdaptorTest, modifyListAllInsert) {
string config = "[\n"
"{},\n"
"{},\n"
"{ \"test\": 1234 },\n"
"]\n";
ElementPtr json;
ASSERT_NO_THROW(json = Element::fromJSON(config));
string spath = "[ -1 ]";
ConstElementPtr path;
ASSERT_NO_THROW(path = Element::fromJSON(spath));
string sactions = "[\n"
"{\n"
" \"action\": \"insert\",\n"
" \"key\": \"test\",\n"
" \"value\": 5678\n"
"}]\n";
ConstElementPtr actions;
ASSERT_NO_THROW(actions = Element::fromJSON(sactions));
string result = "[\n"
"{ \"test\": 5678 },\n"
"{ \"test\": 5678 },\n"
"{ \"test\": 1234 }\n"
"]\n";
ConstElementPtr expected;
ASSERT_NO_THROW(expected = Element::fromJSON(result));
ASSERT_NO_THROW(Adaptor::modify(path, actions, json));
EXPECT_TRUE(expected->equals(*json));
}
TEST(AdaptorTest, modifyListDelete) {
string config = "[[\n"
"{\n"
" \"foo\": \"bar\"\n"
"},{\n"
"},[\n"
"0, 1, 2, 3\n"
"]]]\n";
ElementPtr json;
ASSERT_NO_THROW(json = Element::fromJSON(config));
string spath = "[ 0 ]";
ConstElementPtr path;
ASSERT_NO_THROW(path = Element::fromJSON(spath));
// Put the positional first as it applies after previous actions...
string sactions = "[\n"
"{\n"
" \"action\": \"delete\",\n"
" \"key\": 2\n"
"},{\n"
" \"action\": \"delete\",\n"
" \"key\": { \"key\": \"foo\", \"value\": \"bar\" }\n"
"}]\n";
ConstElementPtr actions;
ASSERT_NO_THROW(actions = Element::fromJSON(sactions));
string result = "[[{}]]\n";
ConstElementPtr expected;
ASSERT_NO_THROW(expected = Element::fromJSON(result));
ASSERT_NO_THROW(Adaptor::modify(path, actions, json));
EXPECT_TRUE(expected->equals(*json));
}
TEST(AdaptorTest, modifyListAllDelete) {
string config = "[[\n"
"{\n"
" \"foo\": \"bar\"\n"
"},{\n"
"},[\n"
"0, 1, 2, 3\n"
"]]]\n";
ElementPtr json;
ASSERT_NO_THROW(json = Element::fromJSON(config));
// The main change from the previous unit test is key 0 -> -1 so
// modify() applies the delete to all elements vs only the first one.
string spath = "[ -1 ]";
ConstElementPtr path;
ASSERT_NO_THROW(path = Element::fromJSON(spath));
// Put the positional first as it applies after previous actions...
string sactions = "[\n"
"{\n"
" \"action\": \"delete\",\n"
" \"key\": 2\n"
"},{\n"
" \"action\": \"delete\",\n"
" \"key\": { \"key\": \"foo\", \"value\": \"bar\" }\n"
"}]\n";
ConstElementPtr actions;
ASSERT_NO_THROW(actions = Element::fromJSON(sactions));
string result = "[[{}]]\n";
ConstElementPtr expected;
ASSERT_NO_THROW(expected = Element::fromJSON(result));
ASSERT_NO_THROW(Adaptor::modify(path, actions, json));
EXPECT_TRUE(expected->equals(*json));
}
}; // end of anonymous namespace

View File

@@ -0,0 +1,605 @@
module keatest-module {
yang-version 1.1;
namespace "urn:ietf:params:xml:ns:yang:keatest-module";
prefix tm;
import ietf-inet-types {
prefix inet;
}
organization "Sysrepo and ISC";
description
"ISC imported an example module from Sysrepo tests and adapted it
to kea testing regime.";
contact
"kea-dev@lists.isc.org";
container container {
config true;
list list {
leaf leaf {
type string;
}
leaf key1 {
type string;
}
leaf key2 {
type string;
}
key "key1 key2";
}
}
typedef threshold-power-dBm {
type union {
type decimal64 {
fraction-digits 2;
}
type enumeration {
enum off {
description "No threshold configured";
}
}
}
description "Power in dBm";
}
container main {
leaf enum {
type enumeration {
enum "yes" {
value 1;
}
enum "no" {
value 2;
}
enum "maybe" {
value 3;
}
}
}
leaf options {
type bits {
bit strict;
bit recursive;
bit logging;
}
}
leaf raw {
type binary;
}
leaf dec64 {
type decimal64{
fraction-digits 2;
}
}
leaf i8 {
type int8;
}
leaf i16 {
type int16;
}
leaf i32 {
type int32;
}
leaf i64 {
type int64;
}
leaf ui8 {
type uint8;
}
leaf ui16 {
type uint16;
}
leaf ui32 {
type uint32;
}
leaf ui64 {
type uint64;
}
leaf empty {
type empty;
}
leaf boolean {
type boolean;
}
leaf string {
type string;
}
leaf id_ref {
type identityref{
base base_id;
}
}
leaf-list numbers {
type uint8;
}
leaf instance_id {
type instance-identifier;
}
anyxml xml-data;
anydata any-data;
}
identity base_id;
identity id_1 {
base base_id;
}
identity id_2 {
base base_id;
}
list list {
key "key";
leaf key {
type string;
}
leaf id_ref {
type identityref{
base base_id;
}
}
leaf instance_id {
type instance-identifier;
}
leaf union {
type union{
type uint8;
type enumeration {
enum "infinity";
}
}
}
container wireless{
presence "wireless is enabled";
leaf vendor_name{
type string;
}
}
}
container transfer {
choice how {
default interval;
case interval {
leaf interval {
type uint16;
default 30;
units minutes;
}
}
case daily {
leaf daily {
type empty;
}
leaf time-of-day {
type string;
units 24-hour-clock;
default 1am;
}
}
case manual {
leaf manual {
type empty;
}
}
}
}
container location {
presence true;
leaf name {
type string;
}
leaf longitude {
type string;
mandatory true;
}
leaf latitude {
type string;
mandatory true;
}
}
leaf hexnumber{
type string {
length "0..4";
pattern "[0-9a-fA-F]*";
}
}
container interface {
leaf ifType {
type enumeration {
enum ethernet;
enum atm;
}
}
leaf ifMTU {
type uint32;
}
must "ifType != 'ethernet' or " +
"(ifType = 'ethernet' and ifMTU = 1500)" {
error-message "An ethernet MTU must be 1500";
}
must "ifType != 'atm' or " +
"(ifType = 'atm' and ifMTU <= 17966 and ifMTU >= 64)" {
error-message "An atm MTU must be 64 .. 17966";
}
}
list user {
description "This is a list of users in the system.";
ordered-by user;
config true;
key "name";
leaf name {
type string;
}
leaf type {
type string;
}
leaf full-name {
type string;
}
}
leaf-list ordered-numbers {
ordered-by user;
type uint8;
}
list with_def {
config true;
key "name";
leaf name {
type string;
}
leaf num {
type int8;
default 0;
}
}
rpc activate-software-image {
input {
leaf image-name {
type string;
must ". != /top-level-default";
}
leaf location {
type string;
default "/";
must ". != 'invalid location'";
}
}
output {
leaf status {
type string;
must ". != 'invalid status'";
}
leaf version {
type string;
}
leaf location {
type string;
default "/";
}
container init-log {
list log-msg {
key "msg time";
leaf msg {
type string;
}
leaf time {
type uint32;
}
leaf msg-type {
type enumeration {
enum "error" {
value 1;
}
enum "warning" {
value 2;
}
enum "debug" {
value 3;
}
}
}
}
}
}
}
leaf top-level-default {
type string;
default "default value";
}
container university {
container students {
list student {
ordered-by user;
config true;
key "name";
leaf name {
type string;
}
leaf age {
type uint8;
}
}
}
container classes {
list class {
config true;
key "title";
leaf title {
type string;
}
list student {
key "name";
leaf name {
type leafref {
path "../../../../students/student/name";
}
}
leaf age {
type leafref {
path "../../../../students/student[name = current()/../name]/age";
}
}
}
}
}
}
container leafref-chain {
leaf A {
type leafref {
path "../B";
}
}
leaf B {
type leafref {
path "../C";
}
}
leaf C {
type leafref {
path "../D";
}
}
leaf D {
type string;
}
}
grouping link {
container source {
leaf address {
type inet:ipv4-address;
}
leaf interface {
type string;
}
}
container destination {
leaf address {
type inet:ipv4-address;
}
leaf interface {
type string;
}
}
leaf MTU {
type uint16;
default 1500;
}
}
notification link-discovered {
uses link;
}
notification link-removed {
uses link;
}
container kernel-modules {
list kernel-module {
key "name";
leaf name {
type string;
}
leaf location {
type string;
default "/lib/modules";
}
leaf loaded {
type boolean;
}
action load {
input {
leaf params {
mandatory "true";
type string;
}
leaf force {
type boolean;
default "false";
when "../../loaded = 'false'";
}
leaf dry-run {
type boolean;
default false;
}
}
output {
leaf return-code {
type uint8;
}
}
}
action unload { }
action get-dependencies {
output {
leaf-list dependency {
type string;
}
leaf location {
type leafref {
path "/kernel-modules/kernel-module[name = current()/../../name]/location";
}
}
leaf location2 {
type leafref {
path "../../location";
}
}
}
}
}
}
typedef uniontpdf {
type union {
type string {
pattern "disabled";
}
type uint8 {
range "1..100";
}
}
default "disabled";
}
typedef enumtpdf {
type enumeration {
enum "a" {
value 1;
}
enum "b" {
value 2;
}
enum "c" {
value 3;
}
}
}
typedef multiplunions {
type union {
type uniontpdf;
type enumtpdf;
type union {
type boolean;
type decimal64 {
fraction-digits 2;
}
}
}
}
typedef leafreftpdf {
type leafref {
path "/list/key";
}
}
typedef inttpdf {
type uint8 {
range "1..100";
}
}
container tpdfs {
leaf unival {
type uniontpdf;
}
leaf leafrefval {
type leafreftpdf;
}
leaf intval {
type inttpdf;
}
leaf undecided {
type multiplunions;
}
}
leaf dec64-in-union {
type threshold-power-dBm;
}
container presence-container {
presence "presence-container";
leaf topleaf1 {
type int8;
}
leaf topleaf2 {
type int8;
}
container child1 {
leaf child1-leaf {
type int8;
}
container grandchild1 {
leaf grandchild1-leaf {
type int8;
default 10;
}
}
}
container child2 {
leaf child2-leaf {
type int8;
}
container grandchild2 {
leaf grandchild2-leaf1 {
type int8;
}
leaf grandchild2-leaf2 {
type int8;
}
leaf grandchild2-leaf3 {
type int8;
}
}
}
}
}

View File

@@ -0,0 +1,20 @@
// 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 <gtest/gtest.h>
#include <util/unittests/run_all.h>
#include <log/logger_support.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
isc::log::initLogger();
return (isc::util::unittests::run_all());
}

View File

@@ -0,0 +1,731 @@
// 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.h>
#include <boost/scoped_ptr.hpp>
#include <gtest/gtest.h>
using namespace std;
using namespace isc;
using namespace isc::data;
using namespace isc::yang;
namespace {
const std::string TEST_MODULE="keatest-module";
/// @brief checks if specified schema is installed and available in sysrepo
///
/// @name name of the schema to be checked (without .yang)
/// @verbose print installed schemas?
/// @return true if installed, false otherwise.
bool schemaInstalled(const std::string& name, bool verbose = false) {
// Get a connection.
S_Connection conn(new Connection("translator unittests"));
// Get a session.
S_Session sess(new Session(conn, SR_DS_CANDIDATE));
S_Yang_Schemas schemas = sess->list_schemas();
size_t schema_cnt = schemas->schema_cnt();
if (verbose) {
cout << "There are " << schema_cnt << " YANG schema(s) installed:" << endl;
}
bool found = false;
for (int i = 0; i < schema_cnt; i++) {
string installed_name(schemas->schema(i)->module_name());
if (installed_name == name) {
found = true;
}
if (verbose) {
std::cout << "Schema " << i << ": " << installed_name << endl;
}
}
return (found);
}
// This test verifies if the test schema is installed and accessible.
TEST(TranslatorBasicTest, environmentCheck1) {
EXPECT_TRUE(schemaInstalled(TEST_MODULE))
<< "\nERROR: Module used in unit-tests " << TEST_MODULE
<< " is not installed. The environment is not suitable for\n"
<< "ERROR: running unit-tests. Please locate " << TEST_MODULE <<".yang "
<< "and issue the following command:\n"
<< "ERROR: sysrepoctl --install --yang=" << TEST_MODULE << ".yang\n"
<< "ERROR:\n"
<< "ERROR: Following tests will most likely fail.\n";
}
// Test constructor.
TEST(TranslatorBasicTest, constructor) {
// Get a connection.
S_Connection conn(new Connection("translator unittests"));
// Get a session.
S_Session sess(new Session(conn, SR_DS_CANDIDATE));
// Get a translator object.
boost::scoped_ptr<TranslatorBasic> t_obj;
EXPECT_NO_THROW(t_obj.reset(new TranslatorBasic(sess)));
}
// Test basic yang value to JSON using the static method.
TEST(TranslatorBasicTest, valueFrom) {
S_Val s_val;
ConstElementPtr elem;
// Null.
EXPECT_THROW(TranslatorBasic::value(s_val), BadValue);
// No easy and direct way to build a container or a list...
// String.
string str("foo");
s_val.reset(new Val(str.c_str(), SR_STRING_T));
EXPECT_NO_THROW(elem = TranslatorBasic::value(s_val));
ASSERT_TRUE(elem);
ASSERT_EQ(Element::string, elem->getType());
EXPECT_EQ(str, elem->stringValue());
elem.reset();
// Bool.
s_val.reset(new Val(false, SR_BOOL_T));
EXPECT_NO_THROW(elem = TranslatorBasic::value(s_val));
ASSERT_TRUE(elem);
ASSERT_EQ(Element::boolean, elem->getType());
EXPECT_FALSE(elem->boolValue());
elem.reset();
// Unsigned 8 bit integer.
uint8_t u8(123);
s_val.reset(new Val(u8, SR_UINT8_T));
EXPECT_NO_THROW(elem = TranslatorBasic::value(s_val));
ASSERT_TRUE(elem);
ASSERT_EQ(Element::integer, elem->getType());
EXPECT_EQ(u8, elem->intValue());
elem.reset();
// Unsigned 16 bit integer.
uint16_t u16(12345);
s_val.reset(new Val(u16, SR_UINT16_T));
EXPECT_NO_THROW(elem = TranslatorBasic::value(s_val));
ASSERT_TRUE(elem);
ASSERT_EQ(Element::integer, elem->getType());
EXPECT_EQ(u16, elem->intValue());
elem.reset();
// Unsigned 32 bit integer.
uint32_t u32(123456789);
s_val.reset(new Val(u32, SR_UINT32_T));
EXPECT_NO_THROW(elem = TranslatorBasic::value(s_val));
ASSERT_TRUE(elem);
ASSERT_EQ(Element::integer, elem->getType());
EXPECT_EQ(u32, elem->intValue());
elem.reset();
// Signed 8 bit integer.
int8_t s8(-123);
s_val.reset(new Val(s8, SR_INT8_T));
EXPECT_NO_THROW(elem = TranslatorBasic::value(s_val));
ASSERT_TRUE(elem);
ASSERT_EQ(Element::integer, elem->getType());
EXPECT_EQ(s8, elem->intValue());
elem.reset();
// Signed 16 bit integer.
int16_t s16(-12345);
s_val.reset(new Val(s16, SR_INT16_T));
EXPECT_NO_THROW(elem = TranslatorBasic::value(s_val));
ASSERT_TRUE(elem);
ASSERT_EQ(Element::integer, elem->getType());
EXPECT_EQ(s16, elem->intValue());
elem.reset();
// Signed 32 bit integer.
int32_t s32(-123456789);
s_val.reset(new Val(s32, SR_INT32_T));
EXPECT_NO_THROW(elem = TranslatorBasic::value(s_val));
ASSERT_TRUE(elem);
ASSERT_EQ(Element::integer, elem->getType());
EXPECT_EQ(s32, elem->intValue());
elem.reset();
// Identity reference.
s_val.reset(new Val(str.c_str(), SR_IDENTITYREF_T));
EXPECT_NO_THROW(elem = TranslatorBasic::value(s_val));
ASSERT_TRUE(elem);
ASSERT_EQ(Element::string, elem->getType());
EXPECT_EQ(str, elem->stringValue());
elem.reset();
// Enumeration item.
s_val.reset(new Val(str.c_str(), SR_ENUM_T));
EXPECT_NO_THROW(elem = TranslatorBasic::value(s_val));
ASSERT_TRUE(elem);
ASSERT_EQ(Element::string, elem->getType());
EXPECT_EQ(str, elem->stringValue());
elem.reset();
// Binary.
string binary("Zm9vYmFy");
s_val.reset(new Val(binary.c_str(), SR_BINARY_T));
EXPECT_NO_THROW(elem = TranslatorBasic::value(s_val));
ASSERT_TRUE(elem);
ASSERT_EQ(Element::string, elem->getType());
EXPECT_EQ("foobar", elem->stringValue());
elem.reset();
// Unknown / unsupported.
double d64(.1234);
s_val.reset(new Val(d64));
EXPECT_THROW(TranslatorBasic::value(s_val), NotImplemented);
}
// Test basic yang value to JSON using sysrepo test models.
TEST(TranslatorBasicTest, getItem) {
// Get a translator object to play with.
S_Connection conn(new Connection("translator unittests"));
S_Session sess(new Session(conn, SR_DS_CANDIDATE));
boost::scoped_ptr<TranslatorBasic> t_obj;
ASSERT_NO_THROW(t_obj.reset(new TranslatorBasic(sess)));
// Container.
string xpath = "/keatest-module:container/list";
S_Val s_val;
EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
ConstElementPtr elem;
EXPECT_NO_THROW(elem = t_obj->getItem("/keatest-module:container"));
EXPECT_FALSE(elem);
elem.reset();
// List.
EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
ASSERT_TRUE(elem);
ASSERT_EQ(Element::list, elem->getType());
EXPECT_EQ(0, elem->size());
elem.reset();
// String.
xpath = "/keatest-module:main/string";
s_val.reset(new Val("str", SR_STRING_T));
EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
ASSERT_TRUE(elem);
ASSERT_EQ(Element::string, elem->getType());
EXPECT_EQ("str", elem->stringValue());
elem.reset();
// Bool.
xpath = "/keatest-module:main/boolean";
s_val.reset(new Val(true, SR_BOOL_T));
EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
ASSERT_TRUE(elem);
ASSERT_EQ(Element::boolean, elem->getType());
EXPECT_TRUE(elem->boolValue());
elem.reset();
// Unsigned 8 bit integer.
xpath = "/keatest-module:main/ui8";
uint8_t u8(8);
s_val.reset(new Val(u8, SR_UINT8_T));
EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
ASSERT_TRUE(elem);
ASSERT_EQ(Element::integer, elem->getType());
EXPECT_EQ(8, elem->intValue());
elem.reset();
// Unsigned 16 bit integer.
xpath = "/keatest-module:main/ui16";
uint16_t u16(16);
s_val.reset(new Val(u16, SR_UINT16_T));
EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
ASSERT_TRUE(elem);
ASSERT_EQ(Element::integer, elem->getType());
EXPECT_EQ(16, elem->intValue());
elem.reset();
// Unsigned 32 bit integer.
xpath = "/keatest-module:main/ui32";
uint32_t u32(32);
s_val.reset(new Val(u32, SR_UINT32_T));
EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
ASSERT_TRUE(elem);
ASSERT_EQ(Element::integer, elem->getType());
EXPECT_EQ(32, elem->intValue());
elem.reset();
// Signed 8 bit integer.
xpath = "/keatest-module:main/i8";
int8_t s8(8);
s_val.reset(new Val(s8, SR_INT8_T));
EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
ASSERT_TRUE(elem);
ASSERT_EQ(Element::integer, elem->getType());
EXPECT_EQ(8, elem->intValue());
elem.reset();
// Signed 16 bit integer.
xpath = "/keatest-module:main/i16";
int16_t s16(16);
s_val.reset(new Val(s16, SR_INT16_T));
EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
ASSERT_TRUE(elem);
ASSERT_EQ(Element::integer, elem->getType());
EXPECT_EQ(16, elem->intValue());
elem.reset();
// Signed 32 bit integer.
xpath = "/keatest-module:main/i32";
int32_t s32(32);
s_val.reset(new Val(s32, SR_INT32_T));
EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
ASSERT_TRUE(elem);
ASSERT_EQ(Element::integer, elem->getType());
EXPECT_EQ(32, elem->intValue());
elem.reset();
// Identity reference.
xpath = "/keatest-module:main/id_ref";
s_val.reset(new Val("keatest-module:id_1", SR_IDENTITYREF_T));
EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
ASSERT_TRUE(elem);
ASSERT_EQ(Element::string, elem->getType());
EXPECT_EQ("keatest-module:id_1", elem->stringValue());
elem.reset();
// Enumeration item.
xpath = "/keatest-module:main/enum";
s_val.reset(new Val("maybe", SR_ENUM_T));
EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
ASSERT_TRUE(elem);
ASSERT_EQ(Element::string, elem->getType());
EXPECT_EQ("maybe", elem->stringValue());
elem.reset();
// Binary.
xpath = "/keatest-module:main/raw";
s_val.reset(new Val("Zm9vYmFy", SR_BINARY_T));
EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
ASSERT_TRUE(elem);
ASSERT_EQ(Element::string, elem->getType());
EXPECT_EQ("foobar", elem->stringValue());
elem.reset();
// Leaf-list: not yet exist.
xpath = "/keatest-module:main/numbers";
EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
EXPECT_FALSE(elem);
elem.reset();
// No easy way to create it empty.
// Leaf-list: 1, 2 and 3.
u8 = 1;
s_val.reset(new Val(u8, SR_UINT8_T));
EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
u8 = 2;
s_val.reset(new Val(u8, SR_UINT8_T));
EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
u8 = 3;
s_val.reset(new Val(u8, SR_UINT8_T));
EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
EXPECT_NO_THROW(elem = t_obj->getItems(xpath));
ASSERT_TRUE(elem);
ASSERT_EQ(Element::list, elem->getType());
EXPECT_EQ(3, elem->size());
EXPECT_EQ("[ 1, 2, 3 ]", elem->str());
elem.reset();
// Unknown / unsupported.
xpath = "/keatest-module:main/dec64";
s_val.reset(new Val(9.85));
EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
EXPECT_THROW(elem = t_obj->getItem(xpath), NotImplemented);
elem.reset();
// Not found.
xpath = "/keatest-module:main/no_such_string";
EXPECT_NO_THROW(sess->delete_item(xpath.c_str()));
EXPECT_NO_THROW(elem = t_obj->getItem(xpath));
EXPECT_FALSE(elem);
elem.reset();
// Check error.
xpath = "null";
try {
elem = t_obj->getItem(xpath);
ADD_FAILURE() << "expected exception";
} catch (const SysrepoError& ex) {
EXPECT_EQ("sysrepo error getting item at 'null': Invalid argument",
string(ex.what()));
} catch (const std::exception& ex) {
ADD_FAILURE() << "unexpected exception with: " << ex.what();
}
}
// Test JSON to basic yang value using the static method.
TEST(TranslatorBasicTest, valueTo) {
// Null.
ConstElementPtr elem;
EXPECT_THROW(TranslatorBasic::value(elem, SR_STRING_T), BadValue);
// Container.
elem = Element::createMap();
EXPECT_THROW(TranslatorBasic::value(elem, SR_CONTAINER_T), NotImplemented);
EXPECT_THROW(TranslatorBasic::value(elem, SR_CONTAINER_PRESENCE_T), NotImplemented);
// List.
elem = Element::createList();
S_Val s_val;
EXPECT_NO_THROW(s_val = TranslatorBasic::value(elem, SR_LIST_T));
EXPECT_FALSE(s_val);
s_val.reset();
// String.
string str("foo");
elem = Element::create(str);
EXPECT_NO_THROW(s_val = TranslatorBasic::value(elem, SR_STRING_T));
ASSERT_TRUE(s_val);
ASSERT_EQ(SR_STRING_T, s_val->type());
EXPECT_EQ(str, string(s_val->data()->get_string()));
s_val.reset();
// Bool.
elem = Element::create(false);
EXPECT_NO_THROW(s_val = TranslatorBasic::value(elem, SR_BOOL_T));
ASSERT_TRUE(s_val);
ASSERT_EQ(SR_BOOL_T, s_val->type());
EXPECT_FALSE(s_val->data()->get_bool());
s_val.reset();
// Unsigned 8 bit integer.
elem = Element::create(123);
EXPECT_NO_THROW(s_val = TranslatorBasic::value(elem, SR_UINT8_T));
ASSERT_TRUE(s_val);
ASSERT_EQ(SR_UINT8_T, s_val->type());
EXPECT_EQ(123, s_val->data()->get_uint8());
elem.reset();
// Unsigned 16 bit integer.
elem = Element::create(12345);
EXPECT_NO_THROW(s_val = TranslatorBasic::value(elem, SR_UINT16_T));
ASSERT_TRUE(s_val);
ASSERT_EQ(SR_UINT16_T, s_val->type());
EXPECT_EQ(12345, s_val->data()->get_uint16());
elem.reset();
// Unsigned 32 bit integer.
elem = Element::create(123456789);
EXPECT_NO_THROW(s_val = TranslatorBasic::value(elem, SR_UINT32_T));
ASSERT_TRUE(s_val);
ASSERT_EQ(SR_UINT32_T, s_val->type());
EXPECT_EQ(123456789, s_val->data()->get_uint32());
elem.reset();
// Signed 8 bit integer.
elem = Element::create(-123);
EXPECT_NO_THROW(s_val = TranslatorBasic::value(elem, SR_INT8_T));
ASSERT_TRUE(s_val);
ASSERT_EQ(SR_INT8_T, s_val->type());
EXPECT_EQ(-123, s_val->data()->get_int8());
elem.reset();
// Signed 16 bit integer.
elem = Element::create(-12345);
EXPECT_NO_THROW(s_val = TranslatorBasic::value(elem, SR_INT16_T));
ASSERT_TRUE(s_val);
ASSERT_EQ(SR_INT16_T, s_val->type());
EXPECT_EQ(-12345, s_val->data()->get_int16());
elem.reset();
// Signed 32 bit integer.
elem = Element::create(-123456789);
EXPECT_NO_THROW(s_val = TranslatorBasic::value(elem, SR_INT32_T));
ASSERT_TRUE(s_val);
ASSERT_EQ(SR_INT32_T, s_val->type());
EXPECT_EQ(-123456789, s_val->data()->get_int32());
elem.reset();
// Identity reference.
elem = Element::create(str);
EXPECT_NO_THROW(s_val = TranslatorBasic::value(elem, SR_IDENTITYREF_T));
ASSERT_TRUE(s_val);
ASSERT_EQ(SR_IDENTITYREF_T, s_val->type());
EXPECT_EQ(str, string(s_val->data()->get_identityref()));
s_val.reset();
// Enumeration item.
EXPECT_NO_THROW(s_val = TranslatorBasic::value(elem, SR_ENUM_T));
ASSERT_TRUE(s_val);
EXPECT_EQ(str, string(s_val->data()->get_enum()));
s_val.reset();
// Binary.
elem = Element::create(string("foobar"));
EXPECT_NO_THROW(s_val = TranslatorBasic::value(elem, SR_BINARY_T));
ASSERT_TRUE(s_val);
EXPECT_EQ("Zm9vYmFy", string(s_val->data()->get_binary()));
s_val.reset();
// Unknown / unsupported.
elem = Element::create(.1234);
EXPECT_THROW(TranslatorBasic::value(elem, SR_DECIMAL64_T), NotImplemented);
}
// Test JSON to basic yang value using sysrepo test models.
TEST(TranslatorBasicTest, setItem) {
// Get a translator object to play with.
S_Connection conn(new Connection("translator unittests"));
S_Session sess(new Session(conn, SR_DS_CANDIDATE));
boost::scoped_ptr<TranslatorBasic> t_obj;
ASSERT_NO_THROW(t_obj.reset(new TranslatorBasic(sess)));
// Container.
string xpath = "/keatest-module:container";
ConstElementPtr elem = Element::createMap();
EXPECT_THROW(t_obj->setItem(xpath, elem, SR_CONTAINER_T), NotImplemented);
EXPECT_THROW(t_obj->setItem(xpath, elem, SR_CONTAINER_PRESENCE_T),
NotImplemented);
// List.
xpath = "/keatest-module:container/list";
elem = Element::createList();
EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_LIST_T));
S_Val s_val;
EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
ASSERT_TRUE(s_val);
ASSERT_EQ(SR_LIST_T, s_val->type());
s_val.reset();
// String.
xpath = "/keatest-module:main/string";
elem = Element::create(string("str"));
EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_STRING_T));
EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
ASSERT_TRUE(s_val);
ASSERT_EQ(SR_STRING_T, s_val->type());
EXPECT_EQ("str", string(s_val->data()->get_string()));
s_val.reset();
// Bool.
xpath = "/keatest-module:main/boolean";
elem = Element::create(true);
EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_BOOL_T));
EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
ASSERT_TRUE(s_val);
ASSERT_EQ(SR_BOOL_T, s_val->type());
EXPECT_TRUE(s_val->data()->get_bool());
s_val.reset();
// Unsigned 8 bit integer.
xpath = "/keatest-module:main/ui8";
elem = Element::create(8);
EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_UINT8_T));
EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
ASSERT_TRUE(s_val);
ASSERT_EQ(SR_UINT8_T, s_val->type());
EXPECT_EQ(8, s_val->data()->get_uint8());
s_val.reset();
// Unsigned 16 bit integer.
xpath = "/keatest-module:main/ui16";
elem = Element::create(16);
EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_UINT16_T));
EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
ASSERT_TRUE(s_val);
ASSERT_EQ(SR_UINT16_T, s_val->type());
EXPECT_EQ(16, s_val->data()->get_uint16());
s_val.reset();
// Unsigned 32 bit integer.
xpath = "/keatest-module:main/ui32";
elem = Element::create(32);
EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_UINT32_T));
EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
ASSERT_TRUE(s_val);
ASSERT_EQ(SR_UINT32_T, s_val->type());
EXPECT_EQ(32, s_val->data()->get_uint32());
s_val.reset();
// Signed 8 bit integer.
xpath = "/keatest-module:main/i8";
elem = Element::create(8);
EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_INT8_T));
EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
ASSERT_TRUE(s_val);
ASSERT_EQ(SR_INT8_T, s_val->type());
EXPECT_EQ(8, s_val->data()->get_int8());
s_val.reset();
// Signed 16 bit integer.
xpath = "/keatest-module:main/i16";
elem = Element::create(16);
EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_INT16_T));
EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
ASSERT_TRUE(s_val);
ASSERT_EQ(SR_INT16_T, s_val->type());
EXPECT_EQ(16, s_val->data()->get_int16());
s_val.reset();
// Signed 32 bit integer.
xpath = "/keatest-module:main/i32";
elem = Element::create(32);
EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_INT32_T));
EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
ASSERT_TRUE(s_val);
ASSERT_EQ(SR_INT32_T, s_val->type());
EXPECT_EQ(32, s_val->data()->get_int32());
s_val.reset();
// Identity reference.
xpath = "/keatest-module:main/id_ref";
elem = Element::create(string("keatest-module:id_1"));
EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_IDENTITYREF_T));
EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
ASSERT_TRUE(s_val);
ASSERT_EQ(SR_IDENTITYREF_T, s_val->type());
EXPECT_EQ("keatest-module:id_1", string(s_val->data()->get_identityref()));
s_val.reset();
// Enumeration item.
xpath = "/keatest-module:main/enum";
elem = Element::create(string("maybe"));
EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_ENUM_T));
EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
ASSERT_TRUE(s_val);
ASSERT_EQ(SR_ENUM_T, s_val->type());
EXPECT_EQ("maybe", string(s_val->data()->get_enum()));
s_val.reset();
// Binary.
xpath = "/keatest-module:main/raw";
elem = Element::create(string("foobar"));
EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_BINARY_T));
EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
ASSERT_TRUE(s_val);
ASSERT_EQ(SR_BINARY_T, s_val->type());
EXPECT_EQ("Zm9vYmFy", string(s_val->data()->get_binary()));
s_val.reset();
// Leaf-list.
xpath = "/keatest-module:main/numbers";
S_Vals s_vals;
EXPECT_NO_THROW(s_vals = sess->get_items(xpath.c_str()));
EXPECT_FALSE(s_vals);
s_vals.reset();
// Fill it.
elem = Element::create(1);
EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_UINT8_T));
elem = Element::create(2);
EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_UINT8_T));
elem = Element::create(3);
EXPECT_NO_THROW(t_obj->setItem(xpath, elem, SR_UINT8_T));
EXPECT_NO_THROW(s_vals = sess->get_items(xpath.c_str()));
ASSERT_TRUE(s_vals);
EXPECT_EQ(3, s_vals->val_cnt());
s_vals.reset();
// Clean it.
EXPECT_NO_THROW(t_obj->delItem(xpath));
EXPECT_NO_THROW(s_vals = sess->get_items(xpath.c_str()));
EXPECT_FALSE(s_vals);
s_vals.reset();
// Unknown / unsupported.
xpath = "/keatest-module:main/dec64";
elem = Element::create(9.85);
EXPECT_THROW(t_obj->setItem(xpath, elem, SR_DECIMAL64_T), NotImplemented);
// Bad xpath.
xpath = "/keatest-module:main/no_such_string";
elem = Element::create(string("str"));
try {
t_obj->setItem(xpath, elem, SR_STRING_T);
ADD_FAILURE() << "expected exception";
} catch (const SysrepoError& ex) {
string expected = "sysrepo error setting item '\"str\"' at '" +
xpath + "': Request contains unknown element";
EXPECT_EQ(expected, string(ex.what()));
} catch (const std::exception& ex) {
ADD_FAILURE() << "unexpected exception with: " << ex.what();
}
// Bad type.
xpath = "/keatest-module:main/string";
elem = Element::create(true);
try {
t_obj->setItem(xpath, elem, SR_BOOL_T);
ADD_FAILURE() << "expected exception";
} catch (const SysrepoError& ex) {
string expected = "sysrepo error setting item 'true' at '" +
xpath + "': Invalid argument";
EXPECT_EQ(expected, string(ex.what()));
} catch (const std::exception& ex) {
ADD_FAILURE() << "unexpected exception with: " << ex.what();
}
// Delete (twice).
xpath = "/keatest-module:main/string";
EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
EXPECT_TRUE(s_val);
s_val.reset();
EXPECT_NO_THROW(t_obj->delItem(xpath));
EXPECT_NO_THROW(s_val = sess->get_item(xpath.c_str()));
EXPECT_FALSE(s_val);
EXPECT_NO_THROW(t_obj->delItem(xpath));
}
// Test yang list iteration.
TEST(TranslatorBasicTest, list) {
// Get a translator object to play with.
S_Connection conn(new Connection("translator unittests"));
S_Session sess(new Session(conn, SR_DS_CANDIDATE));
boost::scoped_ptr<TranslatorBasic> t_obj;
ASSERT_NO_THROW(t_obj.reset(new TranslatorBasic(sess)));
// Empty list.
S_Iter_Value iter;
EXPECT_NO_THROW(iter = t_obj->getIter("/keatest-module:container/list"));
ASSERT_TRUE(iter);
string xpath;
EXPECT_NO_THROW(xpath = t_obj->getNext(iter));
EXPECT_TRUE(xpath.empty());
// Retried with a filled list.
xpath = "/keatest-module:container/list[key1='key1'][key2='key2']/leaf";
S_Val s_val(new Val("Leaf value"));
EXPECT_NO_THROW(sess->set_item(xpath.c_str(), s_val));
EXPECT_NO_THROW(iter = t_obj->getIter("/keatest-module:container/list"));
ASSERT_TRUE(iter);
EXPECT_NO_THROW(xpath = t_obj->getNext(iter));
EXPECT_EQ("/keatest-module:container/list[key1='key1'][key2='key2']", xpath);
EXPECT_NO_THROW(xpath = t_obj->getNext(iter));
EXPECT_TRUE(xpath.empty());
// Not found: same than empty because sr_get_items_iter() translates
// SR_ERR_NOT_FOUND by SR_ERR_OK...
}
}; // end of anonymous namespace

295
src/lib/yang/translator.cc Normal file
View File

@@ -0,0 +1,295 @@
// 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/translator.h>
#include <util/encode/base64.h>
#include <cstring>
using namespace std;
using namespace isc::data;
using namespace isc::util::encode;
namespace {
string encode64(const string& input) {
vector<uint8_t> binary;
binary.resize(input.size());
memmove(&binary[0], input.c_str(), binary.size());
return (encodeBase64(binary));
}
string decode64(const string& input) {
vector<uint8_t> binary;
decodeBase64(input, binary);
string result;
result.resize(binary.size());
memmove(&result[0], &binary[0], result.size());
return (result);
}
} // end of anonymous namespace
namespace isc {
namespace yang {
TranslatorBasic::TranslatorBasic(S_Session session) : session_(session) {
}
TranslatorBasic::~TranslatorBasic() {
}
ElementPtr
TranslatorBasic::value(S_Val s_val) {
if (!s_val) {
isc_throw(BadValue, "value called with null");
}
switch (s_val->type()) {
case SR_CONTAINER_T:
case SR_CONTAINER_PRESENCE_T:
// Internal node.
return (ElementPtr());
case SR_LIST_T:
return (Element::createList());
case SR_STRING_T:
return (Element::create(string(s_val->data()->get_string())));
case SR_BOOL_T:
return (Element::create(s_val->data()->get_bool() ? true : false));
case SR_UINT8_T:
return (Element::create(static_cast<long long>(s_val->data()->get_uint8())));
case SR_UINT16_T:
return (Element::create(static_cast<long long>(s_val->data()->get_uint16())));
case SR_UINT32_T:
return (Element::create(static_cast<long long>(s_val->data()->get_uint32())));
case SR_INT8_T:
return (Element::create(static_cast<long long>(s_val->data()->get_int8())));
case SR_INT16_T:
return (Element::create(static_cast<long long>(s_val->data()->get_int16())));
case SR_INT32_T:
return (Element::create(static_cast<long long>(s_val->data()->get_int32())));
case SR_IDENTITYREF_T:
return (Element::create(string(s_val->data()->get_identityref())));
case SR_ENUM_T:
return (Element::create(string(s_val->data()->get_enum())));
case SR_BINARY_T:
return (Element::create(decode64(s_val->data()->get_binary())));
default:
isc_throw(NotImplemented,
"value called with unupported type: " << s_val->type());
}
}
ElementPtr
TranslatorBasic::getItem(const string& xpath) {
S_Val s_val;
try {
s_val = session_->get_item(xpath.c_str());
} catch (const sysrepo_exception& ex) {
isc_throw(SysrepoError, "sysrepo error getting item at '" << xpath
<< "': " << ex.what());
}
if (!s_val) {
return (ElementPtr());
}
return (value(s_val));
}
ElementPtr
TranslatorBasic::getItems(const string& xpath) {
S_Vals s_vals;
try {
s_vals = session_->get_items(xpath.c_str());
if (!s_vals) {
return (ElementPtr());
}
ElementPtr result = Element::createList();
for (size_t i = 0; i < s_vals->val_cnt(); ++i) {
S_Val s_val = s_vals->val(i);
result->add(value(s_val));
}
return (result);
} catch (const sysrepo_exception& ex) {
isc_throw(SysrepoError,
"sysrepo error getting item at '" << xpath
<< "': " << ex.what());
}
}
S_Val
TranslatorBasic::value(ConstElementPtr elem, sr_type_t type) {
if (!elem) {
isc_throw(BadValue, "value called with null");
}
S_Val s_val;
switch (type) {
case SR_CONTAINER_T:
case SR_CONTAINER_PRESENCE_T:
isc_throw(NotImplemented, "value called for a container");
case SR_LIST_T:
if (elem->getType() != Element::list) {
isc_throw(BadValue, "value for a list called with not a list: "
<< elem->str());
}
// Return null.
break;
case SR_STRING_T:
case SR_IDENTITYREF_T:
case SR_ENUM_T:
if (elem->getType() != Element::string) {
isc_throw(BadValue,
"value for a string called with not a string: "
<< elem->str());
}
s_val.reset(new Val(elem->stringValue().c_str(), type));
break;
case SR_BOOL_T:
if (elem->getType() != Element::boolean) {
isc_throw(BadValue,
"value for a boolean called with not a boolean: "
<< elem->str());
}
s_val.reset(new Val(elem->boolValue(), type));
break;
case SR_UINT8_T:
if (elem->getType() != Element::integer) {
isc_throw(BadValue,
"value for an integer called with not an integer: "
<< elem->str());
}
s_val.reset(new Val(static_cast<uint8_t>(elem->intValue()), type));
break;
case SR_UINT16_T:
if (elem->getType() != Element::integer) {
isc_throw(BadValue,
"value for an integer called with not an integer: "
<< elem->str());
}
s_val.reset(new Val(static_cast<uint16_t>(elem->intValue()), type));
break;
case SR_UINT32_T:
if (elem->getType() != Element::integer) {
isc_throw(BadValue,
"value for an integer called with not an integer: "
<< elem->str());
}
s_val.reset(new Val(static_cast<uint32_t>(elem->intValue()), type));
break;
case SR_INT8_T:
if (elem->getType() != Element::integer) {
isc_throw(BadValue,
"value for an integer called with not an integer: "
<< elem->str());
}
s_val.reset(new Val(static_cast<int8_t>(elem->intValue()), type));
break;
case SR_INT16_T:
if (elem->getType() != Element::integer) {
isc_throw(BadValue,
"value for an integer called with not an integer: "
<< elem->str());
}
s_val.reset(new Val(static_cast<int16_t>(elem->intValue()), type));
break;
case SR_INT32_T:
if (elem->getType() != Element::integer) {
isc_throw(BadValue,
"value for an integer called with not an integer: "
<< elem->str());
}
s_val.reset(new Val(static_cast<int32_t>(elem->intValue()), type));
break;
case SR_BINARY_T:
if (elem->getType() != Element::string) {
isc_throw(BadValue,
"value for a base64 called with not a string: "
<< elem->str());
}
s_val.reset(new Val(encode64(elem->stringValue()).c_str(), type));
break;
default:
isc_throw(NotImplemented,
"value called with unupported type: " << type);
}
return (s_val);
}
void
TranslatorBasic::setItem(const string& xpath, ConstElementPtr elem,
sr_type_t type) {
S_Val s_val = value(elem, type);
if (!s_val && (type != SR_LIST_T)) {
return;
}
try {
session_->set_item(xpath.c_str(), s_val);
} catch (const sysrepo_exception& ex) {
isc_throw(SysrepoError,
"sysrepo error setting item '" << elem->str()
<< "' at '" << xpath << "': " << ex.what());
}
}
void
TranslatorBasic::delItem(const std::string& xpath) {
try {
session_->delete_item(xpath.c_str());
} catch (const sysrepo_exception& ex) {
isc_throw(SysrepoError,
"sysrepo error deleting item at '"
<< xpath << "': " << ex.what());
}
}
S_Iter_Value
TranslatorBasic::getIter(const std::string& xpath) {
return (session_->get_items_iter(xpath.c_str()));
}
string
TranslatorBasic::getNext(S_Iter_Value iter) {
if (!iter) {
isc_throw(BadValue, "getNext called with null");
}
S_Val s_val;
try {
s_val = session_->get_item_next(iter);
} catch (const sysrepo_exception&) {
// Should not happen according to the doc but still happen?
return ("");
}
if (!s_val) {
return ("");
}
return (s_val->xpath());
}
}; // end of namespace isc::yang
}; // end of namespace isc

100
src/lib/yang/translator.h Normal file
View File

@@ -0,0 +1,100 @@
// 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_H
#define ISC_TRANSLATOR_H 1
#include <cc/data.h>
#include <yang/sysrepo_error.h>
#include <sysrepo-cpp/Session.h>
namespace isc {
namespace yang {
/// @brief Between Yang and JSON translator class for basic values.
class TranslatorBasic {
public:
/// @brief Constructor.
///
/// @param session Sysrepo session.
TranslatorBasic(S_Session session);
/// @brief Destructor.
virtual ~TranslatorBasic();
/// @brief Translate basic value from Yang to JSON.
///
/// @note Please don't use this outside tests.
///
/// @param s_val The value.
/// @return The Element representing the sysrepo value.
/// @throw NotImplemented when the value type is not supported.
static isc::data::ElementPtr value(S_Val s_val);
/// @brief Get and translate basic value from Yang to JSON.
///
/// @note Should be const as it is read only...
///
/// @param xpath The xpath of the basic value.
/// @return The Element representing the item at xpath or null
/// when not found.
/// @throw SysrepoError when sysrepo raises an error.
/// @throw NotImplemented when the value type is not supported.
isc::data::ElementPtr getItem(const std::string& xpath);
/// @brief Get and translate a list of basic values from Yang to JSON.
///
/// @param xpath The xpath of the list of basic values.
/// @return The ListElement representing the leaf-list at xpath or
/// null when not found.
isc::data::ElementPtr getItems(const std::string& xpath);
/// @brief Translate basic value from JSON to Yang.
///
/// @note Please don't use this outside tests.
///
/// @param elem The JSON element.
/// @param type The sysrepo type.
static S_Val value(isc::data::ConstElementPtr elem, sr_type_t type);
/// @brief Translate and set basic value from JSON to Yang.
///
/// @param xpath The xpath of the basic value.
/// @param elem The JSON element.
/// @param type The sysrepo type.
void setItem(const std::string& xpath, isc::data::ConstElementPtr elem,
sr_type_t type);
/// @brief Delete basic value from Yang.
///
/// @param xpath The xpath of the basic value.
void delItem(const std::string& xpath);
/// List iterator methods keeping the session private.
/// @brief Get iterator over a Yang list.
///
/// @param xpath The xpath of the list.
/// @return An S_Iter_Value pointer. Null is the list does not exist.
S_Iter_Value getIter(const std::string& xpath);
/// @brief Get xpath of the next Yang list item.
///
/// @param iter The iterator pointing to the previous element
/// @return The xpath of the next element. Empty string when at the end of the list.
std::string getNext(S_Iter_Value iter);
protected:
/// @brief The sysrepo session.
S_Session session_;
};
}; // end of namespace isc::yang
}; // end of namespace isc
#endif // ISC_TRANSLATOR_H

102
src/lib/yang/yang.dox Normal file
View File

@@ -0,0 +1,102 @@
// 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/.
/**
@page libyang libkea-yang - Kea Yang Utilities Library
The libkea-yang library was developed to handle base YANG operations,
such are retrieving YANG schema and configuration and translating
data between YANG syntax and JSON that is understandable by Kea.
@section yangTranslator Translator between Yang and JSON
An essential concept is the idea of translator. It is a primitive that is able
to convert certain data structure between YANG and JSON. It is envisaged
that more complex translators will use other translators to handle more
complex data structures. For details, see @ref isc::yang::TranslatorBasic.
Note that although the initial focus is on translation from YANG to JSON (so
Kea can retrieve its configuration), the opposite translation direction -
from JSON to YANG - is also very useful, for at least two reasons. First,
in many cases we can use it in tests to check that conversion back and forth
doesn't lose anything: yang = toYang(toJson(yang)). Second, YANG models
cover two major types of data: configuration and run-time state. While
we're initially focusing on getting the configuration, the run-time state
is something that Kea is expected to provide. Kea uses JSON internally in many
places and that data will have to be exported in YANG format.
All translators take a Session pointer (a structure provided by Sysrepo that
is responsible for maintaining a connection) in constructors and derive from
the basic / base class and recursively of translators for embedded parts.
@c isc::yang::TranslatorBasic provides some methods:
- getItem() gets and translates basic value from Yang to JSON
- getItems() gets and translates a leaf-list from Yang to JSON
(for a list please use an iterator)
- setItem() translates and sets a basic value from JSON to Yang
- delItem() deletes a value
- getIter() gets an iterator over a Yang list
- getNext() returns the xpath of the next item
@section yangTranslatorPool Pool translator
@c isc::yang::TranslatorPool is the standard example of a translator
for a structured value. Its constructor takes a model name: the code
implements some variants to accommodate the model with shared code
moved into a common private routine.
@c isc::yang::TranslatorPools deals with a list of pools. The getPools
method iterates over the list in both ways. Look at examples in unit
tests to understand how can be filled.
Note pools show two shortcomings in IETF models:
- option sets make to track changes nearly impossible: the only easy
code is to translate the whole configuration.
- prefix and start - end forms of pool ranges are both mandatory.
@section yangTranslatorSubnet Subnet translator
The new thing here is the use of adaptors to move timers from subnets
to pools and back.
@section yangAdaptor Adapting JSON configuration
Adaptors are tools which adapts JSON complete or partial configuration
before translation to Yang to ease this translation or after translation
from Yang to follow the Kea syntax, for instance by adding static
components which are not in the model.
Methods provided by adaptors are class methods (i.e. declared static).
Specific adaptors are derived from the isc::yang::Adaptor base class.
@page unitTestsSysrepo Running unit-tests with Sysrepo
To run YANG/NETCONF/Sysrepo tests you obviously need to compile Kea with
Sysrepo support:
@verbatim
./configure --with-sysrepo
@endverbatim
For details, see Section 20 "YANG/NETCONF support" in the Kea User's Guide.
You also need to install YANG schemas, so the unit-tests are able to
retrieve, add, update and generally interact with the sysrepo information.
There are several production Kea models (src/lib/yang/models) and one test
specific model (src/lib/yang/tests/keatest-module.yang).
To install the test module, issue the following command:
@verbatim
sudo sysrepoctl --install --yang=src/lib/yang/tests/keatest-module.yang
@endverbatim
To verify that you have the schemas installed, do this:
@verbatim
sysrepoctl -l
@endverbatim
Make sure that keatest-module is on the list.
*/

View File

@@ -765,8 +765,521 @@ CREATE TABLE logs (
CREATE INDEX timestamp_index ON logs (timestamp);
#add auth key for reconfiguration
ALTER TABLE hosts
ADD COLUMN auth_key VARCHAR(16) NULL;
ALTER TABLE hosts
ADD COLUMN auth_key VARCHAR(16) NULL;
-- -----------------------------------------------------
-- Table `modification`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS modification (
id TINYINT(3) NOT NULL,
modification_type VARCHAR(32) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB;
INSERT INTO modification(id, modification_type)
VALUES(0, "create");
INSERT INTO modification(id, modification_type)
VALUES(1, "update");
INSERT INTO modification(id, modification_type)
VALUES(2, "delete");
-- -----------------------------------------------------
-- Table `dhcp4_server`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS dhcp4_server (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
tag VARCHAR(256) NOT NULL,
description TEXT,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY dhcp4_server_tag_UNIQUE (tag),
KEY key_dhcp4_server_modification_ts (modification_ts)
) ENGINE=InnoDB;
-- -----------------------------------------------------
-- Table `dhcp4_audit`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS dhcp4_audit (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
object_type VARCHAR(256) NOT NULL,
object_id BIGINT(20) UNSIGNED NOT NULL,
modification_type TINYINT(1) NOT NULL,
modification_ts TIMESTAMP NOT NULL,
log_message TEXT,
PRIMARY KEY (id),
KEY key_dhcp4_audit_by_modification_ts (modification_ts),
KEY fk_dhcp4_audit_modification_type (modification_type),
CONSTRAINT fk_dhcp4_audit_modification_type FOREIGN KEY (modification_type)
REFERENCES modification (id)
ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
-- -----------------------------------------------------
-- Table `dhcp4_global_parameter`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS dhcp4_global_parameter (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(128) NOT NULL,
value LONGTEXT NOT NULL,
modification_ts timestamp NOT NULL,
PRIMARY KEY (id),
KEY key_dhcp4_global_parameter_modification_ts (modification_ts),
KEY key_dhcp4_global_parameter_name (name)
) ENGINE=InnoDB;
-- -----------------------------------------------------
-- Table `dhcp4_global_parameter_server`
-- M-to-M cross-reference between global parameters and
-- servers
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS dhcp4_global_parameter_server (
parameter_id BIGINT(20) UNSIGNED NOT NULL,
server_id BIGINT(20) UNSIGNED NOT NULL,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (parameter_id, server_id),
KEY fk_dhcp4_global_parameter_server_server_id (server_id),
KEY key_dhcp4_global_parameter_server (modification_ts),
CONSTRAINT fk_dhcp4_global_parameter_server_parameter_id FOREIGN KEY (parameter_id)
REFERENCES dhcp4_global_parameter (id)
ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_dhcp4_global_parameter_server_server_id FOREIGN KEY (server_id)
REFERENCES dhcp4_server (id)
ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
-- -----------------------------------------------------
-- Table `dhcp4_option_def`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS dhcp4_option_def (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
code TINYINT(3) UNSIGNED NOT NULL,
space VARCHAR(128) NOT NULL,
modification_ts TIMESTAMP NOT NULL,
array TINYINT(1) NOT NULL,
encapsulate VARCHAR(128) NOT NULL,
record_types VARCHAR(512) DEFAULT NULL,
user_context LONGTEXT,
PRIMARY KEY (id),
KEY key_dhcp4_option_def_modification_ts (modification_ts),
KEY key_dhcp4_option_def_code_space (code, space)
) ENGINE=InnoDB;
-- -----------------------------------------------------
-- Table `dhcp4_option_def_server`
-- M-to-M cross-reference between option definitions and
-- servers
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS dhcp4_option_def_server (
option_def_id BIGINT(20) UNSIGNED NOT NULL,
server_id BIGINT(20) UNSIGNED NOT NULL,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (option_def_id, server_id),
KEY fk_dhcp4_option_def_server_server_id_idx (server_id),
KEY key_dhcp4_option_def_server_modification_ts (modification_ts),
CONSTRAINT fk_dhcp4_option_def_server_option_def_id FOREIGN KEY (option_def_id)
REFERENCES dhcp4_option_def (id)
ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_dhcp4_option_def_server_server_id FOREIGN KEY (server_id)
REFERENCES dhcp4_server (id) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
-- -----------------------------------------------------
-- Table `dhcp4_shared_network`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS dhcp4_shared_network (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(128) NOT NULL,
client_class VARCHAR(128) DEFAULT NULL,
interface VARCHAR(128) DEFAULT NULL,
match_client_id TINYINT(1) NOT NULL DEFAULT '1',
modification_ts TIMESTAMP NOT NULL,
rebind_timer INT(10) DEFAULT NULL,
relay LONGTEXT,
renew_timer INT(10) DEFAULT NULL,
require_client_classes LONGTEXT DEFAULT NULL,
reservation_mode TINYINT(3) NOT NULL DEFAULT '3',
server_hostname VARCHAR(512) DEFAULT NULL,
user_context LONGTEXT,
valid_lifetime INT(10) DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY name_UNIQUE (name),
KEY key_dhcp4_shared_network_modification_ts (modification_ts)
) ENGINE=InnoDB;
-- -----------------------------------------------------
-- Table `dhcp4_shared_network_server`
-- M-to-M cross-reference between shared networks and
-- servers
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS dhcp4_shared_network_server (
shared_network_id BIGINT(20) UNSIGNED NOT NULL,
server_id BIGINT(20) UNSIGNED NOT NULL,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (shared_network_id, server_id),
KEY key_dhcp4_shared_network_server_modification_ts (modification_ts),
KEY fk_dhcp4_shared_network_server_server_id (server_id),
CONSTRAINT fk_dhcp4_shared_network_server_server_id FOREIGN KEY (server_id)
REFERENCES dhcp4_server (id)
ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_dhcp4_shared_network_server_shared_network_id FOREIGN KEY (shared_network_id)
REFERENCES dhcp4_shared_network (id) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
-- -----------------------------------------------------
-- Table `dhcp4_subnet`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS dhcp4_subnet (
subnet_id INT(10) UNSIGNED NOT NULL,
subnet_prefix VARCHAR(32) NOT NULL,
4o6_interface VARCHAR(128) DEFAULT NULL,
4o6_interface_id VARCHAR(128) DEFAULT NULL,
4o6_subnet VARCHAR(64) DEFAULT NULL,
boot_file_name VARCHAR(512) DEFAULT NULL,
client_class VARCHAR(128) DEFAULT NULL,
interface VARCHAR(128) DEFAULT NULL,
match_client_id TINYINT(1) NOT NULL DEFAULT '1',
modification_ts TIMESTAMP NOT NULL,
next_server INT(10) DEFAULT NULL,
rebind_timer INT(10) DEFAULT NULL,
relay LONGTEXT,
renew_timer INT(10) DEFAULT NULL,
require_client_classes LONGTEXT DEFAULT NULL,
reservation_mode TINYINT(3) NOT NULL DEFAULT '3',
server_hostname VARCHAR(512) DEFAULT NULL,
shared_network_name VARCHAR(128) DEFAULT NULL,
user_context LONGTEXT,
valid_lifetime INT(10) DEFAULT NULL,
PRIMARY KEY (subnet_id),
UNIQUE KEY subnet4_subnet_prefix (subnet_prefix),
KEY fk_dhcp4_subnet_shared_network (shared_network_name),
KEY key_dhcp4_subnet_modification_ts (modification_ts),
CONSTRAINT fk_dhcp4_subnet_shared_network FOREIGN KEY (shared_network_name)
REFERENCES dhcp4_shared_network (name)
ON DELETE SET NULL ON UPDATE NO ACTION
) ENGINE=InnoDB;
-- -----------------------------------------------------
-- Table `dhcp4_pool`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS dhcp4_pool (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
start_address INT(10) NOT NULL,
end_address INT(10) NOT NULL,
subnet_id INT(10) UNSIGNED NOT NULL,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (id),
KEY key_dhcp4_pool_modification_ts (modification_ts),
KEY fk_dhcp4_pool_subnet_id (subnet_id),
CONSTRAINT fk_dhcp4_pool_subnet_id FOREIGN KEY (subnet_id)
REFERENCES dhcp4_subnet (subnet_id)
ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB;
-- -----------------------------------------------------
-- Table `dhcp4_subnet_server`
-- M-to-M cross-reference between subnets and servers
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS dhcp4_subnet_server (
subnet_id INT(10) UNSIGNED NOT NULL,
server_id BIGINT(20) UNSIGNED NOT NULL,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (subnet_id,server_id),
KEY fk_dhcp4_subnet_server_server_id_idx (server_id),
KEY key_dhcp4_subnet_server_modification_ts (modification_ts),
CONSTRAINT fk_dhcp4_subnet_server_server_id FOREIGN KEY (server_id)
REFERENCES dhcp4_server (id)
ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_dhcp4_subnet_server_subnet_id FOREIGN KEY (subnet_id)
REFERENCES dhcp4_subnet (subnet_id)
ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
# Modify the primary key to BINGINT as other tables have.
ALTER TABLE dhcp4_options MODIFY option_id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT;
# Add conifguration backend specific columns.
ALTER TABLE dhcp4_options
ADD COLUMN shared_network_name VARCHAR(128) DEFAULT NULL,
ADD COLUMN pool_id BIGINT(20) UNSIGNED DEFAULT NULL,
ADD COLUMN modification_ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;
-- -----------------------------------------------------
-- Table `dhcp4_options_server`
-- M-to-M cross-reference between options and servers
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS dhcp4_options_server (
option_id BIGINT(20) UNSIGNED NOT NULL,
server_id BIGINT(20) UNSIGNED NOT NULL,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (option_id, server_id),
KEY fk_dhcp4_options_server_server_id (server_id),
KEY key_dhcp4_options_server_modification_ts (modification_ts),
CONSTRAINT fk_dhcp4_options_server_option_id FOREIGN KEY (option_id)
REFERENCES dhcp4_options (option_id)
ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_dhcp4_options_server_server_id FOREIGN KEY (server_id)
REFERENCES dhcp4_server (id)
ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
-- -----------------------------------------------------
-- Table `dhcp6_server`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS dhcp6_server (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
tag VARCHAR(256) NOT NULL,
description TEXT,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY dhcp6_server_tag_UNIQUE (tag),
KEY key_dhcp6_server_modification_ts (modification_ts)
) ENGINE=InnoDB;
-- -----------------------------------------------------
-- Table `dhcp6_audit`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS dhcp6_audit (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
object_type VARCHAR(256) NOT NULL,
object_id BIGINT(20) UNSIGNED NOT NULL,
modification_type TINYINT(1) NOT NULL,
modification_ts TIMESTAMP NOT NULL,
log_message TEXT,
PRIMARY KEY (id),
KEY key_dhcp6_audit_modification_ts (modification_ts),
KEY fk_dhcp6_audit_modification_type (modification_type),
CONSTRAINT fk_dhcp6_audit_modification_type FOREIGN KEY (modification_type)
REFERENCES modification (id)
ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
-- -----------------------------------------------------
-- Table `dhcp6_global_parameter`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS dhcp6_global_parameter (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(128) NOT NULL,
value LONGTEXT NOT NULL,
modification_ts timestamp NOT NULL,
PRIMARY KEY (id),
KEY key_dhcp6_global_parameter_modification_ts (modification_ts),
KEY key_dhcp6_global_parameter_name (name)
) ENGINE=InnoDB;
-- -----------------------------------------------------
-- Table `dhcp6_global_parameter_server`
-- M-to-M cross-reference between global parameters and
-- servers
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS dhcp6_global_parameter_server (
parameter_id BIGINT(20) UNSIGNED NOT NULL,
server_id BIGINT(20) UNSIGNED NOT NULL,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (parameter_id, server_id),
KEY fk_dhcp6_global_parameter_server_server_id (server_id),
KEY key_dhcp6_global_parameter_server (modification_ts),
CONSTRAINT fk_dhcp6_global_parameter_server_parameter_id FOREIGN KEY (parameter_id)
REFERENCES dhcp6_global_parameter (id)
ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_dhcp6_global_parameter_server_server_id FOREIGN KEY (server_id)
REFERENCES dhcp6_server (id)
ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
-- -----------------------------------------------------
-- Table `dhcp6_option_def`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS dhcp6_option_def (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
code TINYINT(3) UNSIGNED NOT NULL,
space VARCHAR(128) NOT NULL,
modification_ts TIMESTAMP NOT NULL,
array TINYINT(1) NOT NULL,
encapsulate VARCHAR(128) NOT NULL,
record_types VARCHAR(512) DEFAULT NULL,
user_context LONGTEXT,
PRIMARY KEY (id),
KEY key_dhcp6_option_def_modification_ts (modification_ts),
KEY key_dhcp6_option_def_code_space (code, space)
) ENGINE=InnoDB;
-- -----------------------------------------------------
-- Table `dhcp6_option_def_server`
-- M-to-M cross-reference between option definitions and
-- servers
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS dhcp6_option_def_server (
option_def_id BIGINT(20) UNSIGNED NOT NULL,
server_id BIGINT(20) UNSIGNED NOT NULL,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (option_def_id, server_id),
KEY fk_dhcp6_option_def_server_server_id_idx (server_id),
KEY key_dhcp6_option_def_server_modification_ts (modification_ts),
CONSTRAINT fk_dhcp6_option_def_server_option_def_id FOREIGN KEY (option_def_id)
REFERENCES dhcp6_option_def (id)
ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_dhcp6_option_def_server_server_id FOREIGN KEY (server_id)
REFERENCES dhcp6_server (id) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
-- -----------------------------------------------------
-- Table `dhcp6_shared_network`
-- -----------------------------------------------------
CREATE TABLE dhcp6_shared_network (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(128) NOT NULL,
client_class VARCHAR(128) DEFAULT NULL,
interface VARCHAR(128) DEFAULT NULL,
modification_ts TIMESTAMP NOT NULL,
preferred_lifetime INT(10) DEFAULT NULL,
rapid_commit TINYINT(1) NOT NULL DEFAULT '1',
rebind_timer INT(10) DEFAULT NULL,
relay LONGTEXT DEFAULT NULL,
renew_timer INT(10) DEFAULT NULL,
require_client_classes LONGTEXT,
reservation_mode TINYINT(3) NOT NULL DEFAULT '3',
server_hostname VARCHAR(512) DEFAULT NULL,
user_context LONGTEXT,
valid_lifetime INT(10) DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY name_UNIQUE (name),
KEY key_dhcp6_shared_network_modification_ts (modification_ts)
) ENGINE=InnoDB;
-- -----------------------------------------------------
-- Table `dhcp6_shared_network_server`
-- M-to-M cross-reference between shared networks and
-- servers
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS dhcp6_shared_network_server (
shared_network_id BIGINT(20) UNSIGNED NOT NULL,
server_id BIGINT(20) UNSIGNED NOT NULL,
modification_ts TIMESTAMP NOT NULL,
KEY key_dhcp6_shared_network_server_modification_ts (modification_ts),
KEY fk_dhcp6_shared_network_server_server_id_idx (server_id),
KEY fk_dhcp6_shared_network_server_shared_network_id (shared_network_id),
CONSTRAINT fk_dhcp6_shared_network_server_server_id FOREIGN KEY (server_id)
REFERENCES dhcp6_server (id)
ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_dhcp6_shared_network_server_shared_network_id FOREIGN KEY (shared_network_id)
REFERENCES dhcp6_shared_network (id)
ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
-- -----------------------------------------------------
-- Table `dhcp6_subnet`
-- -----------------------------------------------------
CREATE TABLE dhcp6_subnet (
subnet_id INT(10) UNSIGNED NOT NULL,
subnet_prefix VARCHAR(64) NOT NULL,
client_class VARCHAR(128) DEFAULT NULL,
interface VARCHAR(128) DEFAULT NULL,
modification_ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
preferred_lifetime INT(10) DEFAULT NULL,
rapid_commit TINYINT(1) NOT NULL DEFAULT '1',
rebind_timer INT(10) DEFAULT NULL,
relay LONGTEXT DEFAULT NULL,
renew_timer INT(10) DEFAULT NULL,
require_client_classes LONGTEXT,
reservation_mode TINYINT(3) NOT NULL DEFAULT '3',
shared_network_name VARCHAR(128) DEFAULT NULL,
user_context LONGTEXT,
valid_lifetime INT(10) DEFAULT NULL,
PRIMARY KEY (subnet_id),
UNIQUE KEY subnet_prefix_UNIQUE (subnet_prefix),
KEY subnet6_subnet_prefix (subnet_prefix),
KEY fk_dhcp6_subnet_shared_network (shared_network_name),
KEY key_dhcp6_subnet_modification_ts (modification_ts),
CONSTRAINT fk_dhcp6_subnet_shared_network FOREIGN KEY (shared_network_name)
REFERENCES dhcp6_shared_network (name)
ON DELETE SET NULL ON UPDATE NO ACTION
) ENGINE=InnoDB;
-- -----------------------------------------------------
-- Table `dhcp6_subnet_server`
-- M-to-M cross-reference between subnets and servers
-- -----------------------------------------------------
CREATE TABLE dhcp6_subnet_server (
subnet_id INT(10) UNSIGNED NOT NULL,
server_id BIGINT(20) UNSIGNED NOT NULL,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (subnet_id, server_id),
KEY fk_dhcp6_subnet_server_server_id (server_id),
KEY key_dhcp6_subnet_server_modification_ts (modification_ts),
CONSTRAINT fk_dhcp6_subnet_server_server_id FOREIGN KEY (server_id)
REFERENCES dhcp6_server (id)
ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_dhcp6_subnet_server_subnet_id FOREIGN KEY (subnet_id)
REFERENCES dhcp6_subnet (subnet_id)
ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
-- -----------------------------------------------------
-- Table `dhcp6_pd_pool`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS dhcp6_pd_pool (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
prefix VARCHAR(45) NOT NULL,
prefix_length TINYINT(3) NOT NULL,
delegated_prefix_length TINYINT(3) NOT NULL,
dhcp6_subnet_id INT(10) UNSIGNED NOT NULL,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (id),
KEY fk_dhcp6_pd_pool_subnet_id (dhcp6_subnet_id),
KEY key_dhcp6_pd_pool_modification_ts (modification_ts),
CONSTRAINT fk_dhcp6_pd_pool_subnet_id FOREIGN KEY (dhcp6_subnet_id)
REFERENCES dhcp6_subnet (subnet_id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB;
-- -----------------------------------------------------
-- Table `dhcp6_pool`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS dhcp6_pool (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
start_address VARCHAR(45) NOT NULL,
end_address VARCHAR(45) NOT NULL,
dhcp6_subnet_id INT(10) UNSIGNED NOT NULL,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (id),
KEY fk_dhcp6_pool_subnet_id (dhcp6_subnet_id),
KEY key_dhcp6_pool_modification_ts (modification_ts),
CONSTRAINT fk_dhcp6_pool_subnet_id FOREIGN KEY (dhcp6_subnet_id)
REFERENCES dhcp6_subnet (subnet_id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB;
# Modify the primary key to BINGINT as other tables have.
ALTER TABLE dhcp6_options MODIFY option_id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT;
# Add conifguration backend specific columns.
ALTER TABLE dhcp6_options
ADD COLUMN shared_network_name VARCHAR(128) DEFAULT NULL,
ADD COLUMN pool_id BIGINT(20) UNSIGNED DEFAULT NULL,
ADD COLUMN pd_pool_id BIGINT(20) UNSIGNED DEFAULT NULL,
ADD COLUMN modification_ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;
-- -----------------------------------------------------
-- Table `dhcp6_options_server`
-- M-to-M cross-reference between options and servers
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS dhcp6_options_server (
option_id BIGINT(20) UNSIGNED NOT NULL,
server_id BIGINT(20) UNSIGNED NOT NULL,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (option_id, server_id),
KEY fk_dhcp6_options_server_server_id_idx (server_id),
KEY key_dhcp6_options_server_modification_ts (modification_ts),
CONSTRAINT fk_dhcp6_options_server_option_id FOREIGN KEY (option_id)
REFERENCES dhcp6_options (option_id)
ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_dhcp6_options_server_server_id FOREIGN KEY (server_id)
REFERENCES dhcp6_server (id)
ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
# Update the schema version number
UPDATE schema_version

View File

@@ -32,3 +32,29 @@ DROP TRIGGER IF EXISTS lease6_stat_update;
DROP TRIGGER IF EXISTS lease6_stat_delete;
DROP TABLE IF EXISTS lease6_stat;
DROP TABLE IF EXISTS logs;
DROP TABLE IF EXISTS dhcp4_audit;
DROP TABLE IF EXISTS dhcp4_global_parameter;
DROP TABLE IF EXISTS dhcp4_global_parameter_server;
DROP TABLE IF EXISTS dhcp4_option_def;
DROP TABLE IF EXISTS dhcp4_option_def_server;
DROP TABLE IF EXISTS dhcp4_options_server;
DROP TABLE IF EXISTS dhcp4_pool;
DROP TABLE IF EXISTS dhcp4_server;
DROP TABLE IF EXISTS dhcp4_shared_network;
DROP TABLE IF EXISTS dhcp4_shared_network_server;
DROP TABLE IF EXISTS dhcp4_subnet;
DROP TABLE IF EXISTS dhcp4_subnet_server;
DROP TABLE IF EXISTS dhcp6_audit;
DROP TABLE IF EXISTS dhcp6_global_parameter;
DROP TABLE IF EXISTS dhcp6_global_parameter_server;
DROP TABLE IF EXISTS dhcp6_option_def;
DROP TABLE IF EXISTS dhcp6_option_def_server;
DROP TABLE IF EXISTS dhcp6_options_server;
DROP TABLE IF EXISTS dhcp6_pd_pool;
DROP TABLE IF EXISTS dhcp6_pool;
DROP TABLE IF EXISTS dhcp6_server;
DROP TABLE IF EXISTS dhcp6_shared_network;
DROP TABLE IF EXISTS dhcp6_shared_network_server;
DROP TABLE IF EXISTS dhcp6_subnet;
DROP TABLE IF EXISTS dhcp6_subnet_server;
DROP TABLE IF EXISTS modification;

View File

@@ -112,6 +112,485 @@ UPDATE dhcp4_options SET dhcp4_subnet_id = NULL WHERE dhcp4_subnet_id = 0;
UPDATE hosts SET dhcp6_subnet_id = NULL WHERE dhcp6_subnet_id = 0;
UPDATE dhcp6_options SET dhcp6_subnet_id = NULL WHERE dhcp6_subnet_id = 0;
# Create table modification
CREATE TABLE IF NOT EXISTS modification (
id TINYINT(3) NOT NULL,
modification_type VARCHAR(32) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB;
INSERT INTO modification(id, modification_type)
VALUES(0, "create");
INSERT INTO modification(id, modification_type)
VALUES(1, "update");
INSERT INTO modification(id, modification_type)
VALUES(2, "delete");
# Create table dhcp4_server
#
CREATE TABLE IF NOT EXISTS dhcp4_server (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
tag VARCHAR(256) NOT NULL,
description TEXT,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY dhcp4_server_tag_UNIQUE (tag),
KEY key_dhcp4_server_modification_ts (modification_ts)
) ENGINE=InnoDB;
# Create table dhcp4_audit
#
CREATE TABLE IF NOT EXISTS dhcp4_audit (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
object_type VARCHAR(256) NOT NULL,
object_id BIGINT(2) UNSIGNED NOT NULL,
modification_type TINYINT(1) NOT NULL,
modification_ts TIMESTAMP NOT NULL,
log_message TEXT,
PRIMARY KEY (id),
KEY key_dhcp4_audit_by_modification_ts (modification_ts),
KEY fk_dhcp4_audit_modification_type (modification_type),
CONSTRAINT fk_dhcp4_audit_modification_type FOREIGN KEY (modification_type)
REFERENCES modification (id)
ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
# Create table dhcp4_global_parameter
#
CREATE TABLE IF NOT EXISTS dhcp4_global_parameter (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(128) NOT NULL,
value LONGTEXT NOT NULL,
modification_ts timestamp NOT NULL,
PRIMARY KEY (id),
KEY key_dhcp4_global_parameter_modification_ts (modification_ts),
KEY key_dhcp4_global_parameter_name (name)
) ENGINE=InnoDB;
# Create table dhcp4_global_parameter_server
# M-to-M cross-reference between global parameters and servers
#
CREATE TABLE IF NOT EXISTS dhcp4_global_parameter_server (
parameter_id BIGINT(20) UNSIGNED NOT NULL,
server_id BIGINT(20) UNSIGNED NOT NULL,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (parameter_id, server_id),
KEY fk_dhcp4_global_parameter_server_server_id (server_id),
KEY key_dhcp4_global_parameter_server (modification_ts),
CONSTRAINT fk_dhcp4_global_parameter_server_parameter_id FOREIGN KEY (parameter_id)
REFERENCES dhcp4_global_parameter (id)
ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_dhcp4_global_parameter_server_server_id FOREIGN KEY (server_id)
REFERENCES dhcp4_server (id)
ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
# Create table dhcp4_option_def
#
CREATE TABLE IF NOT EXISTS dhcp4_option_def (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
code TINYINT(3) UNSIGNED NOT NULL,
space VARCHAR(128) NOT NULL,
modification_ts TIMESTAMP NOT NULL,
array TINYINT(1) NOT NULL,
encapsulate VARCHAR(128) NOT NULL,
record_types VARCHAR(512) DEFAULT NULL,
user_context LONGTEXT,
PRIMARY KEY (id),
KEY key_dhcp4_option_def_modification_ts (modification_ts),
KEY key_dhcp4_option_def_code_space (code, space)
) ENGINE=InnoDB;
# Create table dhcp4_option_def_server
# M-to-M cross-reference between option definitions and servers
#
CREATE TABLE IF NOT EXISTS dhcp4_option_def_server (
option_def_id BIGINT(20) UNSIGNED NOT NULL,
server_id BIGINT(20) UNSIGNED NOT NULL,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (option_def_id, server_id),
KEY fk_dhcp4_option_def_server_server_id_idx (server_id),
KEY key_dhcp4_option_def_server_modification_ts (modification_ts),
CONSTRAINT fk_dhcp4_option_def_server_option_def_id FOREIGN KEY (option_def_id)
REFERENCES dhcp4_option_def (id)
ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_dhcp4_option_def_server_server_id FOREIGN KEY (server_id)
REFERENCES dhcp4_server (id) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
# Create table dhcp4_shared_network
#
CREATE TABLE IF NOT EXISTS dhcp4_shared_network (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(128) NOT NULL,
client_class VARCHAR(128) DEFAULT NULL,
interface VARCHAR(128) DEFAULT NULL,
match_client_id TINYINT(1) NOT NULL DEFAULT '1',
modification_ts TIMESTAMP NOT NULL,
rebind_timer INT(10) DEFAULT NULL,
relay LONGTEXT,
renew_timer INT(10) DEFAULT NULL,
require_client_classes LONGTEXT DEFAULT NULL,
reservation_mode TINYINT(3) NOT NULL DEFAULT '3',
server_hostname VARCHAR(512) DEFAULT NULL,
user_context LONGTEXT,
valid_lifetime INT(10) DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY name_UNIQUE (name),
KEY key_dhcp4_shared_network_modification_ts (modification_ts)
) ENGINE=InnoDB;
# Create table dhcp4_shared_network_server
# M-to-M cross-reference between shared networks and servers
#
CREATE TABLE IF NOT EXISTS dhcp4_shared_network_server (
shared_network_id BIGINT(20) UNSIGNED NOT NULL,
server_id BIGINT(20) UNSIGNED NOT NULL,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (shared_network_id, server_id),
KEY key_dhcp4_shared_network_server_modification_ts (modification_ts),
KEY fk_dhcp4_shared_network_server_server_id (server_id),
CONSTRAINT fk_dhcp4_shared_network_server_server_id FOREIGN KEY (server_id)
REFERENCES dhcp4_server (id)
ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_dhcp4_shared_network_server_shared_network_id FOREIGN KEY (shared_network_id)
REFERENCES dhcp4_shared_network (id) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
# Create table dhcp4_subnet
#
CREATE TABLE IF NOT EXISTS dhcp4_subnet (
subnet_id INT(10) UNSIGNED NOT NULL,
subnet_prefix VARCHAR(32) NOT NULL,
4o6_interface VARCHAR(128) DEFAULT NULL,
4o6_interface_id VARCHAR(128) DEFAULT NULL,
4o6_subnet VARCHAR(64) DEFAULT NULL,
boot_file_name VARCHAR(512) DEFAULT NULL,
client_class VARCHAR(128) DEFAULT NULL,
interface VARCHAR(128) DEFAULT NULL,
match_client_id TINYINT(1) NOT NULL DEFAULT '1',
modification_ts TIMESTAMP NOT NULL,
next_server INT(10) DEFAULT NULL,
rebind_timer INT(10) DEFAULT NULL,
relay LONGTEXT,
renew_timer INT(10) DEFAULT NULL,
require_client_classes LONGTEXT DEFAULT NULL,
reservation_mode TINYINT(3) NOT NULL DEFAULT '3',
server_hostname VARCHAR(512) DEFAULT NULL,
shared_network_name VARCHAR(128) DEFAULT NULL,
user_context LONGTEXT,
valid_lifetime INT(10) DEFAULT NULL,
PRIMARY KEY (subnet_id),
UNIQUE KEY subnet4_subnet_prefix (subnet_prefix),
KEY fk_dhcp4_subnet_shared_network (shared_network_name),
KEY key_dhcp4_subnet_modification_ts (modification_ts),
CONSTRAINT fk_dhcp4_subnet_shared_network FOREIGN KEY (shared_network_name)
REFERENCES dhcp4_shared_network (name)
ON DELETE SET NULL ON UPDATE NO ACTION
) ENGINE=InnoDB;
# Create table dhcp4_pool
#
CREATE TABLE IF NOT EXISTS dhcp4_pool (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
start_address INT(10) NOT NULL,
end_address INT(10) NOT NULL,
subnet_id INT(10) UNSIGNED NOT NULL,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (id),
KEY key_dhcp4_pool_modification_ts (modification_ts),
KEY fk_dhcp4_pool_subnet_id (subnet_id),
CONSTRAINT fk_dhcp4_pool_subnet_id FOREIGN KEY (subnet_id)
REFERENCES dhcp4_subnet (subnet_id)
ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB;
# Create table dhcp4_subnet_server
# M-to-M cross-reference between subnets and servers
#
CREATE TABLE IF NOT EXISTS dhcp4_subnet_server (
subnet_id INT(10) UNSIGNED NOT NULL,
server_id BIGINT(20) UNSIGNED NOT NULL,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (subnet_id,server_id),
KEY fk_dhcp4_subnet_server_server_id_idx (server_id),
KEY key_dhcp4_subnet_server_modification_ts (modification_ts),
CONSTRAINT fk_dhcp4_subnet_server_server_id FOREIGN KEY (server_id)
REFERENCES dhcp4_server (id)
ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_dhcp4_subnet_server_subnet_id FOREIGN KEY (subnet_id)
REFERENCES dhcp4_subnet (subnet_id)
ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
# Modify the primary key to BINGINT as other tables have.
#
ALTER TABLE dhcp4_options MODIFY option_id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT;
# Add conifguration backend specific columns.
ALTER TABLE dhcp4_options
ADD COLUMN shared_network_name VARCHAR(128) DEFAULT NULL,
ADD COLUMN pool_id BIGINT(20) UNSIGNED DEFAULT NULL,
ADD COLUMN modification_ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;
# Create table dhcp4_options_server
# M-to-M cross-reference between options and servers
#
CREATE TABLE IF NOT EXISTS dhcp4_options_server (
option_id BIGINT(20) UNSIGNED NOT NULL,
server_id BIGINT(20) UNSIGNED NOT NULL,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (option_id, server_id),
KEY fk_dhcp4_options_server_server_id (server_id),
KEY key_dhcp4_options_server_modification_ts (modification_ts),
CONSTRAINT fk_dhcp4_options_server_option_id FOREIGN KEY (option_id)
REFERENCES dhcp4_options (option_id)
ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_dhcp4_options_server_server_id FOREIGN KEY (server_id)
REFERENCES dhcp4_server (id)
ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
# Create table dhcp6_server
#
CREATE TABLE IF NOT EXISTS dhcp6_server (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
tag VARCHAR(256) NOT NULL,
description TEXT,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY dhcp6_server_tag_UNIQUE (tag),
KEY key_dhcp6_server_modification_ts (modification_ts)
) ENGINE=InnoDB;
# Create table dhcp6_audit
#
CREATE TABLE IF NOT EXISTS dhcp6_audit (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
object_type VARCHAR(256) NOT NULL,
object_id BIGINT(20) UNSIGNED NOT NULL,
modification_type TINYINT(1) NOT NULL,
modification_ts TIMESTAMP NOT NULL,
log_message TEXT,
PRIMARY KEY (id),
KEY key_dhcp6_audit_modification_ts (modification_ts),
KEY fk_dhcp6_audit_modification_type (modification_type),
CONSTRAINT fk_dhcp6_audit_modification_type FOREIGN KEY (modification_type)
REFERENCES modification (id)
ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
# Create table dhcp6_global_parameter
#
CREATE TABLE IF NOT EXISTS dhcp6_global_parameter (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(128) NOT NULL,
value LONGTEXT NOT NULL,
modification_ts timestamp NOT NULL,
PRIMARY KEY (id),
KEY key_dhcp6_global_parameter_modification_ts (modification_ts),
KEY key_dhcp6_global_parameter_name (name)
) ENGINE=InnoDB;
# Create table dhcp6_global_parameter_server
# M-to-M cross-reference between global parameters and servers
#
CREATE TABLE IF NOT EXISTS dhcp6_global_parameter_server (
parameter_id BIGINT(20) UNSIGNED NOT NULL,
server_id BIGINT(20) UNSIGNED NOT NULL,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (parameter_id, server_id),
KEY fk_dhcp6_global_parameter_server_server_id (server_id),
KEY key_dhcp6_global_parameter_server (modification_ts),
CONSTRAINT fk_dhcp6_global_parameter_server_parameter_id FOREIGN KEY (parameter_id)
REFERENCES dhcp6_global_parameter (id)
ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_dhcp6_global_parameter_server_server_id FOREIGN KEY (server_id)
REFERENCES dhcp6_server (id)
ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
# Create table dhcp6_option_def
#
CREATE TABLE IF NOT EXISTS dhcp6_option_def (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
code TINYINT(3) UNSIGNED NOT NULL,
space VARCHAR(128) NOT NULL,
modification_ts TIMESTAMP NOT NULL,
array TINYINT(1) NOT NULL,
encapsulate VARCHAR(128) NOT NULL,
record_types VARCHAR(512) DEFAULT NULL,
user_context LONGTEXT,
PRIMARY KEY (id),
KEY key_dhcp6_option_def_modification_ts (modification_ts),
KEY key_dhcp6_option_def_code_space (code, space)
) ENGINE=InnoDB;
# Create table dhcp6_option_def_server
# M-to-M cross-reference between option definitions and servers
#
CREATE TABLE IF NOT EXISTS dhcp6_option_def_server (
option_def_id BIGINT(20) UNSIGNED NOT NULL,
server_id BIGINT(20) UNSIGNED NOT NULL,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (option_def_id, server_id),
KEY fk_dhcp6_option_def_server_server_id_idx (server_id),
KEY key_dhcp6_option_def_server_modification_ts (modification_ts),
CONSTRAINT fk_dhcp6_option_def_server_option_def_id FOREIGN KEY (option_def_id)
REFERENCES dhcp6_option_def (id)
ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_dhcp6_option_def_server_server_id FOREIGN KEY (server_id)
REFERENCES dhcp6_server (id) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
# Create table dhcp6_shared_network
#
CREATE TABLE dhcp6_shared_network (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(128) NOT NULL,
client_class VARCHAR(128) DEFAULT NULL,
interface VARCHAR(128) DEFAULT NULL,
modification_ts TIMESTAMP NOT NULL,
preferred_lifetime INT(10) DEFAULT NULL,
rapid_commit TINYINT(1) NOT NULL DEFAULT '1',
rebind_timer INT(10) DEFAULT NULL,
relay LONGTEXT DEFAULT NULL,
renew_timer INT(10) DEFAULT NULL,
require_client_classes LONGTEXT,
reservation_mode TINYINT(3) NOT NULL DEFAULT '3',
server_hostname VARCHAR(512) DEFAULT NULL,
user_context LONGTEXT,
valid_lifetime INT(10) DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY name_UNIQUE (name),
KEY key_dhcp6_shared_network_modification_ts (modification_ts)
) ENGINE=InnoDB;
# Create table dhcp6_shared_network_server
# M-to-M cross-reference between shared networks and servers
#
CREATE TABLE IF NOT EXISTS dhcp6_shared_network_server (
shared_network_id BIGINT(20) UNSIGNED NOT NULL,
server_id BIGINT(20) UNSIGNED NOT NULL,
modification_ts TIMESTAMP NOT NULL,
KEY key_dhcp6_shared_network_server_modification_ts (modification_ts),
KEY fk_dhcp6_shared_network_server_server_id_idx (server_id),
KEY fk_dhcp6_shared_network_server_shared_network_id (shared_network_id),
CONSTRAINT fk_dhcp6_shared_network_server_server_id FOREIGN KEY (server_id)
REFERENCES dhcp6_server (id)
ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_dhcp6_shared_network_server_shared_network_id FOREIGN KEY (shared_network_id)
REFERENCES dhcp6_shared_network (id)
ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
# Create table dhcp6_subnet
#
CREATE TABLE dhcp6_subnet (
subnet_id int(10) UNSIGNED NOT NULL,
subnet_prefix VARCHAR(64) NOT NULL,
client_class VARCHAR(128) DEFAULT NULL,
interface VARCHAR(128) DEFAULT NULL,
modification_ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
preferred_lifetime INT(10) DEFAULT NULL,
rapid_commit TINYINT(1) NOT NULL DEFAULT '1',
rebind_timer INT(10) DEFAULT NULL,
relay LONGTEXT DEFAULT NULL,
renew_timer INT(10) DEFAULT NULL,
require_client_classes LONGTEXT,
reservation_mode TINYINT(3) NOT NULL DEFAULT '3',
shared_network_name VARCHAR(128) DEFAULT NULL,
user_context LONGTEXT,
valid_lifetime INT(10) DEFAULT NULL,
PRIMARY KEY (subnet_id),
UNIQUE KEY subnet6_subnet_prefix (subnet_prefix),
KEY fk_dhcp6_subnet_shared_network (shared_network_name),
KEY key_dhcp6_subnet_modification_ts (modification_ts),
CONSTRAINT fk_dhcp6_subnet_shared_network FOREIGN KEY (shared_network_name)
REFERENCES dhcp6_shared_network (name)
ON DELETE SET NULL ON UPDATE NO ACTION
) ENGINE=InnoDB;
# Create table dhcp6_subnet_server
# M-to-M cross-reference between subnets and servers
#
CREATE TABLE dhcp6_subnet_server (
subnet_id INT(10) UNSIGNED NOT NULL,
server_id BIGINT(20) UNSIGNED NOT NULL,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (subnet_id, server_id),
KEY fk_dhcp6_subnet_server_server_id (server_id),
KEY key_dhcp6_subnet_server_modification_ts (modification_ts),
CONSTRAINT fk_dhcp6_subnet_server_server_id FOREIGN KEY (server_id)
REFERENCES dhcp6_server (id)
ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_dhcp6_subnet_server_subnet_id FOREIGN KEY (subnet_id)
REFERENCES dhcp6_subnet (subnet_id)
ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
# Create table dhcp6_pd_pool
#
CREATE TABLE IF NOT EXISTS dhcp6_pd_pool (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
prefix VARCHAR(45) NOT NULL,
prefix_length TINYINT(3) NOT NULL,
delegated_prefix_length TINYINT(3) NOT NULL,
dhcp6_subnet_id INT(10) UNSIGNED NOT NULL,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (id),
KEY fk_dhcp6_pd_pool_subnet_id (dhcp6_subnet_id),
KEY key_dhcp6_pd_pool_modification_ts (modification_ts),
CONSTRAINT fk_dhcp6_pd_pool_subnet_id FOREIGN KEY (dhcp6_subnet_id)
REFERENCES dhcp6_subnet (subnet_id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB;
# Create table dhcp6_pool
#
CREATE TABLE IF NOT EXISTS dhcp6_pool (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
start_address VARCHAR(45) NOT NULL,
end_address VARCHAR(45) NOT NULL,
dhcp6_subnet_id INT(10) UNSIGNED NOT NULL,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (id),
KEY fk_dhcp6_pool_subnet_id (dhcp6_subnet_id),
KEY key_dhcp6_pool_modification_ts (modification_ts),
CONSTRAINT fk_dhcp6_pool_subnet_id FOREIGN KEY (dhcp6_subnet_id)
REFERENCES dhcp6_subnet (subnet_id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB;
# Modify the primary key to BINGINT as other tables have.
ALTER TABLE dhcp6_options MODIFY option_id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT;
# Add conifguration backend specific columns.
ALTER TABLE dhcp6_options
ADD COLUMN shared_network_name VARCHAR(128) DEFAULT NULL,
ADD COLUMN pool_id BIGINT(20) UNSIGNED DEFAULT NULL,
ADD COLUMN pd_pool_id BIGINT(20) UNSIGNED DEFAULT NULL,
ADD COLUMN modification_ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;
# Create table dhcp6_options_server
# M-to-M cross-reference between options and servers
#
CREATE TABLE IF NOT EXISTS dhcp6_options_server (
option_id BIGINT(20) UNSIGNED NOT NULL,
server_id BIGINT(20) UNSIGNED NOT NULL,
modification_ts TIMESTAMP NOT NULL,
PRIMARY KEY (option_id, server_id),
KEY fk_dhcp6_options_server_server_id_idx (server_id),
KEY key_dhcp6_options_server_modification_ts (modification_ts),
CONSTRAINT fk_dhcp6_options_server_option_id FOREIGN KEY (option_id)
REFERENCES dhcp6_options (option_id)
ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_dhcp6_options_server_server_id FOREIGN KEY (server_id)
REFERENCES dhcp6_server (id)
ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
# Update the schema version number
UPDATE schema_version
SET version = '7', minor = '0';