summaryrefslogtreecommitdiffstats
path: root/src/hooks
diff options
context:
space:
mode:
authorAndrei Pavel <andrei.pavel@qualitance.com>2017-08-17 20:04:29 +0200
committerAndrei Pavel <andrei.pavel@qualitance.com>2017-08-17 20:04:29 +0200
commit529d15326887b3513413567e497118b3db2c24f3 (patch)
tree8b66b262349433802bd52e920bb4783baac57cb3 /src/hooks
parentAdded mysql_execute_script (diff)
parent[master] Added ChangeLog 1288 for trac 5315. (diff)
downloadkea-529d15326887b3513413567e497118b3db2c24f3.tar.xz
kea-529d15326887b3513413567e497118b3db2c24f3.zip
Merge branch 'isc-master' into minor-changes
Diffstat (limited to 'src/hooks')
-rw-r--r--src/hooks/dhcp/Makefile.am2
-rw-r--r--src/hooks/dhcp/lease_cmds/.gitignore4
-rw-r--r--src/hooks/dhcp/lease_cmds/Makefile.am63
-rw-r--r--src/hooks/dhcp/lease_cmds/lease_cmds.cc895
-rw-r--r--src/hooks/dhcp/lease_cmds/lease_cmds.dox96
-rw-r--r--src/hooks/dhcp/lease_cmds/lease_cmds.h67
-rw-r--r--src/hooks/dhcp/lease_cmds/lease_cmds_log.cc16
-rw-r--r--src/hooks/dhcp/lease_cmds/lease_cmds_log.h23
-rw-r--r--src/hooks/dhcp/lease_cmds/lease_cmds_messages.mes51
-rw-r--r--src/hooks/dhcp/lease_cmds/lease_parser.cc257
-rw-r--r--src/hooks/dhcp/lease_cmds/lease_parser.h92
-rw-r--r--src/hooks/dhcp/lease_cmds/load_unload.cc63
-rw-r--r--src/hooks/dhcp/lease_cmds/tests/.gitignore5
-rw-r--r--src/hooks/dhcp/lease_cmds/tests/Makefile.am57
-rw-r--r--src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc2070
-rw-r--r--src/hooks/dhcp/lease_cmds/tests/run_unittests.cc17
-rw-r--r--src/hooks/dhcp/lease_cmds/version.cc17
-rw-r--r--src/hooks/dhcp/user_chk/pkt_send_co.cc4
-rw-r--r--src/hooks/dhcp/user_chk/tests/user_file_unittests.cc10
-rw-r--r--src/hooks/dhcp/user_chk/tests/user_registry_unittests.cc4
-rw-r--r--src/hooks/dhcp/user_chk/tests/userid_unittests.cc14
-rw-r--r--src/hooks/dhcp/user_chk/user.cc4
-rw-r--r--src/hooks/dhcp/user_chk/user_file.h4
-rw-r--r--src/hooks/dhcp/user_chk/user_registry.cc4
24 files changed, 3816 insertions, 23 deletions
diff --git a/src/hooks/dhcp/Makefile.am b/src/hooks/dhcp/Makefile.am
index 6785617c01..ebf9cfe743 100644
--- a/src/hooks/dhcp/Makefile.am
+++ b/src/hooks/dhcp/Makefile.am
@@ -1 +1 @@
-SUBDIRS = user_chk
+SUBDIRS = user_chk lease_cmds
diff --git a/src/hooks/dhcp/lease_cmds/.gitignore b/src/hooks/dhcp/lease_cmds/.gitignore
new file mode 100644
index 0000000000..310e853700
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/.gitignore
@@ -0,0 +1,4 @@
+/lease_cmds_messages.cc
+/lease_cmds_messages.h
+/s-messages
+/html
diff --git a/src/hooks/dhcp/lease_cmds/Makefile.am b/src/hooks/dhcp/lease_cmds/Makefile.am
new file mode 100644
index 0000000000..60f090b6ac
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/Makefile.am
@@ -0,0 +1,63 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+
+# Define rule to build logging source files from message file
+lease_cmds_messages.h lease_cmds_messages.cc: s-messages
+s-messages: lease_cmds_messages.mes
+ $(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/hooks/dhcp/lease_cmds/lease_cmds_messages.mes
+ touch $@
+
+# Tell automake that the message files are built as part of the build process
+# (so that they are built before the main library is built).
+BUILT_SOURCES = lease_cmds_messages.h lease_cmds_messages.cc
+
+# Ensure that the message file is included in the distribution
+EXTRA_DIST = lease_cmds_messages.mes
+
+# Get rid of generated message files on a clean
+CLEANFILES = *.gcno *.gcda lease_cmds_messages.h lease_cmds_messages.cc s-messages
+
+# convenience archive
+
+noinst_LTLIBRARIES = liblease_cmds.la
+
+liblease_cmds_la_SOURCES = lease_cmds.cc lease_cmds.h
+liblease_cmds_la_SOURCES += lease_parser.h lease_parser.cc
+liblease_cmds_la_SOURCES += lease_cmds_log.cc lease_cmds_log.h
+liblease_cmds_la_SOURCES += load_unload.cc
+liblease_cmds_la_SOURCES += version.cc
+
+nodist_liblease_cmds_la_SOURCES = lease_cmds_messages.cc lease_cmds_messages.h
+
+liblease_cmds_la_CXXFLAGS = $(AM_CXXFLAGS)
+liblease_cmds_la_CPPFLAGS = $(AM_CPPFLAGS)
+
+# install the shared object into $(libdir)/hooks
+lib_hooksdir = $(libdir)/hooks
+lib_hooks_LTLIBRARIES = libdhcp_lease_cmds.la
+
+libdhcp_lease_cmds_la_SOURCES =
+libdhcp_lease_cmds_la_LDFLAGS = $(AM_LDFLAGS)
+libdhcp_lease_cmds_la_LDFLAGS += -avoid-version -export-dynamic -module
+libdhcp_lease_cmds_la_LIBADD = liblease_cmds.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/eval/libkea-eval.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/stats/libkea-stats.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/dns/libkea-dns++.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/util/threads/libkea-threads.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+libdhcp_lease_cmds_la_LIBADD += $(LOG4CPLUS_LIBS)
+libdhcp_lease_cmds_la_LIBADD += $(CRYPTO_LIBS)
+libdhcp_lease_cmds_la_LIBADD += $(BOOST_LIBS)
diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds.cc b/src/hooks/dhcp/lease_cmds/lease_cmds.cc
new file mode 100644
index 0000000000..85eb442948
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/lease_cmds.cc
@@ -0,0 +1,895 @@
+// Copyright (C) 2017 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 <lease_cmds.h>
+#include <config/command_mgr.h>
+#include <lease_parser.h>
+#include <cc/command_interpreter.h>
+#include <cc/data.h>
+#include <asiolink/io_address.h>
+#include <lease_cmds_log.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/lease_mgr.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <dhcpsrv/subnet_id.h>
+#include <dhcp/duid.h>
+#include <util/encode/hex.h>
+#include <util/strutil.h>
+#include <exceptions/exceptions.h>
+#include <boost/bind.hpp>
+#include <string>
+
+using namespace isc::dhcp;
+using namespace isc::data;
+using namespace isc::config;
+using namespace isc::asiolink;
+using namespace std;
+
+namespace isc {
+namespace lease_cmds {
+
+/// @brief Wrapper class around reservation command handlers.
+class LeaseCmdsImpl {
+public:
+ LeaseCmdsImpl();
+
+ ~LeaseCmdsImpl();
+
+/// @brief Parameters specified for reservation-get and reservation-del
+///
+/// As both call types (get and delete) need specify which reservation to
+/// act on, they have the same set of parameters. In particular, those
+/// two call types support the following sets of parameters:
+/// - address
+/// - subnet-id, identifier-type, identifier-value (v4)
+/// - subnet-id, lease-type, iaid, identifier-type, identifier-value (v6)
+///
+/// This class stores those parameters and is used to pass them around.
+class Parameters {
+public:
+
+ /// @brief specifies type of query (by IP addr, by hwaddr, by DUID)
+ typedef enum {
+ TYPE_ADDR, ///< query by IP address (either v4 or v6)
+ TYPE_HWADDR, ///< query by hardware address (v4 only)
+ TYPE_DUID ///< query by DUID (v6 only)
+ } Type;
+
+ /// @brief Specifies subnet-id (always used)
+ SubnetID subnet_id;
+
+ /// @brief Specifies IPv4/v6 address (used when query_type is TYPE_ADDR)
+ IOAddress addr;
+
+ /// @brief Specifies hardware address (used when query_type is TYPE_HWADDR)
+ HWAddrPtr hwaddr;
+
+ /// @brief Specifies identifier value (used when query_type is TYPE_DUID)
+ isc::dhcp::DuidPtr duid;
+
+ /// @brief Attempts to covert text to one of specified types
+ ///
+ /// Supported values are: "address", hw-address and duid.
+ ///
+ /// @param txt text to be converted
+ /// @return value converted to Parameters::Type
+ /// @throw BadValue if unsupported type is specified
+ static Type txtToType(const std::string& txt) {
+ if (txt == "address") {
+ return (Parameters::TYPE_ADDR);
+ } else if (txt == "hw-address") {
+ return (Parameters::TYPE_HWADDR);
+ } else if (txt == "duid") {
+ return (Parameters::TYPE_DUID);
+ } else {
+ isc_throw(BadValue, "Incorrect identifier type: "
+ << txt << ", the only supported values are: "
+ "address, hw-address, duid");
+ }
+ }
+
+ /// @brief specifies parameter types (true = query by address, false =
+ /// query by identifier-type,identifier)
+ Type query_type;
+
+ /// @brief Lease type (NA,TA or PD) used for v6 leases
+ Lease::Type lease_type;
+
+ /// @brief IAID identifier used for v6 leases
+ uint32_t iaid;
+
+ /// @brief Default constructor.
+ Parameters()
+ :addr("::"), query_type(TYPE_ADDR), lease_type(Lease::TYPE_NA),
+ iaid(0) {
+ }
+};
+
+private:
+
+ /// @brief Registers commands:
+ ///
+ /// Registers:
+ /// - lease4-add
+ /// - lease6-add
+ /// - lease4-get
+ /// - lease6-get
+ /// - lease4-del
+ /// - lease6-del
+ /// - lease4-update
+ /// - lease6-update
+ /// - lease4-wipe
+ /// - lease6-wipe
+
+ /// @throw Unexpected if CommandMgr is not available (should not happen)
+ void registerCommands();
+
+ /// @brief Deregisters commands:
+ ///
+ /// Deregisters:
+ /// - lease4-add
+ /// - lease6-add
+ /// - lease4-get
+ /// - lease6-get
+ /// - lease4-del
+ /// - lease6-del
+ /// - lease4-update
+ /// - lease6-update
+ /// - lease4-wipe
+ /// - lease6-wipe
+ ///
+ /// @throw Unexpected if CommandMgr is not available (should not happen)
+ void deregisterCommands();
+
+ /// @brief lease4-add, lease6-add command handler
+ ///
+ /// This command attempts to add a lease.
+ ///
+ /// An example full command looks as follows. Note that the args
+ /// parameter is expected to contain the "arguments" portion of it.
+ /// This function covers both v4 and v6 leases.
+ ///
+ /// Example command for v4:
+ /// {
+ /// "command": "lease4-add",
+ /// "parameters": {
+ /// "address": "192.0.2.1",
+ /// "hwaddr": "00:01:02:03:04:05",
+ /// "client-id": "this-is-a-client",
+ /// "valid-lft": 3600,
+ /// "expire": 12345678,
+ /// "subnet-id": 1,
+ /// "fqdn-fwd": true,
+ /// "fqdn-rev": true,
+ /// "hostname": "myhost.example.org",
+ /// "state": 0
+ /// }
+ /// }
+ /// Example command for v6:
+ /// {
+ /// "command": "lease6-add",
+ /// "arguments": {
+ /// "subnet-id": 66,
+ /// "ip-address": "2001:db8:abcd::",
+ /// "type": "IA_PD",
+ /// "prefix-len": 48,
+ /// "duid": "01:02:03:04:05:06:07:08",
+ /// "iaid": 1234,
+ /// "preferred-lft": 500,
+ /// "valid-lft": 1000,
+ /// "expire": 12345678,
+ /// "fqdn-fwd": true,
+ /// "fqdn-rev": true,
+ /// "hostname": "urania.example.org""
+ /// }
+ /// }
+
+ ///
+ /// @param command should be 'lease4-add' or 'lease6-add'
+ /// @param args must contain host reservation definition.
+ /// @return result of the operation
+ static ConstElementPtr
+ leaseAddHandler(const string& command, ConstElementPtr args);
+
+ /// @brief lease4-get, lease6-get command handler
+ ///
+ /// This command attempts to retrieve a lease that match selected criteria.
+ /// The following types of parameters are supported:
+ /// - (subnet-id, address) for both v4 and v6
+ /// - (subnet-id, identifier-type, identifier) for v4
+ /// - (subnet-id, type, iana, identifier-type, identifier) for v6
+ ///
+ /// Example command for query by (subnet-id, address):
+ /// {
+ /// "command": "lease4-get",
+ /// "arguments": {
+ /// "subnet-id": 1,
+ /// "ip-address": "192.0.2.202"
+ /// }
+ /// }
+ ///
+ /// Example command for query by (subnet-id, identifier-type, identifier)
+ /// {
+ /// "command": "lease4-get",
+ /// "arguments": {
+ /// "subnet-id": 1,
+ /// "identifier-type": "hw-address",
+ /// "identifier": "00:01:02:03:04:05"
+ /// }
+ /// }
+ ///
+ /// Example command for query by (subnet-id, type, iana, identifier-type,
+ /// identifier):
+ /// {
+ /// "command": "lease6-get",
+ /// "arguments": {
+ /// "subnet-id": 66,
+ /// "iaid": 42,
+ /// "type": "IA_NA",
+ /// "identifier-type": "duid",
+ /// "identifier": "77:77:77:77:77:77:77:77"
+ /// }
+ /// }
+ /// @param command "lease4-get" or "lease6-get"
+ /// @param args must contain host reservation definition.
+ /// @return result of the operation (includes lease details, if found)
+ static ConstElementPtr
+ leaseGetHandler(const string& command, ConstElementPtr args);
+
+ /// @brief lease4-del command handler
+ ///
+ /// This command attempts to delete an IPv4 lease that match selected
+ /// criteria. Two types of parameters are supported: (subnet-id, address) or
+ /// (subnet-id, identifier-type, identifier).
+ ///
+ /// Example command for deletion by (subnet-id, address):
+ /// {
+ /// "command": "lease4-del",
+ /// "arguments": {
+ /// "subnet-id": 1,
+ /// "ip-address": "192.0.2.202"
+ /// }
+ /// }
+ ///
+ /// Example command for deletion by (subnet-id, identifier-type, identifier)
+ /// {
+ /// "command": "lease4-del",
+ /// "arguments": {
+ /// "subnet-id": 1,
+ /// "identifier-type": "hw-address",
+ /// "identifier": "00:01:02:03:04:05"
+ /// }
+ /// }";
+ /// @param command should be 'lease4-del' (but it's ignored)
+ /// @param args must contain host reservation definition.
+ /// @return result of the operation (host will be included as parameters, if found)
+ static ConstElementPtr
+ lease4DelHandler(const string& command, ConstElementPtr args);
+
+ /// @brief lease6-del command handler
+ ///
+ /// This command attempts to delete a lease that match selected criteria.
+ /// Two types of parameters are supported: (subnet-id, address) or
+ /// (subnet-id, type, iaid, identifier-type, identifier).
+ ///
+ /// Example command for deletion by (subnet-id, address):
+ /// {
+ /// "command": "lease6-del",
+ /// "arguments": {
+ /// "subnet-id": 1,
+ /// "ip-address": "192.0.2.202"
+ /// }
+ /// }
+ ///
+ /// Example command for deletion by (subnet-id, type, iaid, identifier-type,
+ /// identifier):
+ /// {
+ /// "command": "lease6-del",
+ /// "arguments": {
+ /// "subnet-id": 1,
+ /// "type": "IA_NA",
+ /// "iaid": 123456,
+ /// "identifier-type": "hw-address",
+ /// "identifier": "00:01:02:03:04:05"
+ /// }
+ /// }";
+ /// @param command should be 'lease6-del' (but it's ignored)
+ /// @param args must contain host reservation definition.
+ /// @return result of the operation (host will be included as parameters, if found)
+ static ConstElementPtr
+ lease6DelHandler(const string& command, ConstElementPtr args);
+
+ /// @brief lease4-update handler
+ ///
+ /// This command attempts to update existing IPv4 lease. The parameters
+ /// specified will replace existing lease. The only condition is that
+ /// the IP address must not change. If you want to change the IP address,
+ /// please use lease4-del and lease4-add instead.
+ ///
+ /// Example command:
+ /// {
+ /// "command": "lease4-update",
+ /// "arguments": {
+ /// "subnet-id": 44,
+ /// "ip-address": "192.0.2.1",
+ /// "hw-address": "1a:1b:1c:1d:1e:1f",
+ /// "hostname": "newhostname.example.org"
+ /// }
+ /// };
+ ///
+ /// @param command - should be "lease4-update", but it is ignored
+ /// @param args arguments that describe the lease being updated.
+ static ConstElementPtr
+ lease4UpdateHandler(const string& command, ConstElementPtr args);
+
+ /// @brief lease6-update handler
+ ///
+ /// This command attempts to update existing IPv6 lease. The parameters
+ /// specified will replace existing lease. The only condition is that
+ /// the IP address must not change. If you want to change the IP address,
+ /// please use lease6-del and lease6-add instead.
+ ///
+ /// Example command:
+ /// {
+ /// "command": "lease6-update",
+ /// "arguments": {
+ /// "subnet-id": 66,
+ /// "ip-address": "2001:db8::1",
+ /// "iaid": 7654321,
+ /// "duid": "88:88:88:88:88:88:88:88",
+ /// "hostname": "newhostname.example.org"
+ /// }
+ /// }";
+ ///
+ /// @param command - should be "lease6-update" (but it is ignored)
+ /// @param args arguments that describe the lease being updated.
+ static ConstElementPtr
+ lease6UpdateHandler(const string& command, ConstElementPtr args);
+
+ /// @brief lease4-wipe handler
+ ///
+ /// This commands attempts to remove all IPv4 leases from a specific
+ /// subnet. Currently the leases are removed from the database,
+ /// without any processing (like calling hooks or doing DDNS
+ /// cleanups).
+ ///
+ /// Example command:
+ /// {\n"
+ /// "command": "lease4-wipe",\n"
+ /// "arguments": {"
+ /// "subnet-id": 44
+ /// }\n"
+ /// }";
+ /// @param command - should be "lease4-wipe" (but is ignored)
+ /// @param args arguments that describe the lease being updated.
+ static ConstElementPtr
+ lease4WipeHandler(const string& command, ConstElementPtr args);
+
+ /// @brief lease6-wipe handler
+ ///
+ /// This commands attempts to remove all IPv4 leases from a specific
+ /// subnet. Currently the leases are removed from the database,
+ /// without any processing (like calling hooks or doing DDNS
+ /// cleanups).
+ ///
+ /// Example command:
+ /// {\n"
+ /// "command": "lease4-wipe",\n"
+ /// "arguments": {"
+ /// "subnet-id": 44
+ /// }\n"
+ /// }";
+ /// @param command - should be "lease4-wipe" (but is ignored)
+ /// @param args arguments that describe the lease being updated.
+ static ConstElementPtr
+ lease6WipeHandler(const string& command, ConstElementPtr args);
+
+ /// @brief Extracts parameters required for reservation-get and reservation-del
+ ///
+ /// See @ref Parameters class for detailed description of what is expected
+ /// in the args structure.
+ ///
+ /// @param v6 whether addresses allowed are v4 (false) or v6 (true)
+ /// @param args arguments passed to command
+ /// @return parsed parameters
+ /// @throw BadValue if input arguments don't make sense.
+ static Parameters getParameters(bool v6, const ConstElementPtr& args);
+};
+
+LeaseCmdsImpl::LeaseCmdsImpl() {
+ registerCommands();
+}
+
+LeaseCmdsImpl::~LeaseCmdsImpl() {
+ deregisterCommands();
+}
+
+void LeaseCmdsImpl::registerCommands() {
+ /// @todo: Use registration mechanism once #5314 is merged.
+ /// See #5321 discussion.
+ CommandMgr::instance().registerCommand("lease4-add",
+ boost::bind(&LeaseCmdsImpl::leaseAddHandler, _1, _2));
+ CommandMgr::instance().registerCommand("lease6-add",
+ boost::bind(&LeaseCmdsImpl::leaseAddHandler, _1, _2));
+
+ CommandMgr::instance().registerCommand("lease4-get",
+ boost::bind(&LeaseCmdsImpl::leaseGetHandler, _1, _2));
+ CommandMgr::instance().registerCommand("lease6-get",
+ boost::bind(&LeaseCmdsImpl::leaseGetHandler, _1, _2));
+
+ CommandMgr::instance().registerCommand("lease4-del",
+ boost::bind(&LeaseCmdsImpl::lease4DelHandler, _1, _2));
+ CommandMgr::instance().registerCommand("lease6-del",
+ boost::bind(&LeaseCmdsImpl::lease6DelHandler, _1, _2));
+
+ CommandMgr::instance().registerCommand("lease4-update",
+ boost::bind(&LeaseCmdsImpl::lease4UpdateHandler, _1, _2));
+ CommandMgr::instance().registerCommand("lease6-update",
+ boost::bind(&LeaseCmdsImpl::lease6UpdateHandler, _1, _2));
+
+ CommandMgr::instance().registerCommand("lease4-wipe",
+ boost::bind(&LeaseCmdsImpl::lease4WipeHandler, _1, _2));
+ CommandMgr::instance().registerCommand("lease6-wipe",
+ boost::bind(&LeaseCmdsImpl::lease6WipeHandler, _1, _2));
+}
+
+void LeaseCmdsImpl::deregisterCommands() {
+ /// @todo: Use deregistration mechanism once #5321 discussion is done
+ CommandMgr::instance().deregisterCommand("lease4-add");
+ CommandMgr::instance().deregisterCommand("lease6-add");
+
+ CommandMgr::instance().deregisterCommand("lease4-get");
+ CommandMgr::instance().deregisterCommand("lease6-get");
+
+ CommandMgr::instance().deregisterCommand("lease4-del");
+ CommandMgr::instance().deregisterCommand("lease6-del");
+
+ CommandMgr::instance().deregisterCommand("lease4-update");
+ CommandMgr::instance().deregisterCommand("lease6-update");
+
+ CommandMgr::instance().deregisterCommand("lease4-wipe");
+ CommandMgr::instance().deregisterCommand("lease6-wipe");
+}
+
+ConstElementPtr
+LeaseCmdsImpl::leaseAddHandler(const std::string& name,
+ ConstElementPtr params) {
+ bool v4 = (name == "lease4-add");
+
+ string txt = "(missing parameters)";
+ if (params) {
+ txt = params->str();
+ }
+
+ try {
+ if (!params) {
+ isc_throw(isc::BadValue, "no parameters specified for the command");
+ }
+
+ ConstSrvConfigPtr config = CfgMgr::instance().getCurrentCfg();
+
+ Lease4Ptr lease4;
+ Lease6Ptr lease6;
+ if (v4) {
+ Lease4Parser parser;
+ lease4 = parser.parse(config, params);
+
+ // checkLeaseIntegrity(config, lease4);
+
+ if (lease4) {
+ LeaseMgrFactory::instance().addLease(lease4);
+ }
+
+ } else {
+ Lease6Parser parser;
+ lease6 = parser.parse(config, params);
+
+ // checkLeaseIntegrity(config, lease6);
+
+ if (lease6) {
+ LeaseMgrFactory::instance().addLease(lease6);
+ }
+ }
+
+
+ } catch (const std::exception& ex) {
+ LOG_ERROR(lease_cmds_logger, v4 ? LEASE_CMDS_ADD4_FAILED : LEASE_CMDS_ADD6_FAILED)
+ .arg(txt)
+ .arg(ex.what());
+ return (createAnswer(CONTROL_RESULT_ERROR, ex.what()));
+ }
+ LOG_INFO(lease_cmds_logger,
+ v4 ? LEASE_CMDS_ADD4 : LEASE_CMDS_ADD6).arg(txt);
+ return (createAnswer(CONTROL_RESULT_SUCCESS, "Lease added."));
+}
+
+LeaseCmdsImpl::Parameters
+LeaseCmdsImpl::getParameters(bool v6, const ConstElementPtr& params) {
+ Parameters x;
+
+ if (!params || params->getType() != Element::map) {
+ isc_throw(BadValue, "Parameters missing or are not a map.");
+ }
+
+ // We support several sets of parameters for leaseX-get/lease-del:
+ // lease-get(type, address)
+ // lease-get(type, subnet-id, identifier-type, identifier)
+
+ if (params->contains("type")) {
+ string t = params->get("type")->stringValue();
+ if (t == "IA_NA" || t == "0") {
+ x.lease_type = Lease::TYPE_NA;
+ } else if (t == "IA_TA" || t == "1") {
+ x.lease_type = Lease::TYPE_TA;
+ } else if (t == "IA_PD" || t == "2") {
+ x.lease_type = Lease::TYPE_PD;
+ } else if (t == "V4" || t == "3") {
+ x.lease_type = Lease::TYPE_V4;
+ } else {
+ isc_throw(BadValue, "Invalid lease type specified: "
+ << t << ", only supported values are: IA_NA, IA_TA,"
+ << " IA_PD and V4");
+ }
+ }
+
+ ConstElementPtr tmp = params->get("ip-address");
+ if (tmp) {
+ if (tmp->getType() != Element::string) {
+ isc_throw(BadValue, "'ip-address' is not a string.");
+ }
+
+ x.addr = IOAddress(tmp->stringValue());
+
+ if ((v6 && !x.addr.isV6()) || (!v6 && !x.addr.isV4())) {
+ stringstream txt;
+ txt << "Invalid " << (v6 ? "IPv6" : "IPv4")
+ << " address specified: " << tmp->stringValue();
+ isc_throw(BadValue, txt.str());
+ }
+
+ x.query_type = Parameters::TYPE_ADDR;
+ return (x);
+ }
+
+ tmp = params->get("subnet-id");
+ if (!tmp) {
+ isc_throw(BadValue, "Mandatory 'subnet-id' parameter missing.");
+ }
+ if (tmp->getType() != Element::integer) {
+ isc_throw(BadValue, "'subnet-id' parameter is not integer.");
+ }
+ x.subnet_id = tmp->intValue();
+
+ if (params->contains("iaid")) {
+ x.iaid = params->get("iaid")->intValue();
+ }
+
+ // No address specified. Ok, so it must be identifier based query.
+ // "identifier-type": "duid",
+ // "identifier": "aa:bb:cc:dd:ee:..."
+
+ ConstElementPtr type = params->get("identifier-type");
+ ConstElementPtr ident = params->get("identifier");
+ if (!type || type->getType() != Element::string) {
+ isc_throw(BadValue, "No 'ip-address' provided"
+ " and 'identifier-type' is either missing or not a string.");
+ }
+ if (!ident || ident->getType() != Element::string) {
+ isc_throw(BadValue, "No 'ip-address' provided"
+ " and 'identifier' is either missing or not a string.");
+ }
+
+ // Got the parameters. Let's see if their values make sense.
+ // Try to convert identifier-type
+ x.query_type = Parameters::txtToType(type->stringValue());
+
+ switch (x.query_type) {
+ case Parameters::TYPE_HWADDR: {
+ HWAddr hw = HWAddr::fromText(ident->stringValue());
+ x.hwaddr = HWAddrPtr(new HWAddr(hw));
+ break;
+ }
+ case Parameters::TYPE_DUID: {
+ DUID duid = DUID::fromText(ident->stringValue());
+ x.duid = DuidPtr(new DUID(duid));
+ break;
+ }
+ case Parameters::TYPE_ADDR: {
+ // We should never get here. The address clause should have been caught
+ // earlier.
+ return (x);
+ }
+ default: {
+ isc_throw(BadValue, "Identifier type " << type->stringValue() <<
+ " is not supported.");
+ }
+ }
+ return (x);
+}
+
+ConstElementPtr
+LeaseCmdsImpl::leaseGetHandler(const std::string& name, ConstElementPtr params) {
+ Parameters p;
+ Lease4Ptr lease4;
+ Lease6Ptr lease6;
+ bool v4 = (name == "lease4-get");
+ try {
+ p = getParameters(!v4, params);
+
+ switch (p.query_type) {
+ case Parameters::TYPE_ADDR: {
+ // Query by address
+ if (v4) {
+ lease4 = LeaseMgrFactory::instance().getLease4(p.addr);
+ } else {
+ lease6 = LeaseMgrFactory::instance().getLease6(p.lease_type, p.addr);
+ }
+ break;
+ }
+ case Parameters::TYPE_HWADDR:
+ if (v4) {
+ if (!p.hwaddr) {
+ return (createAnswer(CONTROL_RESULT_ERROR,
+ "Program error: Query by hw-address "
+ "requires hwaddr to be specified"));
+ }
+ lease4 = LeaseMgrFactory::instance().getLease4(*p.hwaddr, p.subnet_id);
+ } else {
+ return (createAnswer(CONTROL_RESULT_ERROR,
+ "Query by hw-address is not allowed in v6."));
+ }
+ break;
+ case Parameters::TYPE_DUID:
+ if (!v4) {
+ if (!p.duid) {
+ return (createAnswer(CONTROL_RESULT_ERROR,
+ "Program error: Query by duid "
+ "requires duid to be specified"));
+ }
+ lease6 = LeaseMgrFactory::instance().getLease6(p.lease_type, *p.duid,
+ p.iaid, p.subnet_id);
+ } else {
+ return (createAnswer(CONTROL_RESULT_ERROR,
+ "Query by duid is not allowed in v4."));
+ }
+ break;
+ default: {
+ stringstream tmp;
+ tmp << "Unknown query type: " << static_cast<int>(p.query_type);
+ return (createAnswer(CONTROL_RESULT_ERROR, tmp.str()));
+ }
+ }
+ } catch (const std::exception& ex) {
+ return (createAnswer(CONTROL_RESULT_ERROR, ex.what()));
+ }
+
+ ElementPtr lease_json;
+ if (v4 && lease4) {
+ lease_json = lease4->toElement();
+ return (createAnswer(CONTROL_RESULT_SUCCESS, "IPv4 lease found.", lease_json));
+ }
+ if (!v4 && lease6) {
+ lease_json = lease6->toElement();
+ return (createAnswer(CONTROL_RESULT_SUCCESS, "IPv6 lease found.", lease_json));
+
+ }
+
+ // If we got here, the lease has not been found.
+ return (createAnswer(CONTROL_RESULT_EMPTY, "Lease not found."));
+}
+
+ConstElementPtr
+LeaseCmdsImpl::lease4DelHandler(const std::string& , ConstElementPtr params) {
+ Parameters p;
+ Lease4Ptr lease4;
+ IOAddress addr(IOAddress::IPV4_ZERO_ADDRESS());
+ try {
+ p = getParameters(false, params);
+
+ switch (p.query_type) {
+ case Parameters::TYPE_ADDR: {
+
+ // If address was specified explicitly, let's use it as is.
+ addr = p.addr;
+ break;
+ }
+ case Parameters::TYPE_HWADDR:
+ if (!p.hwaddr) {
+ return (createAnswer(CONTROL_RESULT_ERROR,
+ "Program error: Query by hw-address "
+ "requires hwaddr to be specified"));
+ }
+
+ // Let's see if there's such a lease at all.
+ lease4 = LeaseMgrFactory::instance().getLease4(*p.hwaddr, p.subnet_id);
+ if (!lease4) {
+ return (createAnswer(CONTROL_RESULT_EMPTY, "IPv4 lease not found."));
+ }
+
+ // Found it, can use it as is.
+ addr = lease4->addr_;
+ break;
+
+ case Parameters::TYPE_DUID:
+ return (createAnswer(CONTROL_RESULT_ERROR,
+ "Delete by duid is not allowed in v4."));
+ break;
+
+ default: {
+ stringstream tmp;
+ tmp << "Unknown query type: " << static_cast<int>(p.query_type);
+ return (createAnswer(CONTROL_RESULT_ERROR, tmp.str()));
+ }
+ }
+
+ if (LeaseMgrFactory::instance().deleteLease(addr)) {
+ return (createAnswer(CONTROL_RESULT_SUCCESS, "IPv4 lease deleted."));
+ } else {
+ return (createAnswer(CONTROL_RESULT_EMPTY, "IPv4 lease not found."));
+ }
+ } catch (const std::exception& ex) {
+ return (createAnswer(CONTROL_RESULT_ERROR, ex.what()));
+ }
+}
+
+ConstElementPtr
+LeaseCmdsImpl::lease6DelHandler(const std::string& , ConstElementPtr params) {
+ Parameters p;
+ Lease6Ptr lease6;
+ IOAddress addr(IOAddress::IPV6_ZERO_ADDRESS());
+ try {
+ p = getParameters(true, params);
+
+ switch (p.query_type) {
+ case Parameters::TYPE_ADDR: {
+
+ // If address was specified explicitly, let's use it as is.
+ addr = p.addr;
+ break;
+ }
+ case Parameters::TYPE_HWADDR:
+ return (createAnswer(CONTROL_RESULT_ERROR,
+ "Delete by hw-address is not allowed in v6."));
+
+ case Parameters::TYPE_DUID:
+ if (!p.duid) {
+ return (createAnswer(CONTROL_RESULT_ERROR,
+ "Program error: Query by duid "
+ "requires duid to be specified"));
+ }
+
+ // Let's see if there's such a lease at all.
+ lease6 = LeaseMgrFactory::instance().getLease6(p.lease_type, *p.duid,
+ p.iaid, p.subnet_id);
+ if (!lease6) {
+ return (createAnswer(CONTROL_RESULT_EMPTY, "IPv6 lease not found."));
+ }
+
+ // Found it, can use it as is.
+ addr = lease6->addr_;
+ break;
+
+ default: {
+ stringstream tmp;
+ tmp << "Unknown query type: " << static_cast<int>(p.query_type);
+ return (createAnswer(CONTROL_RESULT_ERROR, tmp.str()));
+ }
+ }
+
+ if (LeaseMgrFactory::instance().deleteLease(addr)) {
+ return (createAnswer(CONTROL_RESULT_SUCCESS, "IPv6 lease deleted."));
+ } else {
+ return (createAnswer(CONTROL_RESULT_EMPTY, "IPv6 lease not found."));
+ }
+ } catch (const std::exception& ex) {
+ return (createAnswer(CONTROL_RESULT_ERROR, ex.what()));
+ }
+}
+
+ConstElementPtr
+LeaseCmdsImpl::lease4UpdateHandler(const string& , ConstElementPtr params) {
+ try {
+
+ // We need the lease to be specified.
+ if (!params) {
+ isc_throw(isc::BadValue, "no parameters specified for lease4-update command");
+ }
+
+ // Get the parameters specified by the user first.
+ ConstSrvConfigPtr config = CfgMgr::instance().getCurrentCfg();
+ Lease4Ptr lease4;
+ Lease4Parser parser;
+ // The parser does sanity checks (if the address is in scope, if
+ // subnet-id is valid, etc)
+ lease4 = parser.parse(config, params);
+
+ LeaseMgrFactory::instance().updateLease4(lease4);
+ return (createAnswer(CONTROL_RESULT_SUCCESS, "IPv4 lease updated."));
+
+ } catch (const std::exception& ex) {
+ return (createAnswer(CONTROL_RESULT_ERROR, ex.what()));
+ }
+}
+
+ConstElementPtr
+LeaseCmdsImpl::lease6UpdateHandler(const string& , ConstElementPtr params) {
+ try {
+ // We need the lease to be specified.
+ if (!params) {
+ isc_throw(isc::BadValue, "no parameters specified for lease6-update command");
+ }
+
+ // Get the parameters specified by the user first.
+ ConstSrvConfigPtr config = CfgMgr::instance().getCurrentCfg();
+ Lease6Ptr lease6;
+ Lease6Parser parser;
+ // The parser does sanity checks (if the address is in scope, if
+ // subnet-id is valid, etc)
+ lease6 = parser.parse(config, params);
+
+ LeaseMgrFactory::instance().updateLease6(lease6);
+ return (createAnswer(CONTROL_RESULT_SUCCESS, "IPv6 lease updated."));
+
+ } catch (const std::exception& ex) {
+ return (createAnswer(CONTROL_RESULT_ERROR, ex.what()));
+ }
+}
+
+ConstElementPtr
+LeaseCmdsImpl::lease4WipeHandler(const string& /*cmd*/, ConstElementPtr params) {
+ try {
+
+ // The subnet-id is a mandatory parameter.
+ if (!params) {
+ isc_throw(isc::BadValue, "no parameters specified for lease4-wipe command");
+ }
+ SimpleParser parser;
+ SubnetID id = parser.getUint32(params, "subnet-id");
+
+ size_t num = LeaseMgrFactory::instance().wipeLeases4(id);
+
+ stringstream tmp;
+ tmp << "Deleted " << num << " IPv4 lease(s).";
+ return (createAnswer(num ? CONTROL_RESULT_SUCCESS : CONTROL_RESULT_EMPTY,
+ tmp.str()));
+ } catch (const std::exception& ex) {
+ return (createAnswer(CONTROL_RESULT_ERROR, ex.what()));
+ }
+}
+
+ConstElementPtr
+LeaseCmdsImpl::lease6WipeHandler(const string& /*cmd*/, ConstElementPtr params) {
+ try {
+
+ // The subnet-id is a mandatory parameter.
+ if (!params) {
+ isc_throw(isc::BadValue, "no parameters specified for lease6-wipe command");
+ }
+ SimpleParser parser;
+ SubnetID id = parser.getUint32(params, "subnet-id");
+
+ size_t num = LeaseMgrFactory::instance().wipeLeases6(id);
+
+ stringstream tmp;
+ tmp << "Deleted " << num << " IPv6 lease(s).";
+ return (createAnswer(num ? CONTROL_RESULT_SUCCESS : CONTROL_RESULT_EMPTY,
+ tmp.str()));
+ } catch (const std::exception& ex) {
+ return (createAnswer(CONTROL_RESULT_ERROR, ex.what()));
+ }
+}
+
+LeaseCmds::LeaseCmds()
+ :impl_(new LeaseCmdsImpl()) {
+}
+
+LeaseCmds::~LeaseCmds() {
+ impl_.reset();
+}
+
+};
+};
diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds.dox b/src/hooks/dhcp/lease_cmds/lease_cmds.dox
new file mode 100644
index 0000000000..761c65f491
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/lease_cmds.dox
@@ -0,0 +1,96 @@
+// Copyright (C) 2017 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/.
+
+/**
+
+@mainpage Kea Lease Commands Hooks Library
+
+Welcome to Kea Lease Commands Hooks Library. This documentation is addressed to
+developers who are interested in the internal operation of the Lease Commands
+library. This file provides information needed to understand and perhaps extend
+this library.
+
+This documentation is stand-alone: you should have read and understood <a
+href="http://kea.isc.org/docs/devel/">Kea Developer's Guide</a> and in
+particular its section about hooks.
+
+@section lease_cmds Lease Commands Overview
+
+Lease Commands (or lease_cmds) is a Hook library that can be loaded by Kea to
+extend it with additional mechanisms.
+
+The primary purpose of this library is to provide commands that manage leases.
+As such, the whole library is structured around command handlers. When the
+library is loaded it registers a number of handlers for new commands. When a
+command is issued (be it directly via control channel or indirectly via REST
+interface from control agent), the code receives a JSON command with
+parameters. Those are parsed and then actual operation commences. This
+operation always interacts with an instantiation of isc::dhcp::LeaseMgr
+instance, which is Kea's way of storing leases. At the time of writing this text
+(Aug. 2017), Kea supports four types of lease managers: memfile, MySQL,
+PostgreSQL or Cassandra. The lease commands provided by this library
+provide a unified interface for those backends.
+
+As with other hooks, this one also keeps its code in a separate namespace which
+corresponds to the file name of the library: isc::lease_cmds.
+
+@section lease_cmdsCode Lease Commands Code Overview
+
+The library operation starts with Kea calling the load() function (file
+load_unload.cc). It instantiates an isc::lease_cmds::LeaseCmds object.
+The constructor of that object registers all of the lease commands. For a list,
+see @ref isc::lease_cmds::LeaseCmds class documentation. This class uses Pimpl
+design pattern, thus the real implementation is hidden in isc::lease_cmds::LeaseCmdsImpl.
+
+Almost every command has its own handler, except few that share the same handler
+between v4 and v6 due to its similarity. For example
+isc::lease_cmds::LeaseCmdsImpl::leaseAddHandler handles lease4-add and lease6-add
+commands. Although the details differ between handlers, the general approach
+is the same. First, it starts with parameters sanitization and then some
+interaction with isc::dhcp::LeaseMgr is conducted.
+
+For commands that do something with a specific lease (lease4-get, lease6-get,
+lease4-del, lease6-del), there is a @ref isc::lease_cmds::Parameters class that
+contains parsed elements.
+
+For details see documentation and code of the following handlers:
+- @ref isc::lease_cmds::LeaseCmdsImpl::leaseAddHandler (lease4-add, lease6-add)
+- @ref isc::lease_cmds::LeaseCmdsImpl::leaseGetHandler (lease4-get, lease6-get)
+- @ref isc::lease_cmds::LeaseCmdsImpl::lease4DelHandler (lease4-del)
+- @ref isc::lease_cmds::LeaseCmdsImpl::lease6DelHandler (lease6-del)
+- @ref isc::lease_cmds::LeaseCmdsImpl::lease4UpdateHandler (lease4-update)
+- @ref isc::lease_cmds::LeaseCmdsImpl::lease6UpdateHandler (lease6-update)
+- @ref isc::lease_cmds::LeaseCmdsImpl::lease4WipeHandler (lease4-wipe)
+- @ref isc::lease_cmds::LeaseCmdsImpl::lease6WipeHandler (lease6-wipe)
+
+@section lease_cmdsDesigns Lease Commands Design choices
+
+The lease manipulation commands were implemented to provide a convenient interface
+for sysadmins. The primary goal was to offer a way to interact with the live
+lease database in unified way, regardless of the actual backend being used.
+
+For some backends (MySQL, PostgreSQL and Cassandra) it is possible to interact
+directly with the backend while Kea is running and possibly change its content. This
+ability is both powerful and dangerous. In particular, only rudimentary
+checks are enforced by the DB schemas (e.g. not possible to have two leases
+for the same address). However, it does not prevent sysadmins from making
+more obscure errors, like inserting leases for subnets that do not exist
+or configuring an address that is topologically outside of the subnet to which
+it should belong. These kind of checks are only possible by DHCP-aware
+code, which this library provides.
+
+Some of the queries may require a seemingly odd set of parameters. For example,
+lease6-get query requires at least DUID, subnet-id and IAID to retrieve a lease
+by DUID. The need for a sysadmin to know and specify an IAID is troublesome.
+However, the guiding principle here was to use whatever queries were already
+exposed by the lease manager and not introduce new indexes, unless absolutely
+necessary. This ensures that there is no performance degradation when the
+library is loaded. The only exception for that was lease4-wipe and lease6-wipe
+commands that remove all leases from specific subnet. As there were no
+queries that could retrieve or otherwise enumerate leases for a specific subnet,
+a new query type and a new index had to be added.
+
+*/
diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds.h b/src/hooks/dhcp/lease_cmds/lease_cmds.h
new file mode 100644
index 0000000000..57d2db5a40
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/lease_cmds.h
@@ -0,0 +1,67 @@
+// Copyright (C) 2017 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 LEASE_CMDS_H
+#define LEASE_CMDS_H
+
+#include <boost/shared_ptr.hpp>
+
+namespace isc {
+namespace lease_cmds {
+
+/// @brief Forward declaration of implementation class.
+class LeaseCmdsImpl;
+
+/// @brief A wrapper class that provides convenient initialization to the library.
+///
+/// This is a wrapper class that simply registers extra commands when
+/// instantiated and deregisters them when the instance is destroyed.
+///
+/// For an actual implementation, see @ref LeaseCmdsImpl class in lease_cmds.cc file.
+class LeaseCmds {
+public:
+
+ /// @brief Initializes additional lease commands.
+ ///
+ /// It registers the following commands:
+ /// - lease4-add
+ /// - lease6-add
+ /// - lease4-get
+ /// - lease6-get
+ /// - lease4-del
+ /// - lease6-del
+ /// - lease4-update
+ /// - lease6-update
+ /// - lease4-del-all
+ /// - lease6-del-all
+ ///
+ /// @throw Unexpected If any of the above fails.
+ LeaseCmds();
+
+ /// @brief Destructor
+ ///
+ /// Unregisters commands:
+ /// - lease4-add
+ /// - lease6-add
+ /// - lease4-get
+ /// - lease6-get
+ /// - lease4-del
+ /// - lease6-del
+ /// - lease4-update
+ /// - lease6-update
+ /// - lease4-del-all
+ /// - lease6-del-all
+ ~LeaseCmds();
+private:
+
+ /// Pointer to the actual implementation
+ boost::shared_ptr<LeaseCmdsImpl> impl_;
+};
+
+};
+};
+
+#endif
diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds_log.cc b/src/hooks/dhcp/lease_cmds/lease_cmds_log.cc
new file mode 100644
index 0000000000..8215a6effa
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/lease_cmds_log.cc
@@ -0,0 +1,16 @@
+// Copyright (C) 2017 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 <lease_cmds_log.h>
+
+namespace isc {
+namespace lease_cmds {
+
+isc::log::Logger lease_cmds_logger("lease_cmds_hooks");
+
+}
+}
+
diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds_log.h b/src/hooks/dhcp/lease_cmds/lease_cmds_log.h
new file mode 100644
index 0000000000..56172b0feb
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/lease_cmds_log.h
@@ -0,0 +1,23 @@
+// Copyright (C) 2017 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 LEASE_CMD_LOG_H
+#define LEASE_CMD_LOG_H
+
+#include <log/logger_support.h>
+#include <log/macros.h>
+#include <lease_cmds_messages.h>
+
+namespace isc {
+namespace lease_cmds {
+
+extern isc::log::Logger lease_cmds_logger;
+
+} // end of isc::lease_cmds
+} // end of isc namespace
+
+
+#endif
diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds_messages.mes b/src/hooks/dhcp/lease_cmds/lease_cmds_messages.mes
new file mode 100644
index 0000000000..9f5b5b019e
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/lease_cmds_messages.mes
@@ -0,0 +1,51 @@
+# Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+
+% LEASE_CMDS_INIT_FAILED loading Lease Commands hooks library failed: %1
+This error message indicates an error during loading the Lease Commands
+hooks library. The details of the error are provided as argument of
+the log message.
+
+% LEASE_CMDS_INIT_OK loading Lease Commands hooks library successful
+This info message indicates that the Lease Commands hooks library has been
+loaded successfully. Enjoy!
+
+% LEASE_CMDS_DEINIT_FAILED unloading Lease Commands hooks library failed: %1
+This error message indicates an error during unloading the Lease Commands
+hooks library. The details of the error are provided as argument of
+the log message.
+
+% LEASE_CMDS_DEINIT_OK unloading Lease Commands hooks library successful
+This info message indicates that the Lease Commands hooks library has been
+removed successfully.
+
+% LEASE_CMDS_ADD4_FAILED lease4-add command failed (parameters: %1, reason: %2)
+The lease4-add command has failed. Both the reason as well as the
+parameters passed are logged.
+
+% LEASE_CMDS_ADD6_FAILED Lease6-add command failed (parameters: %1, reason: %2)
+The lease6-add command has failed. Both the reason as well as the
+parameters passed are logged.
+
+% LEASE_CMDS_ADD4 lease4-add command successful (parameters: %1)
+The lease4-add command has been successful. Parameters of the host
+added are logged.
+
+% LEASE_CMDS_ADD6 lease6-add command successful (parameters: %1)
+The lease6-add command has been successful. Parameters of the host
+added are logged.
+
+% LEASE_CMDS_DEL4_FAILED lease4-del command failed (parameters: %1, reason: %2)
+The attempt to delete an IPv4 lease (lease4-del command) has failed. Both the
+reason as well as the parameters passed are logged.
+
+% LEASE_CMDS_DEL6_FAILED lease6-del command failed (parameters: %1, reason: %2)
+The attempt to delete an IPv6 lease (lease4-del command) has failed. Both the
+reason as well as the parameters passed are logged.
+
+% LEASE_CMDS_DEL4 lease4-del command successful (parameters: %1)
+The attempt to delete an IPv4 lease (lease4-del command) has been successful.
+Parameters of the host removed are logged.
+
+% LEASE_CMDS_DEL6 lease4-del command successful (parameters: %1)
+The attempt to delete an IPv4 lease (lease4-del command) has been successful.
+Parameters of the host removed are logged.
diff --git a/src/hooks/dhcp/lease_cmds/lease_parser.cc b/src/hooks/dhcp/lease_cmds/lease_parser.cc
new file mode 100644
index 0000000000..b0288002d2
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/lease_parser.cc
@@ -0,0 +1,257 @@
+// Copyright (C) 2017 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 <cc/data.h>
+#include <dhcp/hwaddr.h>
+#include <asiolink/io_address.h>
+#include <dhcpsrv/lease.h>
+#include <lease_parser.h>
+
+#include <config.h>
+
+using namespace std;
+using namespace isc::dhcp;
+using namespace isc::data;
+using namespace isc::asiolink;
+
+namespace isc {
+namespace lease_cmds {
+
+Lease4Ptr
+Lease4Parser::parse(ConstSrvConfigPtr& cfg,
+ const ConstElementPtr& lease_info) {
+ if (!lease_info) {
+ isc_throw(BadValue, "lease information missing");
+ }
+
+ // These are mandatory parameters.
+ IOAddress addr = getAddress(lease_info, "ip-address");
+ SubnetID subnet_id = getUint32(lease_info, "subnet-id");
+
+ if (!addr.isV4()) {
+ isc_throw(BadValue, "Non-IPv4 address specified: " << addr);
+ }
+
+ // Not a most straightforward conversion, but it works.
+ string hwaddr_txt = getString(lease_info, "hw-address");
+ HWAddr hwaddr = HWAddr::fromText(hwaddr_txt);
+ HWAddrPtr hwaddr_ptr = HWAddrPtr(new HWAddr(hwaddr));
+
+ Subnet4Ptr subnet = cfg->getCfgSubnets4()->getSubnet(subnet_id);
+ if (!subnet) {
+ isc_throw(BadValue, "Invalid subnet-id: No IPv4 subnet with subnet-id="
+ << subnet_id << " currently configured.");
+ }
+
+ if (!subnet->inRange(addr)) {
+ isc_throw(BadValue, "The address " << addr.toText() << " does not belong "
+ "to subnet " << subnet->toText() << ", subnet-id=" << subnet_id);
+ }
+
+ // Client-id is optional.
+ ClientIdPtr client_id;
+ if (lease_info->contains("client-id")) {
+ string txt = getString(lease_info, "client-id");
+ client_id = ClientId::fromText(txt);
+ }
+
+ // These parameters are optional. If not specified, we'll derive them from
+ // the current subnet configuration, if possible.
+ uint32_t valid_lft = 0;
+ if (lease_info->contains("valid-lft")) {
+ valid_lft = getUint32(lease_info, "valid-lft");
+ } else {
+ valid_lft = subnet->getValid();
+ }
+
+ /// Let's calculate client last transmission time (cltt). If expiration
+ /// timestamp is specified explicitly, we will use that. Note there are no
+ /// checks whether this is in the past. There may be valid cases when user
+ /// wants to insert expired leases, e.g. when migrating from one DHCP server
+ /// to another and wants to migrate the database as is, without discarding
+ /// any leases.
+ time_t cltt;
+ if (lease_info->contains("expire")) {
+ int64_t tmp = getUint32(lease_info, "expire");
+ cltt = static_cast<time_t>(tmp - valid_lft);
+ } else {
+ cltt = time(NULL);
+ }
+
+ bool fqdn_fwd = false;
+ if (lease_info->contains("fqdn-fwd")) {
+ fqdn_fwd = getBoolean(lease_info, "fqdn-fwd");
+ }
+ bool fqdn_rev = false;
+ if (lease_info->contains("fqdn-rev")) {
+ fqdn_rev = getBoolean(lease_info, "fqdn-rev");
+ }
+ string hostname;
+ if (lease_info->contains("hostname")) {
+ hostname = getString(lease_info, "hostname");
+ }
+ if (hostname.empty() && (fqdn_fwd || fqdn_rev)) {
+ isc_throw(BadValue, "No hostname specified and either forward or reverse"
+ " fqdn was set to true.");
+ }
+
+ uint32_t state = 0;
+ if (lease_info->contains("state")) {
+ state = getUint8(lease_info, "state");
+ }
+
+ // Check if the state value is sane.
+ if (state > Lease::STATE_EXPIRED_RECLAIMED) {
+ isc_throw(BadValue, "Invalid state value: " << state << ", supported "
+ "values are: 0 (default), 1 (declined) and 2 (expired-reclaimed)");
+ }
+
+ // Let's fabricate some data and we're ready to go.
+ uint32_t t1 = subnet->getT1();
+ uint32_t t2 = subnet->getT2();
+
+ Lease4Ptr l(new Lease4(addr, hwaddr_ptr, client_id, valid_lft, t1, t2,
+ cltt, subnet_id,
+ fqdn_fwd, fqdn_rev, hostname));
+ l->state_ = state;
+ return (l);
+}
+
+Lease6Ptr
+Lease6Parser::parse(ConstSrvConfigPtr& cfg,
+ const ConstElementPtr& lease_info) {
+ if (!lease_info) {
+ isc_throw(BadValue, "lease information missing");
+ }
+
+ // These are mandatory parameters.
+ IOAddress addr = getAddress(lease_info, "ip-address");
+ SubnetID subnet_id = getUint32(lease_info, "subnet-id");
+
+ if (addr.isV4()) {
+ isc_throw(BadValue, "Non-IPv6 address specified: " << addr);
+ }
+
+ // Not a most straightforward conversion, but it works.
+ string duid_txt = getString(lease_info, "duid");
+ DUID duid = DUID::fromText(duid_txt);
+ DuidPtr duid_ptr = DuidPtr(new DUID(duid));
+
+ // Check if the subnet-id specified is sane.
+ Subnet6Ptr subnet = cfg->getCfgSubnets6()->getSubnet(subnet_id);
+ if (!subnet) {
+ isc_throw(BadValue, "Invalid subnet-id: No IPv6 subnet with subnet-id="
+ << subnet_id << " currently configured.");
+ }
+
+ Lease::Type type = Lease::TYPE_NA;
+ uint8_t prefix_len = 128;
+ if (lease_info->contains("type")) {
+ string txt = getString(lease_info, "type");
+ if (txt == "IA_NA") {
+ type = Lease::TYPE_NA;
+ } else if (txt == "IA_TA") {
+ type = Lease::TYPE_TA;
+ } else if (txt == "IA_PD") {
+ type = Lease::TYPE_PD;
+
+ prefix_len = getUint8(lease_info, "prefix-len");
+ } else {
+ isc_throw(BadValue, "Incorrect lease type: " << txt << ", the only "
+ "supported values are: na, ta and pd");
+ }
+ }
+
+ // Check if the address specified really belongs to the subnet.
+ if ((type == Lease::TYPE_NA) && !subnet->inRange(addr)) {
+ isc_throw(BadValue, "The address " << addr.toText() << " does not belong "
+ "to subnet " << subnet->toText() << ", subnet-id=" << subnet_id);
+ }
+
+ uint32_t iaid = getUint32(lease_info, "iaid");
+
+ // Hw-address is optional in v6 leases.
+ HWAddrPtr hwaddr_ptr;
+ if (lease_info->contains("hw-address")) {
+ string hwaddr_txt = getString(lease_info, "hw-address");
+ HWAddr hwaddr = HWAddr::fromText(hwaddr_txt);
+ hwaddr_ptr = HWAddrPtr(new HWAddr(hwaddr));
+ }
+
+ // These parameters are optional. If not specified, we'll derive them
+ // from the current subnet configuration, if possible.
+ uint32_t valid_lft = 0;
+ if (lease_info->contains("valid-lft")) {
+ valid_lft = getUint32(lease_info, "valid-lft");
+ } else {
+ valid_lft = subnet->getValid();
+ }
+
+ // These parameters are optional. If not specified, we'll derive them
+ // from the current subnet configuration, if possible.
+ uint32_t pref_lft = 0;
+ if (lease_info->contains("preferred-lft")) {
+ pref_lft = getUint32(lease_info, "preferred-lft");
+ } else {
+ pref_lft = subnet->getValid();
+ }
+
+ /// Let's calculate client last transmission time (cltt). If expiration
+ /// timestamp is specified explicitly, we will use that. Note there are
+ /// no checks whether this is in the past. There may be valid cases when
+ /// user wants to insert expired leases, e.g. when migrating from one
+ /// DHCP server to another and wants to migrate the database as is, without
+ /// discarding any leases.
+ time_t cltt;
+ if (lease_info->contains("expire")) {
+ int64_t tmp = getUint32(lease_info, "expire");
+ cltt = static_cast<time_t>(tmp - valid_lft);
+ } else {
+ cltt = time(NULL);
+ }
+
+ bool fqdn_fwd = false;
+ if (lease_info->contains("fqdn-fwd")) {
+ fqdn_fwd = getBoolean(lease_info, "fqdn-fwd");
+ }
+ bool fqdn_rev = false;
+ if (lease_info->contains("fqdn-rev")) {
+ fqdn_rev = getBoolean(lease_info, "fqdn-rev");
+ }
+ string hostname;
+ if (lease_info->contains("hostname")) {
+ hostname = getString(lease_info, "hostname");
+ }
+ if (hostname.empty() && (fqdn_fwd || fqdn_rev)) {
+ isc_throw(BadValue, "No hostname specified and either forward or reverse"
+ " fqdn was set to true.");
+ }
+
+ uint32_t state = 0;
+ if (lease_info->contains("state")) {
+ state = getUint8(lease_info, "state");
+ }
+
+ // Check if the state value is sane.
+ if (state > Lease::STATE_EXPIRED_RECLAIMED) {
+ isc_throw(BadValue, "Invalid state value: " << state << ", supported "
+ "values are: 0 (default), 1 (declined) and 2 (expired-reclaimed)");
+ }
+
+ // Let's fabricate some data and we're ready to go.
+ uint32_t t1 = subnet->getT1();
+ uint32_t t2 = subnet->getT2();
+
+ Lease6Ptr l(new Lease6(type, addr, duid_ptr, iaid, pref_lft, valid_lft, t1, t2,
+ subnet_id, fqdn_fwd, fqdn_rev, hostname,
+ hwaddr_ptr, prefix_len));
+ l->cltt_ = cltt;
+ l->state_ = state;
+ return (l);
+}
+
+};
+};
diff --git a/src/hooks/dhcp/lease_cmds/lease_parser.h b/src/hooks/dhcp/lease_cmds/lease_parser.h
new file mode 100644
index 0000000000..40400bd94a
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/lease_parser.h
@@ -0,0 +1,92 @@
+// Copyright (C) 2017 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 LEASE_PARSER_H
+#define LEASE_PARSER_H
+
+#include <cc/data.h>
+#include <cc/simple_parser.h>
+#include <dhcpsrv/lease.h>
+#include <dhcpsrv/srv_config.h>
+
+namespace isc {
+namespace lease_cmds {
+
+/// @brief Parser for Lease4 structure
+///
+/// It expects the data in the following format:
+/// {
+/// "ip-address": "192.0.2.1",
+/// "hw-address": "00:01:02:03:04:05",
+/// "client-id": "this-is-a-client",
+/// "valid-lft": 3600,
+/// "cltt": 12345678,
+/// "expire": 1499282530,
+/// "subnet-id": 1,
+/// "fqdn-fwd": true,
+/// "fqdn-rev": true,
+/// "hostname": "myhost.example.org",
+/// "state": 0
+/// }
+class Lease4Parser : public isc::data::SimpleParser {
+public:
+
+ /// @brief Parses Element tree and tries to convert to Lease4
+ ///
+ /// See @ref Lease6Parser class description for expected format.
+ ///
+ /// @param cfg Currently running config (used for sanity checks and defaults)
+ /// @param lease_info structure to be parsed
+ /// @return A pointer to Lease4
+ /// @throw BadValue if any of the parameters is invalid
+ /// @throw DhcpConfigError if mandatory parameter is missing
+ virtual isc::dhcp::Lease4Ptr parse(isc::dhcp::ConstSrvConfigPtr& cfg,
+ const isc::data::ConstElementPtr& lease_info);
+
+ /// @brief virtual dtor (does nothing)
+ virtual ~Lease4Parser() {}
+};
+
+/// @brief Parser for Lease6 structure
+///
+/// {
+/// "address": "2001:db8::1",
+/// "duid": "00:01:02:03:04:05",
+/// "type": "IA_NA",
+/// "cltt": 12345678,
+/// "preferred-lft": 3600,
+/// "valid-lft": 3600,
+/// "expire": 1499282530,
+/// "subnet-id": 1,
+/// "fqdn-fwd": true,
+/// "fqdn-rev": true,
+/// "hostname": "myhost.example.org",
+/// "state": 0
+/// }
+
+/// It expects the input data to use the following format:
+class Lease6Parser : public isc::data::SimpleParser {
+public:
+ /// @brief Parses Element tree and tries to convert to Lease4
+ ///
+ /// See @ref Lease6Parser class description for expected format.
+ ///
+ /// @param cfg Currently running config (used for sanity checks and defaults)
+ /// @param lease_info structure to be parsed
+ /// @return A pointer to Lease4
+ /// @throw BadValue if any of the parameters is invalid
+ /// @throw DhcpConfigError if mandatory parameter is missing
+ virtual isc::dhcp::Lease6Ptr parse(isc::dhcp::ConstSrvConfigPtr& cfg,
+ const isc::data::ConstElementPtr& lease_info);
+
+ /// @brief virtual dtor (does nothing)
+ virtual ~Lease6Parser() {}
+};
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif
diff --git a/src/hooks/dhcp/lease_cmds/load_unload.cc b/src/hooks/dhcp/lease_cmds/load_unload.cc
new file mode 100644
index 0000000000..c1277cfc52
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/load_unload.cc
@@ -0,0 +1,63 @@
+// Copyright (C) 2017 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/.
+
+/// @file load_unload.cc Defines the load and unload hooks library functions.
+
+#include <config.h>
+#include <lease_cmds.h>
+#include <lease_cmds_log.h>
+#include <string>
+#include <hooks/hooks.h>
+#include <exceptions/exceptions.h>
+
+using namespace isc::hooks;
+using namespace isc::lease_cmds;
+
+boost::shared_ptr<LeaseCmds> instance;
+
+extern "C" {
+
+/// @brief This function is called when the library is loaded.
+///
+/// This function creates LeaseCmds object that registers
+/// additional commands.
+///
+/// @param handle library handle (ignored)
+/// @return 0 when initialization is successful, 1 otherwise
+int load(LibraryHandle& /*handle*/) {
+
+ try {
+ instance.reset(new LeaseCmds());
+ } catch (const isc::Unexpected& ex) {
+ LOG_ERROR(lease_cmds_logger, LEASE_CMDS_INIT_FAILED)
+ .arg(ex.what());
+ return (1);
+ }
+
+ LOG_INFO(lease_cmds_logger, LEASE_CMDS_INIT_OK);
+ return (0);
+}
+
+/// @brief This function is called when the library is unloaded.
+///
+/// This function creates LeaseCmds object that deregisters
+/// additional commands.
+///
+/// @return 0 if deregistration was successful, 1 otherwise
+int unload() {
+ try {
+ instance.reset();
+ } catch (const isc::Unexpected& ex) {
+ LOG_ERROR(lease_cmds_logger, LEASE_CMDS_DEINIT_FAILED)
+ .arg(ex.what());
+ return (1);
+ }
+
+ LOG_INFO(lease_cmds_logger, LEASE_CMDS_DEINIT_OK);
+ return (0);
+}
+
+}
diff --git a/src/hooks/dhcp/lease_cmds/tests/.gitignore b/src/hooks/dhcp/lease_cmds/tests/.gitignore
new file mode 100644
index 0000000000..4ec4015b05
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/tests/.gitignore
@@ -0,0 +1,5 @@
+lease_cmds_unittests
+lease_cmds_unittests.log
+lease_cmds_unittests.trs
+test-suite.log
+*~ \ No newline at end of file
diff --git a/src/hooks/dhcp/lease_cmds/tests/Makefile.am b/src/hooks/dhcp/lease_cmds/tests/Makefile.am
new file mode 100644
index 0000000000..0e987d2cd6
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/tests/Makefile.am
@@ -0,0 +1,57 @@
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += -I$(top_builddir)/src/hooks/dhcp/lease_cmds -I$(top_srcdir)/src/hooks/dhcp/lease_cmds
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += -DLEASE_CMDS_LIB_SO=\"$(abs_top_builddir)/src/hooks/dhcp/lease_cmds/.libs/libdhcp_lease_cmds.so\"
+AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
+
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+# Unit test data files need to get installed.
+EXTRA_DIST =
+
+CLEANFILES = *.gcno *.gcda
+
+# TESTS_ENVIRONMENT = $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+LOG_COMPILER = $(LIBTOOL)
+AM_LOG_FLAGS = --mode=execute
+
+TESTS =
+if HAVE_GTEST
+TESTS += lease_cmds_unittests
+
+lease_cmds_unittests_SOURCES = run_unittests.cc
+lease_cmds_unittests_SOURCES += lease_cmds_unittest.cc
+
+lease_cmds_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
+
+lease_cmds_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS)
+
+lease_cmds_unittests_CXXFLAGS = $(AM_CXXFLAGS)
+if USE_CLANGPP
+# This is to workaround unused variables tcout and tcerr in
+# log4cplus's streams.h and unused parameters from some of the
+# Boost headers.
+lease_cmds_unittests_CXXFLAGS += -Wno-unused-parameter
+endif
+
+lease_cmds_unittests_LDADD = $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la
+lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
+lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
+lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
+lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la
+lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
+lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
+lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
+lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+lease_cmds_unittests_LDADD += $(LOG4CPLUS_LIBS)
+lease_cmds_unittests_LDADD += $(CRYPTO_LIBS)
+lease_cmds_unittests_LDADD += $(BOOST_LIBS)
+lease_cmds_unittests_LDADD += $(GTEST_LDADD)
+endif
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc b/src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc
new file mode 100644
index 0000000000..30cdd0da2e
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc
@@ -0,0 +1,2070 @@
+// Copyright (C) 2017 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 <exceptions/exceptions.h>
+#include <hooks/hooks_manager.h>
+#include <config/command_mgr.h>
+#include <dhcpsrv/lease_mgr.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <cc/command_interpreter.h>
+#include <cc/data.h>
+#include <gtest/gtest.h>
+#include <cc/data.h>
+#include <errno.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::hooks;
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+
+namespace {
+
+/// @brief Test fixture for testing loading and unloading the flex-id library
+class LibLoadTest : public ::testing::Test {
+public:
+ /// @brief Constructor
+ LibLoadTest(std::string lib_filename)
+ : lib_name_(lib_filename) {
+ CommandMgr::instance();
+ unloadLibs();
+ }
+
+ /// @brief Destructor
+ /// Removes files that may be left over from previous tests
+ virtual ~LibLoadTest() {
+ unloadLibs();
+ }
+
+ /// @brief Adds library/parameters to list of libraries to be loaded
+ void addLib(const std::string& lib, ConstElementPtr params) {
+ libraries_.push_back(make_pair(lib, params));
+ }
+
+ /// @brief Load all specified libraries.
+ ///
+ /// The libraries are stored in libraries
+ void loadLibs() {
+ ASSERT_TRUE(HooksManager::loadLibraries(libraries_))
+ << "library loading failed";
+ }
+
+ /// @brief Unloads all libraries.
+ void unloadLibs() {
+ ASSERT_NO_THROW(HooksManager::unloadLibraries());
+ }
+
+ /// @brief Checks whether specified command is registered
+ ///
+ /// @param name name of the command to be checked
+ /// @param expect_true true - must be registered, false - must not be
+ void checkCommandRegistered(const std::string& name, bool expect_true) {
+
+ // First get the list of registered commands
+ ConstElementPtr lst = Element::fromJSON("{ \"command\": \"list-commands\" }");
+ ConstElementPtr rsp = CommandMgr::instance().processCommand(lst);
+
+ ASSERT_TRUE(rsp);
+
+ ConstElementPtr args = rsp->get("arguments");
+ ASSERT_TRUE(args);
+
+ string args_txt = args->str();
+
+ if (expect_true) {
+ EXPECT_TRUE(args_txt.find(name) != string::npos);
+ } else {
+ EXPECT_TRUE(args_txt.find(name) == string::npos);
+ }
+ }
+
+ /// @brief tests specified command and verifies response
+ ///
+ /// This method loads the library, sends specific command,
+ /// then checks if the result is as expected, checks if text response
+ /// is ok (optional, check skipped if exp_txt is empty) and then returns
+ /// the response (for possible additional checks).
+ ///
+ /// @param cmd JSON command to be sent (must be valid JSON)
+ /// @param exp_result 0 - success, 1 - error, 2 - ...
+ /// @param exp_txt expected text response (optional)
+ /// @return full response returned by the command execution.
+ ConstElementPtr testCommand(string cmd_txt, int exp_result, string exp_txt) {
+ // Let's load the library first.
+ loadLib();
+
+ ConstElementPtr cmd;
+ EXPECT_NO_THROW(cmd = Element::fromJSON(cmd_txt));
+ if (!cmd) {
+ ADD_FAILURE() << cmd_txt << " is not a valid JSON, test broken";
+ return (ConstElementPtr());
+ }
+
+ // Process the command and verify response.
+ ConstElementPtr rsp = CommandMgr::instance().processCommand(cmd);
+ checkAnswer(rsp, exp_result, exp_txt);
+
+ return (rsp);
+ }
+
+ /// @brief Compares the status in the given parse result to a given value.
+ ///
+ /// @param answer Element set containing an integer response and string
+ /// comment.
+ /// @param exp_status is an integer against which to compare the status.
+ /// @param exp_txt is expected text (not checked if "")
+ void checkAnswer(isc::data::ConstElementPtr answer,
+ int exp_status,
+ string exp_txt = "") {
+ int rcode = 0;
+ isc::data::ConstElementPtr comment;
+ comment = isc::config::parseAnswer(rcode, answer);
+
+ if (rcode != exp_status) {
+ ADD_FAILURE() << "Expected status code " << exp_status
+ << " but received " << rcode << ", comment: "
+ << (comment ? comment->str() : "(none)");
+ }
+
+ // Ok, parseAnswer interface is weird. If there are no arguments,
+ // it returns content of text. But if there is an argument,
+ // it returns the argument and it's not possible to retrieve
+ // "text" (i.e. comment).
+ if (comment->getType() != Element::string) {
+ comment = answer->get("text");
+ }
+
+ if (!exp_txt.empty()) {
+ EXPECT_EQ(exp_txt, comment->stringValue());
+ }
+ }
+
+ /// @brief Loads the library specified by lib_name_
+ void loadLib() {
+ if (libraries_.empty()) {
+ data::ElementPtr params = data::Element::createMap();
+ addLib(lib_name_, params);
+ }
+ EXPECT_NO_THROW(loadLibs());
+ }
+
+ /// @brief Test checks if specified commands are provided by the library.
+ ///
+ /// @param cms a vector of string with command names
+ void testCommands(const std::vector<string> cmds) {
+
+ // The commands should not be registered yet.
+ for (auto cmd = cmds.begin(); cmd != cmds.end(); ++cmd) {
+ checkCommandRegistered(*cmd, false);
+ }
+
+ loadLib();
+
+ // The commands should be available after library was loaded.
+ for (auto cmd = cmds.begin(); cmd != cmds.end(); ++cmd) {
+ checkCommandRegistered(*cmd, true);
+ }
+
+ unloadLibs();
+
+ // and the commands should be gone now.
+ for (auto cmd = cmds.begin(); cmd != cmds.end(); ++cmd) {
+ checkCommandRegistered(*cmd, false);
+ }
+
+ }
+
+ // Check that the library can be loaded and unloaded multiple times.
+ void testMultipleLoads() {
+ EXPECT_NO_THROW(loadLib());
+ EXPECT_NO_THROW(unloadLibs());
+
+ EXPECT_NO_THROW(loadLib());
+ EXPECT_NO_THROW(unloadLibs());
+
+ EXPECT_NO_THROW(loadLib());
+ EXPECT_NO_THROW(unloadLibs());
+
+ EXPECT_NO_THROW(loadLib());
+ EXPECT_NO_THROW(unloadLibs());
+ }
+
+ /// List of libraries to be/being loaded (usually just one)
+ HookLibsCollection libraries_;
+
+ /// Path to the library filename
+ std::string lib_name_;
+};
+
+/// @brief Class dedicated to testing lease_cmds library.
+///
+/// Provides convenience methods for loading, testing all commands and
+/// unloading the lease_cmds library.
+class LeaseCmdsTest : public LibLoadTest {
+public:
+
+ /// @brief Pointer to the lease manager
+ LeaseMgr* lmptr_;
+
+ /// @brief Constructor
+ ///
+ /// Sets the library filename and clears the lease manager pointer.
+ /// Also ensured there is no lease manager leftovers from previous
+ /// test.
+ LeaseCmdsTest()
+ :LibLoadTest(LEASE_CMDS_LIB_SO) {
+ LeaseMgrFactory::destroy();
+ lmptr_ = 0;
+ }
+
+ /// @brief Destructor
+ ///
+ /// Removes library (if any), destroys lease manager (if any).
+ virtual ~LeaseCmdsTest() {
+ // destroys lease manager first because the other order triggers
+ // a clang/boost bug
+ LeaseMgrFactory::destroy();
+ unloadLibs();
+ lmptr_ = 0;
+ }
+
+ /// @brief Initializes lease manager (and optionally populates it with a lease)
+ ///
+ /// Creates a lease manager (memfile, trimmed down to keep everything in memory
+ /// only) and optionally can create a lease, which is useful for leaseX-get and
+ /// leasex-del type of tests. For lease details, see @ref createLease4 and
+ /// @ref createLease6.
+ ///
+ /// @param v6 true = v6, false = v4
+ /// @param insert_lease governs whether a lease should be pre-inserted
+ void initLeaseMgr(bool v6, bool insert_lease) {
+
+ LeaseMgrFactory::destroy();
+ std::ostringstream s;
+ s << "type=memfile persist=false " << (v6 ? "universe=6" : "universe=4");
+ LeaseMgrFactory::create(s.str());
+
+ lmptr_ = &(LeaseMgrFactory::instance());
+ ASSERT_TRUE(lmptr_);
+
+ CfgMgr& cfg_mgr = CfgMgr::instance();
+ if (v6) {
+ Subnet6Ptr subnet6(new Subnet6(IOAddress("2001:db8::"), 48, 1, 2, 3, 4, 66));
+ CfgSubnets6Ptr subnets = cfg_mgr.getStagingCfg()->getCfgSubnets6();
+ subnets->add(subnet6);
+ cfg_mgr.commit();
+ } else {
+ Subnet4Ptr subnet4(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3, 44));
+ CfgSubnets4Ptr subnets = cfg_mgr.getStagingCfg()->getCfgSubnets4();
+ subnets->add(subnet4);
+ cfg_mgr.commit();
+ }
+
+ if (insert_lease) {
+ if (v6) {
+ lmptr_->addLease(createLease6());
+ } else {
+ lmptr_->addLease(createLease4());
+ }
+ }
+ }
+
+ /// @brief Creates an IPv4 lease
+ ///
+ /// Lease parameters: ip-address = 192.0.2.1, hwaddr = 08:08:08:08:08:08,
+ /// client-id = 42:42:42:42:42:42:42:42, valid lifetime = 3600,
+ /// cltt = 12345678, subnet-id = 44, fqdn-fwd = false, fqdn-rev = true
+ /// hostname = myhost.example.com
+ ///
+ /// @return Returns the lease created
+ Lease4Ptr createLease4() {
+ Lease4Ptr lease(new Lease4());
+
+ lease->addr_ = IOAddress("192.0.2.1");
+
+ // Initialize unused fields.
+ lease->t1_ = 0; // Not saved
+ lease->t2_ = 0; // Not saved
+
+ // Set other parameters. For historical reasons, address 0 is not used.
+ lease->hwaddr_.reset(new HWAddr(vector<uint8_t>(6, 0x08), HTYPE_ETHER));
+ lease->client_id_ = ClientIdPtr(new ClientId(vector<uint8_t>(8, 0x42)));
+ lease->valid_lft_ = 3600;
+ lease->cltt_ = 12345678;
+ lease->subnet_id_ = 44;
+ lease->fqdn_fwd_ = false;
+ lease->fqdn_rev_ = true;
+ lease->hostname_ = "myhost.example.com.";
+
+ return (lease);
+ }
+
+ /// @brief Creates an IPv6 lease
+ ///
+ /// Lease parameters: ip-address = 2001:db8::1, duid = 77:77:77:77:77:77:77:77,
+ /// cltt = 12345678, subnet-id = 66, fqdn-fwd = false, fqdn-rev = true,
+ /// hostname = myhost.example.com preferred lifetime = 1800,
+ /// valid lifetime = 3600
+ ///
+ /// @return Returns the lease created
+ Lease6Ptr createLease6() {
+ Lease6Ptr lease(new Lease6());
+
+ lease->addr_ = IOAddress("2001:db8::1");
+ lease->type_ = Lease::TYPE_NA;
+ lease->prefixlen_ = 128;
+ lease->iaid_ = 42;
+ lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x77)));
+ lease->preferred_lft_ = 1800;
+ lease->valid_lft_ = 3600;
+ lease->cltt_ = 12345678;
+ lease->subnet_id_ = 66;
+ lease->fqdn_fwd_ = true;
+ lease->fqdn_rev_ = true;
+ lease->hostname_ = "myhost.example.com.";
+
+ return (lease);
+ }
+
+ /// @brief Checks if specified response contains IPv4 lease
+ ///
+ /// @param lease Element tree that represents a lease
+ /// @param ip expected IP address
+ /// @param subnet_id expected subnet-id
+ /// @param hwaddr expected value of hardware address
+ /// @param client_id_required true if client-id is expected
+ void checkLease4(ConstElementPtr l, std::string ip,
+ uint32_t subnet_id, std::string hwaddr,
+ bool client_id_required) {
+ ASSERT_TRUE(l);
+ ASSERT_TRUE(l->get("ip-address"));
+ EXPECT_EQ(ip, l->get("ip-address")->stringValue());
+
+ ASSERT_TRUE(l->get("subnet-id"));
+ EXPECT_EQ(subnet_id, l->get("subnet-id")->intValue());
+
+ ASSERT_TRUE(l->get("hw-address"));
+ EXPECT_EQ(hwaddr, l->get("hw-address")->stringValue());
+
+ // client-id may or may not appear
+ if (client_id_required) {
+ EXPECT_TRUE(l->get("client-id"));
+ }
+
+ // Check that other parameters are there.
+ EXPECT_TRUE(l->contains("valid-lft"));
+ EXPECT_TRUE(l->contains("cltt"));
+ EXPECT_TRUE(l->contains("subnet-id"));
+ EXPECT_TRUE(l->contains("state"));
+ EXPECT_TRUE(l->contains("fqdn-fwd"));
+ EXPECT_TRUE(l->contains("fqdn-rev"));
+ EXPECT_TRUE(l->contains("hostname"));
+ EXPECT_TRUE(l->contains("state"));
+
+ // Check that there are no v6 specific fields
+ EXPECT_FALSE(l->contains("prefix"));
+ EXPECT_FALSE(l->contains("duid"));
+ EXPECT_FALSE(l->contains("preferred-lft"));
+ }
+
+ /// @brief Checks if specified response contains IPv6 lease
+ ///
+ /// @param lease Element tree that represents a lease
+ /// @param ip expected IP address (or prefix)
+ /// @param prefixlen prefix length (0 = expect address)
+ /// @param subnet_id expected subnet-id
+ /// @param duid expected value of DUID
+ /// @param hwaddr_required true if hwaddr is expected
+ void checkLease6(ConstElementPtr l, std::string ip,
+ uint8_t prefixlen,
+ uint32_t subnet_id, std::string duid,
+ bool hwaddr_required) {
+
+ ASSERT_TRUE(l);
+
+ ASSERT_TRUE(l->contains("ip-address"));
+ EXPECT_EQ(ip, l->get("ip-address")->stringValue());
+ if (prefixlen != 0) {
+ ASSERT_TRUE(l->get("prefix-len"));
+ EXPECT_EQ(prefixlen, l->get("prefix-len")->intValue());
+ }
+
+ ASSERT_TRUE(l->contains("subnet-id"));
+ EXPECT_EQ(subnet_id, l->get("subnet-id")->intValue());
+
+ ASSERT_TRUE(l->contains("duid"));
+ EXPECT_EQ(duid, l->get("duid")->stringValue());
+
+ // hwaddr may or may not appear
+ if (hwaddr_required) {
+ EXPECT_TRUE(l->get("hwaddr"));
+ }
+
+ // Check that there are expected fields
+ EXPECT_TRUE(l->contains("preferred-lft"));
+ EXPECT_TRUE(l->contains("valid-lft"));
+ EXPECT_TRUE(l->contains("cltt"));
+ EXPECT_TRUE(l->contains("subnet-id"));
+ EXPECT_TRUE(l->contains("fqdn-fwd"));
+ EXPECT_TRUE(l->contains("fqdn-rev"));
+ EXPECT_TRUE(l->contains("hostname"));
+ EXPECT_TRUE(l->contains("state"));
+
+ // Check that there are no v4 specific fields.
+ EXPECT_FALSE(l->contains("client-id"));
+ }
+};
+
+// Simple test that checks the library really registers the commands.
+TEST_F(LeaseCmdsTest, commands) {
+
+ vector<string> cmds = { "lease4-add", "lease6-add",
+ "lease4-get", "lease6-get",
+ "lease4-del", "lease6-del",
+ "lease4-update", "lease6-update",
+ "lease4-wipe", "lease6-wipe" };
+ testCommands(cmds);
+}
+
+// Check that the library can be loaded and unloaded multiple times.
+TEST_F(LeaseCmdsTest, multipleLoads) {
+ testMultipleLoads();
+}
+
+using namespace isc::dhcp;
+
+// Check that lease4-add with missing parameters will fail.
+TEST_F(LeaseCmdsTest, Lease4AddMissingParams) {
+
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ // Check that the lease manager pointer is there.
+ ASSERT_TRUE(lmptr_);
+
+ // Everything missing. What sort of crap is that?
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " }\n"
+ "}";
+ string exp_rsp = "missing parameter 'ip-address' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Just ip is not enough (subnet-id and hwaddr missing).
+ txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.123\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'subnet-id' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Better, but still no luck. (hwaddr missing).
+ txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.202\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'hw-address' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Close, but no cigars. (ip-address missing).
+ txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'ip-address' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+// Verify that lease4-add can be rejected if parameters are specified, but
+// have incorrect values.
+TEST_F(LeaseCmdsTest, Lease4AddBadParams) {
+
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ // Check that the lease manager pointer is there.
+ ASSERT_TRUE(lmptr_);
+
+ // All params are there, but there's no subnet-id 123 configured.
+ // (initLeaseMgr initialized subnet-id 44 for v4 and subnet-id 66 for v6).
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"ip-address\": \"192.0.2.202\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Invalid subnet-id: No IPv4 subnet with subnet-id=123 currently configured.";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // This time the IP address does not belong to the subnet.
+ txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"10.0.0.1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "The address 10.0.0.1 does not belong to subnet 192.0.2.0/24, subnet-id=44";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // We don't use any of that bleeding edge nonsense in this museum. v4 only.
+ txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"2001:db8::1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Non-IPv4 address specified: 2001:db8::1";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // currently defined states are 0,1 and 2. 123 is junk.
+ txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"state\": 123\n"
+ " }\n"
+ "}";
+ exp_rsp = "Invalid state value: 123, supported values are: 0 (default), 1 "
+ "(declined) and 2 (expired-reclaimed)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+// Check that a simple, well formed lease4 can be added.
+TEST_F(LeaseCmdsTest, Lease4Add) {
+
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ // Check that the lease manager pointer is there.
+ ASSERT_TRUE(lmptr_);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.202\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Lease added.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Now check that the lease is really there.
+ Lease4Ptr l = lmptr_->getLease4(IOAddress("192.0.2.202"));
+ ASSERT_TRUE(l);
+
+ // Make sure the lease have proper value set.
+ ASSERT_TRUE(l->hwaddr_);
+ EXPECT_EQ("1a:1b:1c:1d:1e:1f", l->hwaddr_->toText(false));
+ EXPECT_EQ(3, l->valid_lft_); // taken from subnet configuration
+ EXPECT_EQ(false, l->fqdn_fwd_);
+ EXPECT_EQ(false, l->fqdn_rev_);
+ EXPECT_EQ("", l->hostname_);
+
+ // Test execution is fast. The cltt should be set to now. In some rare
+ // cases we could have the seconds counter to tick, so having a value off
+ // by one is ok.
+ EXPECT_LE(abs(l->cltt_ - time(NULL)), 1);
+ EXPECT_EQ(0, l->state_);
+
+}
+
+// Check that a well formed lease4 with tons of parameters can be added.
+TEST_F(LeaseCmdsTest, Lease4AddFull) {
+
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ // Check that the lease manager pointer is there.
+ ASSERT_TRUE(lmptr_);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.202\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"client-id\": \"01:02:03:04:05:06:07:08\",\n"
+ " \"valid-lft\": 1000,\n"
+ " \"expire\": 12345678,\n"
+ " \"fqdn-fwd\": true,\n"
+ " \"fqdn-rev\": true,\n"
+ " \"hostname\": \"urania.example.org\""
+ " }\n"
+ "}";
+ string exp_rsp = "Lease added.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Now check that the lease is really there.
+ Lease4Ptr l = lmptr_->getLease4(IOAddress("192.0.2.202"));
+ ASSERT_TRUE(l);
+ EXPECT_EQ("192.0.2.202", l->addr_.toText());
+ ASSERT_TRUE(l->hwaddr_);
+ EXPECT_EQ("1a:1b:1c:1d:1e:1f", l->hwaddr_->toText(false));
+ ASSERT_TRUE(l->client_id_);
+ EXPECT_EQ("01:02:03:04:05:06:07:08", l->client_id_->toText());
+ EXPECT_EQ(l->cltt_, 12344678); // expire (12345678) - valid_lft(1000)
+ EXPECT_EQ(true, l->fqdn_fwd_);
+ EXPECT_EQ(true, l->fqdn_rev_);
+ EXPECT_EQ("urania.example.org", l->hostname_);
+}
+
+// Check that lease6-add with missing parameters will fail.
+TEST_F(LeaseCmdsTest, Lease6AddMissingParams) {
+
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ // Check that the lease manager pointer is there.
+ ASSERT_TRUE(lmptr_);
+
+ // Everything missing. What sort of nonsense is that?
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " }\n"
+ "}";
+ string exp_rsp = "missing parameter 'ip-address' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Just ip is not enough (subnet-id and duid missing).
+ txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"2001:db8::3\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'subnet-id' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Just subnet-id and ip is not enough (duid missing).
+ txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8::3\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'duid' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Just subnet-id and duid is not enough (ip, iaid missing).
+ txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'ip-address' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Just subnet-id, duid and iaid is not enough (ip missing).
+ txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'ip-address' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Close, but no cigars. Still missing iaid.
+ txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"ip-address\": \"2001:db8::3\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'iaid' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+// Verify that lease6-add can be rejected if parameters specified, but
+// have incorrect values.
+TEST_F(LeaseCmdsTest, Lease6AddBadParams) {
+
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ // Check that the lease manager pointer is there.
+ ASSERT_TRUE(lmptr_);
+
+ // Invalid subnet-id. Only 66 is configured.
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"ip-address\": \"2001:db8::3\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Invalid subnet-id: No IPv6 subnet with subnet-id=123 currently configured.";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // This time the IP address does not belong to the subnet.
+ txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"3000::3\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n"
+ " }\n"
+ "}";
+ exp_rsp = "The address 3000::3 does not belong to subnet 2001:db8::/48, subnet-id=66";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // v4? You're a time traveler from early 80s or what?
+ txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"192.0.2.1\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n"
+ " }\n"
+ "}";
+ exp_rsp = "Non-IPv6 address specified: 192.0.2.1";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Invalid state: the only supported values are 0,1,2.
+ txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8::1\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n,"
+ " \"state\": 123\n"
+ " }\n"
+ "}";
+ exp_rsp = "Invalid state value: 123, supported values are: 0 (default), 1 "
+ "(declined) and 2 (expired-reclaimed)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+// Check that a simple, well formed lease6 can be added.
+TEST_F(LeaseCmdsTest, Lease6Add) {
+
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ // Check that the lease manager pointer is there.
+ ASSERT_TRUE(lmptr_);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8::3\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Lease added.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Now check that the lease is really there.
+ EXPECT_TRUE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8::3")));
+}
+
+// Check that a simple, well formed prefix lease can be added.
+TEST_F(LeaseCmdsTest, Lease6AddPrefix) {
+
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ // Check that the lease manager pointer is there.
+ ASSERT_TRUE(lmptr_);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:abcd::\",\n"
+ " \"prefix-len\": 48,\n"
+ " \"type\": \"IA_PD\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Lease added.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Now check that the lease is really there.
+ Lease6Ptr l = lmptr_->getLease6(Lease::TYPE_PD, IOAddress("2001:db8:abcd::"));
+ ASSERT_TRUE(l);
+ EXPECT_EQ(Lease::TYPE_PD, l->type_);
+ EXPECT_EQ(48, l->prefixlen_);
+}
+
+// Check that a well formed lease6 with tons of parameters can be added.
+TEST_F(LeaseCmdsTest, Lease6AddFullAddr) {
+
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ // Check that the lease manager pointer is there.
+ ASSERT_TRUE(lmptr_);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8::3\",\n"
+ " \"duid\": \"01:02:03:04:05:06:07:08\",\n"
+ " \"iaid\": 1234,\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"preferred-lft\": 500,\n"
+ " \"valid-lft\": 1000,\n"
+ " \"expire\": 12345678,\n"
+ " \"fqdn-fwd\": true,\n"
+ " \"fqdn-rev\": true,\n"
+ " \"hostname\": \"urania.example.org\""
+ " }\n"
+ "}";
+ string exp_rsp = "Lease added.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Now check that the lease is really there.
+ Lease6Ptr l = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8::3"));
+ ASSERT_TRUE(l);
+ EXPECT_EQ(Lease::TYPE_NA, l->type_);
+ EXPECT_EQ("2001:db8::3", l->addr_.toText());
+ ASSERT_TRUE(l->hwaddr_);
+ EXPECT_EQ("1a:1b:1c:1d:1e:1f", l->hwaddr_->toText(false));
+ ASSERT_TRUE(l->duid_);
+ EXPECT_EQ("01:02:03:04:05:06:07:08", l->duid_->toText());
+ EXPECT_EQ(l->cltt_, 12344678); // expire (12345678) - valid_lft(1000)
+ EXPECT_EQ(true, l->fqdn_fwd_);
+ EXPECT_EQ(true, l->fqdn_rev_);
+ EXPECT_EQ("urania.example.org", l->hostname_);
+}
+
+// Checks that lease6-get can handle a situation when the query is
+// broken (some required parameters are missing).
+TEST_F(LeaseCmdsTest, Lease4GetMissingParams) {
+
+ // No parameters whatsoever. You want just a lease, any lease?
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get\",\n"
+ " \"arguments\": {"
+ " }\n"
+ "}";
+ string exp_rsp = "Mandatory 'subnet-id' parameter missing.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Just the subnet-id won't cut it, either.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-get\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123"
+ " }\n"
+ "}";
+ exp_rsp = "No 'ip-address' provided and 'identifier-type' is either missing or not a string.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // We can't identify your laptop by color. Sorry, buddy.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-get\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"identifier-type\": \"color\",\n"
+ " \"identifier\": \"blue\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Incorrect identifier type: color, the only supported values are: "
+ "address, hw-address, duid";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Query by DUID is not supported in v4. Sorry.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-get\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"identifier-type\": \"duid\",\n"
+ " \"identifier\": \"01:01:01:01:01:01\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Query by duid is not allowed in v4.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Identifier value is missing.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-get\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"identifier-type\": \"hw-address\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "No 'ip-address' provided and 'identifier' is either missing or not a string.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Identifier-type is missing.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-get\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"identifier\": \"01:02:03:04:05\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "No 'ip-address' provided and 'identifier-type' is either missing or not a string.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+// Checks that lease4-get sanitizes its input.
+TEST_F(LeaseCmdsTest, Lease4GetByAddrBadParam) {
+
+ // Initialize lease manager (false = v4, true = add a lease)
+ initLeaseMgr(false, true);
+
+ // Invalid family
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"2001:db8::1\""
+ " }\n"
+ "}";
+ string exp_rsp = "Invalid IPv4 address specified: 2001:db8::1";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // This is way off
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-get\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"221B Baker St.\""
+ " }\n"
+ "}";
+ exp_rsp = "Failed to convert string to address '221B Baker St.': Invalid argument";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+// Checks that lease4-get can handle a situation when the query is
+// valid, but the lease is not there.
+TEST_F(LeaseCmdsTest, Lease4GetByAddrNotFound) {
+
+ // Initialize lease manager (false = v4, true = add a lease)
+ initLeaseMgr(false, true);
+
+ // Invalid
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.5\","
+ " \"subnet-id\": 44"
+ " }\n"
+ "}";
+ string exp_rsp = "Lease not found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+}
+
+// Checks that lease4-get can return a lease by address.
+TEST_F(LeaseCmdsTest, Lease4GetByAddr) {
+
+ // Initialize lease manager (false = v4, true = add a lease)
+ initLeaseMgr(false, true);
+
+ // Query for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.1\""
+ " }\n"
+ "}";
+ string exp_rsp = "IPv4 lease found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Now check that the lease parameters were indeed returned.
+ ASSERT_TRUE(rsp);
+ ConstElementPtr lease = rsp->get("arguments");
+ ASSERT_TRUE(lease);
+
+ // Let's check if the response makes any sense.
+ checkLease4(lease, "192.0.2.1", 44, "08:08:08:08:08:08", false);
+}
+
+// Checks that lease4-get can handle a situation when the query is
+// well formed, but the lease is not there.
+TEST_F(LeaseCmdsTest, Lease4GetByHWAddrNotFound) {
+
+ // Initialize lease manager (false = v4, false = don't add a lease)
+ initLeaseMgr(false, false);
+
+ // No such lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get\",\n"
+ " \"arguments\": {"
+ " \"identifier-type\": \"hw-address\","
+ " \"identifier\": \"01:02:03:04:05:06\","
+ " \"subnet-id\": 44"
+ " }\n"
+ "}";
+ string exp_rsp = "Lease not found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+}
+
+// Checks that lease4-get can find a lease by hardware address.
+TEST_F(LeaseCmdsTest, Lease4GetByHWAddr) {
+
+ // Initialize lease manager (false = v4, true = add a lease)
+ initLeaseMgr(false, true);
+
+ // Invalid
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get\",\n"
+ " \"arguments\": {"
+ " \"identifier-type\": \"hw-address\","
+ " \"identifier\": \"08:08:08:08:08:08\","
+ " \"subnet-id\": 44"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv4 lease found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Now check that the lease parameters were indeed returned.
+ ASSERT_TRUE(rsp);
+ ConstElementPtr lease = rsp->get("arguments");
+ ASSERT_TRUE(lease);
+
+ // Let's check if the response makes any sense.
+ checkLease4(lease, "192.0.2.1", 44, "08:08:08:08:08:08", false);
+}
+
+// Checks that lease6-get(addr) can handle a situation when
+// the query is correctly formed, but the lease is not there.
+TEST_F(LeaseCmdsTest, Lease6GetByAddr6NotFound) {
+
+ // Initialize lease manager (true = v6, true = add a lease)
+ initLeaseMgr(true, true);
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 1,\n"
+ " \"ip-address\": \"2001:db8::2\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Lease not found.";
+
+ // Note the status expected is empty. The query completed correctly,
+ // just didn't found the lease.
+ testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+}
+
+// Checks that lease6-get(subnet-id, addr) can handle a situation when
+// the query is correctly formed, but the lease is not there.
+TEST_F(LeaseCmdsTest, Lease6GetByDuidNotFound) {
+ // Initialize lease manager (true = v6, true = add a lease)
+ initLeaseMgr(true, true);
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 1,\n"
+ " \"identifier-type\": \"duid\","
+ " \"identifier\": \"00:01:02:03:04:05:06:07\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Lease not found.";
+
+ // Note the status expected is empty. The query completed correctly,
+ // just didn't found the lease.
+ testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+}
+
+// Checks that lease6-get(subnet-id, addr6) can handle a situation when
+// the query is correctly formed and the lease is returned.
+TEST_F(LeaseCmdsTest, Lease6GetByAddr) {
+
+ initLeaseMgr(true, true); // (true = v6, true = create a lease)
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get\",\n"
+ " \"arguments\": {\n"
+ " \"ip-address\": \"2001:db8::1\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv6 lease found.";
+
+ // The status expected is success. The lease should be returned.
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+ ASSERT_TRUE(rsp);
+
+ ConstElementPtr lease = rsp->get("arguments");
+ ASSERT_TRUE(lease);
+
+ // Now check that the lease was indeed returned.
+ checkLease6(lease, "2001:db8::1", 0, 66, "77:77:77:77:77:77:77:77", false);
+}
+
+// Checks that lease6-get sanitizes its input.
+TEST_F(LeaseCmdsTest, Lease6GetByAddrBadParam) {
+
+ // Initialize lease manager (true = v6, true = add a lease)
+ initLeaseMgr(true, true);
+
+ // Invalid family
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.1\""
+ " }\n"
+ "}";
+ string exp_rsp = "Invalid IPv6 address specified: 192.0.2.1";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // This is way off
+ cmd =
+ "{\n"
+ " \"command\": \"lease6-get\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"221B Baker St.\""
+ " }\n"
+ "}";
+ exp_rsp = "Failed to convert string to address '221B Baker St.': Invalid argument";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+// Checks that lease6-get(subnet-id, type, addr6) can handle a situation when
+// the query is correctly formed and the lease is returned.
+TEST_F(LeaseCmdsTest, Lease6GetByAddrPrefix) {
+
+ // We need to get a prefix lease. We need to create it by hand.
+ initLeaseMgr(true, false); // (true = v6, true = create a lease)
+
+ // Let's start with regular address lease and make it a prefix lease.
+ Lease6Ptr l = createLease6();
+ l->addr_ = IOAddress("2001:db8:1234:ab::");
+ l->type_ = Lease::TYPE_PD;
+ l->prefixlen_ = 56;
+ lmptr_->addLease(l);
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get\",\n"
+ " \"arguments\": {"
+ " \"type\": \"IA_PD\","
+ " \"ip-address\": \"2001:db8:1234:ab::\""
+ " }\n"
+ "}";
+ string exp_rsp = "IPv6 lease found.";
+
+ // The status expected is success. The lease should be returned.
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+ ASSERT_TRUE(rsp);
+
+ ConstElementPtr lease = rsp->get("arguments");
+ ASSERT_TRUE(lease);
+
+ // Now check that the lease was indeed returned.
+ checkLease6(lease, "2001:db8:1234:ab::", 56, 66, "77:77:77:77:77:77:77:77", false);
+}
+
+// Checks that lease6-get(subnet-id, iaid, identifier-type, identifier) can handle
+// a situation when the query returns a lease.
+TEST_F(LeaseCmdsTest, Lease6GetByDUID) {
+
+ initLeaseMgr(true, true); // (true = v6, true = create a lease)
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"iaid\": 42,"
+ " \"identifier-type\": \"duid\","
+ " \"identifier\": \"77:77:77:77:77:77:77:77\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv6 lease found.";
+
+ // The status expected is success. The lease should be returned.
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+ ASSERT_TRUE(rsp);
+
+ ConstElementPtr lease = rsp->get("arguments");
+ ASSERT_TRUE(lease);
+
+ // Now check that the lease was indeed returned.
+ checkLease6(lease, "2001:db8::1", 0, 66, "77:77:77:77:77:77:77:77", false);
+}
+
+// Test checks if lease4-update handler refuses calls with missing parameters.
+TEST_F(LeaseCmdsTest, Lease4UpdateMissingParams) {
+ // Initialize lease manager (false = v4, true = add a lease)
+ initLeaseMgr(false, true);
+
+ // Check that the lease manager pointer is there.
+ ASSERT_TRUE(lmptr_);
+
+ // Everything missing. What sort of crap is that?
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " }\n"
+ "}";
+ string exp_rsp = "missing parameter 'ip-address' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Just ip is not enough (subnet-id and hwaddr missing).
+ txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.123\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'subnet-id' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Better, but still no luck. (hwaddr missing).
+ txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.202\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'hw-address' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Close, but no cigars. (ip-address missing).
+ txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'ip-address' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+// Verify that lease4-update can be rejected if parameters are specified, but
+// have incorrect values.
+TEST_F(LeaseCmdsTest, Lease4UpdateBadParams) {
+
+ // Initialize lease manager (false = v4, true = add a lease)
+ initLeaseMgr(false, true);
+
+ // Check that the lease manager pointer is there.
+ ASSERT_TRUE(lmptr_);
+
+ // All params are there, but there's no subnet-id 123 configured.
+ // (initLeaseMgr initialized subnet-id 44 for v4 and subnet-id 66 for v6).
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"ip-address\": \"192.0.2.202\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Invalid subnet-id: No IPv4 subnet with subnet-id=123 currently configured.";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // This time the new IP address does not belong to the subnet.
+ txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"10.0.0.1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "The address 10.0.0.1 does not belong to subnet 192.0.2.0/24, subnet-id=44";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // We don't use any of that bleeding edge nonsense in this museum. v4 only.
+ txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"2001:db8::1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Non-IPv4 address specified: 2001:db8::1";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+// Check that lease4-update correctly handles case when there is
+// no lease to be updated.
+TEST_F(LeaseCmdsTest, Lease4UpdateNoLease) {
+
+ // Initialize lease manager (false = v4, false = don't add any lease)
+ initLeaseMgr(false, false);
+
+ // Check that the lease manager pointer is there.
+ ASSERT_TRUE(lmptr_);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"hostname\": \"newhostname.example.org\""
+ " }\n"
+ "}";
+ string exp_rsp = "failed to update the lease with address 192.0.2.1 - no such lease";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+// Check that a lease4 can be updated. We're changing hw-address
+// and a hostname.
+TEST_F(LeaseCmdsTest, Lease4Update) {
+
+ // Initialize lease manager (false = v4, true = add a lease)
+ initLeaseMgr(false, true);
+
+ // Check that the lease manager pointer is there.
+ ASSERT_TRUE(lmptr_);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"hostname\": \"newhostname.example.org\""
+ " }\n"
+ "}";
+ string exp_rsp = "IPv4 lease updated.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Now check that the lease is still there.
+ Lease4Ptr l = lmptr_->getLease4(IOAddress("192.0.2.1"));
+ ASSERT_TRUE(l);
+
+ // Make sure it's been updated.
+ ASSERT_TRUE(l->hwaddr_);
+ EXPECT_EQ("1a:1b:1c:1d:1e:1f", l->hwaddr_->toText(false));
+ EXPECT_EQ("newhostname.example.org", l->hostname_);
+}
+
+// Test checks if lease6-update handler refuses calls with missing parameters.
+TEST_F(LeaseCmdsTest, Lease6UpdateMissingParams) {
+ // Initialize lease manager (true = v6, true = add a lease)
+ initLeaseMgr(true, true);
+
+ // Check that the lease manager pointer is there.
+ ASSERT_TRUE(lmptr_);
+
+ // Everything missing. What sort of crap is that?
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " }\n"
+ "}";
+ string exp_rsp = "missing parameter 'ip-address' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Just ip is not enough (subnet-id and hwaddr missing).
+ txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"2001:db8::1\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'subnet-id' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Better, but still no luck. (duid missing).
+ txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"2001:db8::1\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'duid' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // ip-address and identifier-type missing.
+ txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'ip-address' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+// Verify that lease6-update can be rejected if parameters are specified, but
+// have incorrect values.
+TEST_F(LeaseCmdsTest, Lease6UpdateBadParams) {
+
+ // Initialize lease manager (true = v6, true = add a lease)
+ initLeaseMgr(true, true);
+
+ // Check that the lease manager pointer is there.
+ ASSERT_TRUE(lmptr_);
+
+ // All params are there, but there's no subnet-id 123 configured.
+ // (initLeaseMgr initialized subnet-id 44 for v4 and subnet-id 66 for v6).
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"ip-address\": \"2001:db8::1\",\n"
+ " \"duid\": \"88:88:88:88:88:88:88:88\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Invalid subnet-id: No IPv6 subnet with subnet-id=123 currently configured.";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // This time the new IP address does not belong to the subnet.
+ txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"3000::1\",\n"
+ " \"duid\": \"88:88:88:88:88:88:88:88\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "The address 3000::1 does not belong to subnet 2001:db8::/48, subnet-id=66";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Nope, can't do v4 address in v6 lease.
+ txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"192.0.2.1\",\n"
+ " \"duid\": \"88:88:88:88:88:88:88:88\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Non-IPv6 address specified: 192.0.2.1";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+// Check that a lease6 can be updated. We're changing hw-address
+// and a hostname.
+TEST_F(LeaseCmdsTest, Lease6Update) {
+
+ // Initialize lease manager (true = v6, true = add a lease)
+ initLeaseMgr(true, true);
+
+ // Check that the lease manager pointer is there.
+ ASSERT_TRUE(lmptr_);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8::1\",\n"
+ " \"iaid\": 7654321,\n"
+ " \"duid\": \"88:88:88:88:88:88:88:88\",\n"
+ " \"hostname\": \"newhostname.example.org\""
+ " }\n"
+ "}";
+ string exp_rsp = "IPv6 lease updated.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Now check that the lease is really there.
+ Lease6Ptr l = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8::1"));
+ ASSERT_TRUE(l);
+
+ // Make sure the lease has been updated.
+ ASSERT_TRUE(l->duid_);
+ EXPECT_EQ("88:88:88:88:88:88:88:88", l->duid_->toText());
+ EXPECT_EQ("newhostname.example.org", l->hostname_);
+ EXPECT_EQ(7654321, l->iaid_);
+}
+
+
+// Check that lease6-update correctly handles case when there is
+// no lease to be updated.
+TEST_F(LeaseCmdsTest, Lease6UpdateNoLease) {
+
+ // Initialize lease manager (true = v6, false = don't add any lease)
+ initLeaseMgr(true, false);
+
+ // Check that the lease manager pointer is there.
+ ASSERT_TRUE(lmptr_);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8::1\",\n"
+ " \"iaid\": 7654321,\n"
+ " \"duid\": \"88:88:88:88:88:88:88:88\",\n"
+ " \"hostname\": \"newhostname.example.org\""
+ " }\n"
+ "}";
+ string exp_rsp = "failed to update the lease with address 2001:db8::1 - no such lease";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+// Checks that lease6-del can handle a situation when the query is
+// broken (some required parameters are missing).
+TEST_F(LeaseCmdsTest, Lease4DelMissingParams) {
+
+ // No parameters whatsoever. You want just a lease, any lease?
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {"
+ " }\n"
+ "}";
+ string exp_rsp = "Mandatory 'subnet-id' parameter missing.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Just the subnet-id won't cut it, either.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123"
+ " }\n"
+ "}";
+ exp_rsp = "No 'ip-address' provided and 'identifier-type' is either missing or not a string.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // We can't identify your laptop by color. Sorry, buddy.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"identifier-type\": \"color\",\n"
+ " \"identifier\": \"blue\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Incorrect identifier type: color, the only supported values are: "
+ "address, hw-address, duid";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Query by DUID is not supported in v4. Sorry.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"identifier-type\": \"duid\",\n"
+ " \"identifier\": \"01:01:01:01:01:01\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Delete by duid is not allowed in v4.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Identifier value is missing.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"identifier-type\": \"hw-address\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "No 'ip-address' provided and 'identifier' is either missing or not a string.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Identifier-type is missing.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"identifier\": \"01:02:03:04:05\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "No 'ip-address' provided and 'identifier-type' is either missing or not a string.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+// Checks that lease4-del can handle a situation when the query is
+// valid, but the lease is not there.
+TEST_F(LeaseCmdsTest, Lease4DelByAddrNotFound) {
+
+ // Initialize lease manager (false = v4, true = add a lease)
+ initLeaseMgr(false, true);
+
+ // Invalid
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.5\","
+ " \"subnet-id\": 44"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv4 lease not found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+}
+
+// Checks that lease4-del can return a lease by address.
+TEST_F(LeaseCmdsTest, Lease4DelByAddr) {
+
+ // Initialize lease manager (false = v4, true = add a lease)
+ initLeaseMgr(false, true);
+
+ // Query for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.1\""
+ " }\n"
+ "}";
+ string exp_rsp = "IPv4 lease deleted.";
+ testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Make sure the lease is really gone.
+ EXPECT_FALSE(lmptr_->getLease4(IOAddress("192.0.2.1")));
+}
+
+
+// Checks that lease4-del sanitizes its input.
+TEST_F(LeaseCmdsTest, Lease4DelByAddrBadParam) {
+
+ // Initialize lease manager (false = v4, true = add a lease)
+ initLeaseMgr(false, true);
+
+ // Invalid family
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"2001:db8::1\""
+ " }\n"
+ "}";
+ string exp_rsp = "Invalid IPv4 address specified: 2001:db8::1";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // This is way off
+ cmd =
+ "{\n"
+ " \"command\": \"lease6-del\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"221B Baker St.\""
+ " }\n"
+ "}";
+ exp_rsp = "Failed to convert string to address '221B Baker St.': Invalid argument";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+// Checks that lease4-del can handle a situation when the query is
+// well formed, but the lease is not there.
+TEST_F(LeaseCmdsTest, Lease4DelByHWAddrNotFound) {
+
+ // Initialize lease manager (false = v4, true = add a lease)
+ initLeaseMgr(false, true);
+
+ // No such lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {"
+ " \"identifier-type\": \"hw-address\","
+ " \"identifier\": \"01:02:03:04:05:06\","
+ " \"subnet-id\": 44"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv4 lease not found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+
+ // Make sure the lease is still there.
+ EXPECT_TRUE(lmptr_->getLease4(IOAddress("192.0.2.1")));
+}
+
+// Checks that lease4-del can find a lease by hardware address.
+TEST_F(LeaseCmdsTest, Lease4DelByHWAddr) {
+
+ // Initialize lease manager (false = v4, true = add a lease)
+ initLeaseMgr(false, true);
+
+ // Invalid
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {"
+ " \"identifier-type\": \"hw-address\","
+ " \"identifier\": \"08:08:08:08:08:08\","
+ " \"subnet-id\": 44"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv4 lease deleted.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Make sure the lease is really gone.
+ EXPECT_FALSE(lmptr_->getLease4(IOAddress("192.0.2.1")));
+}
+
+// Checks that lease6-del(addr) can handle a situation when
+// the query is correctly formed, but the lease is not there.
+TEST_F(LeaseCmdsTest, Lease6DelByAddr6NotFound) {
+
+ // Initialize lease manager (true = v6, true = add a lease)
+ initLeaseMgr(true, true);
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-del\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 1,\n"
+ " \"ip-address\": \"2001:db8::2\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv6 lease not found.";
+
+ // Note the status expected is empty. The query completed correctly,
+ // just didn't found the lease.
+ testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+}
+
+// Checks that lease6-del(subnet-id, addr) can handle a situation when
+// the query is correctly formed, but the lease is not there.
+TEST_F(LeaseCmdsTest, Lease6DelByDuidNotFound) {
+
+ // Initialize lease manager (true = v6, true = add a lease)
+ initLeaseMgr(true, true);
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-del\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 1,\n"
+ " \"identifier-type\": \"duid\","
+ " \"identifier\": \"00:01:02:03:04:05:06:07\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv6 lease not found.";
+
+ // Note the status expected is empty. The query completed correctly,
+ // just didn't found the lease.
+ testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+
+ // Make sure the lease is still there.
+ EXPECT_TRUE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8::1")));
+}
+
+// Checks that lease6-del(subnet-id, addr6) can handle a situation when
+// the query is correctly formed and the lease is returned.
+TEST_F(LeaseCmdsTest, Lease6DelByAddr) {
+
+ initLeaseMgr(true, true); // (true = v6, true = create a lease)
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-del\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8::1\""
+ " }\n"
+ "}";
+ string exp_rsp = "IPv6 lease deleted.";
+
+ // The status expected is success. The lease should be deleted.
+ testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Make sure the lease is really gone.
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8::1")));
+}
+
+// Checks that lease6-del sanitizes its input.
+TEST_F(LeaseCmdsTest, Lease6DelByAddrBadParam) {
+
+ // Initialize lease manager (true = v6, true = add a lease)
+ initLeaseMgr(true, true);
+
+ // Invalid family
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-del\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.1\""
+ " }\n"
+ "}";
+ string exp_rsp = "Invalid IPv6 address specified: 192.0.2.1";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // This is way off
+ cmd =
+ "{\n"
+ " \"command\": \"lease6-del\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"221B Baker St.\""
+ " }\n"
+ "}";
+ exp_rsp = "Failed to convert string to address '221B Baker St.': Invalid argument";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+// Checks that lease6-del(subnet-id, type, addr6) can handle a situation when
+// the query is correctly formed and the lease is deleted.
+TEST_F(LeaseCmdsTest, Lease6DelByAddrPrefix) {
+
+ initLeaseMgr(true, false); // (true = v6, false = don't add any leases)
+
+ // Let's start with regular address lease and make it a prefix lease.
+ Lease6Ptr l = createLease6();
+ l->addr_ = IOAddress("2001:db8:1234:ab::");
+ l->type_ = Lease::TYPE_PD;
+ l->prefixlen_ = 56;
+ lmptr_->addLease(l);
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-del\",\n"
+ " \"arguments\": {"
+ " \"type\": \"IA_PD\","
+ " \"ip-address\": \"2001:db8:1234:ab::\""
+ " }\n"
+ "}";
+ string exp_rsp = "IPv6 lease deleted.";
+
+ // The status expected is success. The lease should be deleted.
+ testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Make sure the lease is really gone.
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_PD, IOAddress("2001:db8:1234:ab::")));
+}
+
+// Checks that lease6-del(subnet-id, iaid, identifier-type, identifier) can handle
+// a situation when the query finds a lease.
+TEST_F(LeaseCmdsTest, Lease6DelByDUID) {
+
+ initLeaseMgr(true, true); // (true = v6, true = create a lease)
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-del\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"iaid\": 42,"
+ " \"identifier-type\": \"duid\","
+ " \"identifier\": \"77:77:77:77:77:77:77:77\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv6 lease deleted.";
+
+ // The status expected is success. The lease should be deleted.
+ testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Make sure the lease is really gone.
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8::1")));
+}
+
+// Checks that lease4-wipe detects missing parmameter properly.
+TEST_F(LeaseCmdsTest, Lease4WipeMissingParam) {
+
+ // Initialize lease manager (false = v4, true = add a lease)
+ initLeaseMgr(false, true);
+
+ // Query for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-wipe\",\n"
+ " \"arguments\": {"
+ " }\n"
+ "}";
+ string exp_rsp = "missing parameter 'subnet-id' (<string>:3:19)";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+// Checks that lease4-wipe can remove leases.
+TEST_F(LeaseCmdsTest, Lease4Wipe) {
+
+ // Initialize lease manager (false = v4, true = add a lease)
+ initLeaseMgr(false, true);
+
+ // Query for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-wipe\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44"
+ " }\n"
+ "}";
+ string exp_rsp = "Deleted 1 IPv4 lease(s).";
+ testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Make sure the lease is really gone.
+ EXPECT_FALSE(lmptr_->getLease4(IOAddress("192.0.2.1")));
+}
+
+// Checks that lease4-wipe properly reports when no leases were deleted.
+TEST_F(LeaseCmdsTest, Lease4WipeNoLeases) {
+
+ // Initialize lease manager (false = v4, false = no leases)
+ initLeaseMgr(false, false);
+
+ // Query for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-wipe\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44"
+ " }\n"
+ "}";
+ string exp_rsp = "Deleted 0 IPv4 lease(s).";
+ testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+}
+
+// Checks that lease4-wipe detects missing parmameter properly.
+TEST_F(LeaseCmdsTest, Lease6WipeMissingParam) {
+
+ // Initialize lease manager (true = v6, true = add a lease)
+ initLeaseMgr(true, true);
+
+ // Query for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-wipe\",\n"
+ " \"arguments\": {"
+ " }\n"
+ "}";
+ string exp_rsp = "missing parameter 'subnet-id' (<string>:3:19)";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+// Checks that lease4-wipe can remove leases.
+TEST_F(LeaseCmdsTest, Lease6Wipe) {
+
+ initLeaseMgr(true, true); // (true = v6, true = create a lease)
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-wipe\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Deleted 1 IPv6 lease(s).";
+
+ // The status expected is success. The lease should be deleted.
+ testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Make sure the lease is really gone.
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8::1")));
+}
+
+// Checks that lease4-wipe properly reports when no leases were deleted.
+TEST_F(LeaseCmdsTest, Lease6WipeNoLeases) {
+
+ // Initialize lease manager (false = v4, false = no leases)
+ initLeaseMgr(true, false);
+
+ // Query for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-wipe\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66"
+ " }\n"
+ "}";
+ string exp_rsp = "Deleted 0 IPv6 lease(s).";
+ testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+}
+
+} // end of anonymous namespace
diff --git a/src/hooks/dhcp/lease_cmds/tests/run_unittests.cc b/src/hooks/dhcp/lease_cmds/tests/run_unittests.cc
new file mode 100644
index 0000000000..600d522379
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/tests/run_unittests.cc
@@ -0,0 +1,17 @@
+// Copyright (C) 2017 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 <log/logger_support.h>
+#include <gtest/gtest.h>
+
+int
+main(int argc, char* argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+ isc::log::initLogger();
+ int result = RUN_ALL_TESTS();
+
+ return (result);
+}
diff --git a/src/hooks/dhcp/lease_cmds/version.cc b/src/hooks/dhcp/lease_cmds/version.cc
new file mode 100644
index 0000000000..d3f705769f
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/version.cc
@@ -0,0 +1,17 @@
+// Copyright (C) 2017 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 <hooks/hooks.h>
+
+extern "C" {
+
+/// @brief returns Kea hooks version.
+int version() {
+ return (KEA_HOOKS_VERSION);
+}
+
+}
diff --git a/src/hooks/dhcp/user_chk/pkt_send_co.cc b/src/hooks/dhcp/user_chk/pkt_send_co.cc
index c36c83cf1f..22164c049c 100644
--- a/src/hooks/dhcp/user_chk/pkt_send_co.cc
+++ b/src/hooks/dhcp/user_chk/pkt_send_co.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2015,2017 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
@@ -259,7 +259,7 @@ void add4Option(Pkt4Ptr& response, uint8_t opt_code, std::string& opt_value) {
/// - DOCSIS3_V6_CONFIG_FILE from user property "bootfile"
/// - DOCSIS3_V6_TFTP_SERVERS from user property "tftp_server"
///
-/// @param response IPv5 reponse packet
+/// @param response IPv5 response packet
/// @param user User from whom properties are sourced
void add6Options(Pkt6Ptr& response, const UserPtr& user) {
if (!user) {
diff --git a/src/hooks/dhcp/user_chk/tests/user_file_unittests.cc b/src/hooks/dhcp/user_chk/tests/user_file_unittests.cc
index cdc25cb947..da8e0c8d30 100644
--- a/src/hooks/dhcp/user_chk/tests/user_file_unittests.cc
+++ b/src/hooks/dhcp/user_chk/tests/user_file_unittests.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2017 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
@@ -41,11 +41,11 @@ TEST(UserFile, construction) {
TEST(UserFile, openFile) {
UserFilePtr user_file;
- // Construct a user file that refers to a non existant file.
+ // Construct a user file that refers to a non existing file.
ASSERT_NO_THROW(user_file.reset(new UserFile("NoSuchFile")));
EXPECT_FALSE(user_file->isOpen());
- // Verify a non-existant file fails to open
+ // Verify a non-existing file fails to open
ASSERT_THROW(user_file->open(), UserFileError);
EXPECT_FALSE(user_file->isOpen());
@@ -62,7 +62,7 @@ TEST(UserFile, openFile) {
// Verify that we cannot open an already open file.
ASSERT_THROW(user_file->open(), UserFileError);
- // Verifyt we can close it.
+ // Verify we can close it.
ASSERT_NO_THROW(user_file->close());
EXPECT_FALSE(user_file->isOpen());
@@ -75,7 +75,7 @@ TEST(UserFile, openFile) {
/// @brief Tests makeUser with invalid user strings
TEST(UserFile, makeUser) {
const char* invalid_strs[]= {
- // Missinge type element.
+ // Missing type element.
"{ \"id\" : \"01AC00F03344\" }",
// Invalid id type string value.
"{ \"type\" : \"BOGUS\", \"id\" : \"01AC00F03344\"}",
diff --git a/src/hooks/dhcp/user_chk/tests/user_registry_unittests.cc b/src/hooks/dhcp/user_chk/tests/user_registry_unittests.cc
index 9eb83d0f39..c2660ab6d4 100644
--- a/src/hooks/dhcp/user_chk/tests/user_registry_unittests.cc
+++ b/src/hooks/dhcp/user_chk/tests/user_registry_unittests.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2017 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
@@ -61,7 +61,7 @@ TEST(UserRegistry, userBasics) {
EXPECT_TRUE(found_user);
EXPECT_EQ(found_user->getUserId(), *id);
- // Verify that searching for a non-existant user returns empty user pointer.
+ // Verify that searching for a non-existing user returns empty user pointer.
UserIdPtr id2;
ASSERT_NO_THROW(id2.reset(new UserId(UserId::HW_ADDRESS, "02020202")));
ASSERT_NO_THROW(found_user = reg->findUser(*id2));
diff --git a/src/hooks/dhcp/user_chk/tests/userid_unittests.cc b/src/hooks/dhcp/user_chk/tests/userid_unittests.cc
index 45702da97c..27b8b85cc5 100644
--- a/src/hooks/dhcp/user_chk/tests/userid_unittests.cc
+++ b/src/hooks/dhcp/user_chk/tests/userid_unittests.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2017 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
@@ -50,21 +50,21 @@ TEST(UserIdTest, hwAddress_type) {
EXPECT_EQ(id->getType(), UserId::HW_ADDRESS);
EXPECT_TRUE(bytes == id->getId());
- // Check relational oeprators when a == b.
+ // Check relational operators when a == b.
UserIdPtr id2;
ASSERT_NO_THROW(id2.reset(new UserId(UserId::HW_ADDRESS, id->toText())));
EXPECT_TRUE(*id == *id2);
EXPECT_FALSE(*id != *id2);
EXPECT_FALSE(*id < *id2);
- // Check relational oeprators when a < b.
+ // Check relational operators when a < b.
ASSERT_NO_THROW(id2.reset(new UserId(UserId::HW_ADDRESS,
"01FF02AC030B0709")));
EXPECT_FALSE(*id == *id2);
EXPECT_TRUE(*id != *id2);
EXPECT_TRUE(*id < *id2);
- // Check relational oeprators when a > b.
+ // Check relational operators when a > b.
ASSERT_NO_THROW(id2.reset(new UserId(UserId::HW_ADDRESS,
"01FF02AC030B0707")));
EXPECT_FALSE(*id == *id2);
@@ -98,20 +98,20 @@ TEST(UserIdTest, duid_type) {
EXPECT_EQ(id->getType(), UserId::DUID);
EXPECT_TRUE(bytes == id->getId());
- // Check relational oeprators when a == b.
+ // Check relational operators when a == b.
UserIdPtr id2;
ASSERT_NO_THROW(id2.reset(new UserId(UserId::DUID, id->toText())));
EXPECT_TRUE(*id == *id2);
EXPECT_FALSE(*id != *id2);
EXPECT_FALSE(*id < *id2);
- // Check relational oeprators when a < b.
+ // Check relational operators when a < b.
ASSERT_NO_THROW(id2.reset(new UserId(UserId::DUID, "01FF02AC030B0709")));
EXPECT_FALSE(*id == *id2);
EXPECT_TRUE(*id != *id2);
EXPECT_TRUE(*id < *id2);
- // Check relational oeprators when a > b.
+ // Check relational operators when a > b.
ASSERT_NO_THROW(id2.reset(new UserId(UserId::DUID, "01FF02AC030B0707")));
EXPECT_FALSE(*id == *id2);
EXPECT_TRUE(*id != *id2);
diff --git a/src/hooks/dhcp/user_chk/user.cc b/src/hooks/dhcp/user_chk/user.cc
index 776c8fedc9..64f99fc0f1 100644
--- a/src/hooks/dhcp/user_chk/user.cc
+++ b/src/hooks/dhcp/user_chk/user.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2015,2017 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
@@ -38,7 +38,7 @@ UserId::UserId(UserIdType id_type, const std::string & id_str) :
// Input is expected to be 2-digits per bytes, no delimiters.
std::vector<uint8_t> addr_bytes;
- // Strip out colon delimeters, decodeHex doesn't like them.
+ // Strip out colon delimiters, decodeHex doesn't like them.
std::string clean_id_str = id_str;
std::string::iterator end_pos = std::remove(clean_id_str.begin(),
clean_id_str.end(), ':');
diff --git a/src/hooks/dhcp/user_chk/user_file.h b/src/hooks/dhcp/user_chk/user_file.h
index 88b68cf8f8..513e95acd0 100644
--- a/src/hooks/dhcp/user_chk/user_file.h
+++ b/src/hooks/dhcp/user_chk/user_file.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2015,2017 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
@@ -43,7 +43,7 @@ public:
///
/// Each entry must have a valid entry for "type" and a valid entry or "id".
///
-/// If an entry contains duplicate option names, that option will be assigend
+/// If an entry contains duplicate option names, that option will be assigned
/// the last value found. This is typical JSON behavior.
/// Currently, only string option values (i.e. enclosed in quotes) are
/// supported.
diff --git a/src/hooks/dhcp/user_chk/user_registry.cc b/src/hooks/dhcp/user_chk/user_registry.cc
index f4acbf0231..c232253b71 100644
--- a/src/hooks/dhcp/user_chk/user_registry.cc
+++ b/src/hooks/dhcp/user_chk/user_registry.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2015,2017 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
@@ -85,7 +85,7 @@ void UserRegistry::refresh() {
addUser(user);
}
} catch (const std::exception& ex) {
- // Source was compromsised so restore registry from backup.
+ // Source was compromised so restore registry from backup.
users_ = backup;
// Close the source.
source_->close();