Parcourir la source

[master] Merge branch 'trac5272' (lease_cmds hook added)

Tomek Mrugalski il y a 7 ans
Parent
commit
785c2e521b
35 fichiers modifiés avec 2894 ajouts et 53 suppressions
  1. 2 0
      configure.ac
  2. 1 1
      src/hooks/dhcp/Makefile.am
  3. 4 0
      src/hooks/dhcp/lease_cmds/.gitignore
  4. 63 0
      src/hooks/dhcp/lease_cmds/Makefile.am
  5. 605 0
      src/hooks/dhcp/lease_cmds/lease_cmds.cc
  6. 67 0
      src/hooks/dhcp/lease_cmds/lease_cmds.h
  7. 16 0
      src/hooks/dhcp/lease_cmds/lease_cmds_log.cc
  8. 23 0
      src/hooks/dhcp/lease_cmds/lease_cmds_log.h
  9. 51 0
      src/hooks/dhcp/lease_cmds/lease_cmds_messages.mes
  10. 245 0
      src/hooks/dhcp/lease_cmds/lease_parser.cc
  11. 92 0
      src/hooks/dhcp/lease_cmds/lease_parser.h
  12. 63 0
      src/hooks/dhcp/lease_cmds/load_unload.cc
  13. 5 0
      src/hooks/dhcp/lease_cmds/tests/.gitignore
  14. 57 0
      src/hooks/dhcp/lease_cmds/tests/Makefile.am
  15. 1182 0
      src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc
  16. 17 0
      src/hooks/dhcp/lease_cmds/tests/run_unittests.cc
  17. 17 0
      src/hooks/dhcp/lease_cmds/version.cc
  18. 1 0
      src/lib/cc/Makefile.am
  19. 6 1
      src/lib/cc/command_interpreter.h
  20. 1 1
      src/lib/cc/data.h
  21. 15 0
      src/lib/cc/simple_parser.cc
  22. 17 2
      src/lib/cc/simple_parser.h
  23. 1 0
      src/lib/cc/tests/Makefile.am
  24. 25 0
      src/lib/cc/tests/simple_parser_unittest.cc
  25. 13 0
      src/lib/dhcpsrv/cfg_subnets4.cc
  26. 8 0
      src/lib/dhcpsrv/cfg_subnets4.h
  27. 14 0
      src/lib/dhcpsrv/cfg_subnets6.cc
  28. 8 0
      src/lib/dhcpsrv/cfg_subnets6.h
  29. 57 1
      src/lib/dhcpsrv/lease.cc
  30. 8 1
      src/lib/dhcpsrv/lease.h
  31. 2 21
      src/lib/dhcpsrv/parsers/dhcp_parsers.cc
  32. 0 20
      src/lib/dhcpsrv/parsers/dhcp_parsers.h
  33. 21 1
      src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc
  34. 21 3
      src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc
  35. 166 1
      src/lib/dhcpsrv/tests/lease_unittest.cc

+ 2 - 0
configure.ac

@@ -1630,6 +1630,8 @@ AC_CONFIG_FILES([Makefile
                  src/bin/shell/tests/shell_unittest.py
                  src/bin/shell/tests/shell_unittest.py
                  src/hooks/Makefile
                  src/hooks/Makefile
                  src/hooks/dhcp/Makefile
                  src/hooks/dhcp/Makefile
+                 src/hooks/dhcp/lease_cmds/Makefile
+                 src/hooks/dhcp/lease_cmds/tests/Makefile
                  src/hooks/dhcp/user_chk/Makefile
                  src/hooks/dhcp/user_chk/Makefile
                  src/hooks/dhcp/user_chk/tests/Makefile
                  src/hooks/dhcp/user_chk/tests/Makefile
                  src/hooks/dhcp/user_chk/tests/test_data_files_config.h
                  src/hooks/dhcp/user_chk/tests/test_data_files_config.h

+ 1 - 1
src/hooks/dhcp/Makefile.am

@@ -1 +1 @@
-SUBDIRS = user_chk
+SUBDIRS = user_chk lease_cmds

+ 4 - 0
src/hooks/dhcp/lease_cmds/.gitignore

@@ -0,0 +1,4 @@
+/lease_cmds_messages.cc
+/lease_cmds_messages.h
+/s-messages
+/html

+ 63 - 0
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)

+ 605 - 0
src/hooks/dhcp/lease_cmds/lease_cmds.cc

@@ -0,0 +1,605 @@
+// 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-del-all
+    /// - lease6-del-all
+
+    /// @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-del-all
+    /// - lease6-del-all
+    ///
+    /// @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, lease6-del command handler
+    ///
+    /// This command attempts to delete 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 command by (subnet-id, address):
+    /// {
+    ///     "command": "lease4-del",
+    ///     "arguments": {
+    ///         "subnet-id": 1,
+    ///         "ip-address": "192.0.2.202"
+    ///     }
+    /// }
+    ///
+    /// Example command for query by (subnet-id, identifier-type, identifier)
+    /// {
+    ///     "command": "lease4-del",
+    ///     "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-del",
+    ///     "arguments": {
+    ///     "subnet-id": 66,
+    ///     "iaid": 42,
+    ///     "type": "IA_NA",
+    ///     "identifier-type": "duid",
+    ///     "identifier": "77:77:77:77:77:77:77:77"
+    ///     }
+    /// }
+    /// @param command 'lease4-del' or 'lease6-del'
+    /// @param args must contain lease parameters
+    /// @return result of the operation
+    static ConstElementPtr
+    leaseDelHandler(const string& command, ConstElementPtr args);
+
+    /// @brief Not implemented yet.
+    static ConstElementPtr
+    leaseUpdateHandler(const string& command, ConstElementPtr args);
+
+    /// @brief Not implemented yet.
+    static ConstElementPtr
+    leaseWipeHandler(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 args - arguments passed to command
+    /// @return parsed parameters
+    /// @throw BadValue if input arguments don't make sense.
+    static Parameters getParameters(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::leaseDelHandler, _1, _2));
+    CommandMgr::instance().registerCommand("lease6-del",
+    boost::bind(&LeaseCmdsImpl::leaseDelHandler, _1, _2));
+
+    CommandMgr::instance().registerCommand("lease4-update",
+    boost::bind(&LeaseCmdsImpl::leaseUpdateHandler, _1, _2));
+    CommandMgr::instance().registerCommand("lease6-update",
+    boost::bind(&LeaseCmdsImpl::leaseUpdateHandler, _1, _2));
+
+    CommandMgr::instance().registerCommand("lease4-del-all",
+    boost::bind(&LeaseCmdsImpl::leaseWipeHandler, _1, _2));
+    CommandMgr::instance().registerCommand("lease6-del-all",
+    boost::bind(&LeaseCmdsImpl::leaseWipeHandler, _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-del-all");
+    CommandMgr::instance().deregisterCommand("lease6-del-all");
+}
+
+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(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());
+        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(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::leaseDelHandler(const std::string& cmd, ConstElementPtr args) {
+    return (createAnswer(CONTROL_RESULT_ERROR, "not implemented yet."));
+}
+
+ConstElementPtr
+LeaseCmdsImpl::leaseUpdateHandler(const string& cmd, ConstElementPtr args) {
+    return (createAnswer(CONTROL_RESULT_ERROR, "not implemented yet."));
+}
+
+ConstElementPtr
+LeaseCmdsImpl::leaseWipeHandler(const string& cmd, ConstElementPtr args) {
+    return (createAnswer(CONTROL_RESULT_ERROR, "not implemented yet."));
+}
+
+LeaseCmds::LeaseCmds()
+    :impl_(new LeaseCmdsImpl()) {
+}
+
+LeaseCmds::~LeaseCmds() {
+    impl_.reset();
+}
+
+};
+};

+ 67 - 0
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

+ 16 - 0
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");
+
+}
+}
+

+ 23 - 0
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

+ 51 - 0
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.

+ 245 - 0
src/hooks/dhcp/lease_cmds/lease_parser.cc

@@ -0,0 +1,245 @@
+// 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");
+    }
+
+    // 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");
+    }
+
+    // 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);
+}
+
+};
+};

+ 92 - 0
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

+ 63 - 0
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);
+}
+
+}

+ 5 - 0
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
+*~

+ 57 - 0
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)

Fichier diff supprimé car celui-ci est trop grand
+ 1182 - 0
src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc


+ 17 - 0
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);
+}

+ 17 - 0
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);
+}
+
+}

+ 1 - 0
src/lib/cc/Makefile.am

@@ -12,6 +12,7 @@ libkea_cc_la_SOURCES += json_feed.cc json_feed.h
 libkea_cc_la_SOURCES += simple_parser.cc simple_parser.h
 libkea_cc_la_SOURCES += simple_parser.cc simple_parser.h
 
 
 libkea_cc_la_LIBADD  = $(top_builddir)/src/lib/util/libkea-util.la
 libkea_cc_la_LIBADD  = $(top_builddir)/src/lib/util/libkea-util.la
+libkea_cc_la_LIBADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
 libkea_cc_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
 libkea_cc_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
 libkea_cc_la_LIBADD += $(BOOST_LIBS)
 libkea_cc_la_LIBADD += $(BOOST_LIBS)
 
 

+ 6 - 1
src/lib/cc/command_interpreter.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2009-2015,2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2009-2017 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -41,6 +41,11 @@ const int CONTROL_RESULT_ERROR = 1;
 /// @brief Status code indicating that the specified command is not supported.
 /// @brief Status code indicating that the specified command is not supported.
 const int CONTROL_RESULT_COMMAND_UNSUPPORTED = 2;
 const int CONTROL_RESULT_COMMAND_UNSUPPORTED = 2;
 
 
+/// @brief Status code indicating that the specified command was completed
+///        correctly, but failed to produce any results. For example, get
+///        completed the search, but couldn't find the object it was looking for.
+const int CONTROL_RESULT_EMPTY = 3;
+
 /// @brief A standard control channel exception that is thrown if a function
 /// @brief A standard control channel exception that is thrown if a function
 /// is there is a problem with one of the messages
 /// is there is a problem with one of the messages
 class CtrlChannelError : public isc::Exception {
 class CtrlChannelError : public isc::Exception {

+ 1 - 1
src/lib/cc/data.h

@@ -326,7 +326,7 @@ public:
     virtual void remove(const std::string& name);
     virtual void remove(const std::string& name);
 
 
     /// Checks if there is data at the given key
     /// Checks if there is data at the given key
-    /// @param name The key of the Element to remove
+    /// @param name The key of the Element checked for existence
     /// @return true if there is data at the key, false if not.
     /// @return true if there is data at the key, false if not.
     virtual bool contains(const std::string& name) const;
     virtual bool contains(const std::string& name) const;
 
 

+ 15 - 0
src/lib/cc/simple_parser.cc

@@ -5,12 +5,14 @@
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 
 #include <cc/simple_parser.h>
 #include <cc/simple_parser.h>
+#include <asiolink/io_address.h>
 #include <boost/foreach.hpp>
 #include <boost/foreach.hpp>
 #include <boost/lexical_cast.hpp>
 #include <boost/lexical_cast.hpp>
 #include <cc/data.h>
 #include <cc/data.h>
 #include <string>
 #include <string>
 
 
 using namespace std;
 using namespace std;
+using namespace isc::asiolink;
 using isc::dhcp::DhcpConfigError;
 using isc::dhcp::DhcpConfigError;
 
 
 namespace isc {
 namespace isc {
@@ -67,6 +69,19 @@ SimpleParser::getBoolean(isc::data::ConstElementPtr scope, const std::string& na
     return (x->boolValue());
     return (x->boolValue());
 }
 }
 
 
+IOAddress
+SimpleParser::getAddress(const ConstElementPtr& scope,
+                         const std::string& name) {
+    std::string str = getString(scope, name);
+    try {
+        return (IOAddress(str));
+    } catch (const std::exception& e) {
+        isc_throw(DhcpConfigError, "Failed to convert '" << str
+                  << "' to address: " << e.what() << "("
+                  << getPosition(name, scope) << ")");
+    }
+}
+
 const data::Element::Position&
 const data::Element::Position&
 SimpleParser::getPosition(const std::string& name, const data::ConstElementPtr parent) {
 SimpleParser::getPosition(const std::string& name, const data::ConstElementPtr parent) {
     if (!parent) {
     if (!parent) {

+ 17 - 2
src/lib/cc/simple_parser.h

@@ -7,6 +7,7 @@
 #ifndef SIMPLE_PARSER_H
 #ifndef SIMPLE_PARSER_H
 #define SIMPLE_PARSER_H
 #define SIMPLE_PARSER_H
 
 
+#include <asiolink/io_address.h>
 #include <cc/data.h>
 #include <cc/data.h>
 #include <cc/dhcp_config_error.h>
 #include <cc/dhcp_config_error.h>
 #include <vector>
 #include <vector>
@@ -114,8 +115,6 @@ class SimpleParser {
     static const data::Element::Position&
     static const data::Element::Position&
     getPosition(const std::string& name, const data::ConstElementPtr parent);
     getPosition(const std::string& name, const data::ConstElementPtr parent);
 
 
-protected:
-
     /// @brief Returns a string parameter from a scope
     /// @brief Returns a string parameter from a scope
     ///
     ///
     /// Unconditionally returns a parameter.
     /// Unconditionally returns a parameter.
@@ -152,6 +151,22 @@ protected:
     static bool getBoolean(isc::data::ConstElementPtr scope,
     static bool getBoolean(isc::data::ConstElementPtr scope,
                            const std::string& name);
                            const std::string& name);
 
 
+
+    /// @brief Returns a IOAddress parameter from a scope
+    ///
+    /// Unconditionally returns a parameter.
+    ///
+    /// @param scope specified parameter will be extracted from this scope
+    /// @param name name of the parameter
+    /// @return an IOAddress representing the value of the parameter
+    /// @throw DhcpConfigError if the parameter is not there or is not of
+    /// appropriate type (or its conversion to IOAddress fails due to not
+    /// being a proper address).
+    static isc::asiolink::IOAddress
+    getAddress(const ConstElementPtr& scope, const std::string& name);
+
+protected:
+
     /// @brief Returns an integer value with range checking from a scope
     /// @brief Returns an integer value with range checking from a scope
     ///
     ///
     /// This template should be instantiated in parsers when useful
     /// This template should be instantiated in parsers when useful

+ 1 - 0
src/lib/cc/tests/Makefile.am

@@ -24,6 +24,7 @@ run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 
 
 run_unittests_LDADD =  $(top_builddir)/src/lib/cc/libkea-cc.la
 run_unittests_LDADD =  $(top_builddir)/src/lib/cc/libkea-cc.la
 run_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
 run_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
+run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/threads/libkea-threads.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/threads/libkea-threads.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la

+ 25 - 0
src/lib/cc/tests/simple_parser_unittest.cc

@@ -9,6 +9,7 @@
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
 using namespace isc::data;
 using namespace isc::data;
+using namespace isc::asiolink;
 using isc::dhcp::DhcpConfigError;
 using isc::dhcp::DhcpConfigError;
 
 
 /// This table defines sample default values. Although these are DHCPv6
 /// This table defines sample default values. Although these are DHCPv6
@@ -210,3 +211,27 @@ TEST_F(SimpleParserTest, getAndConvert) {
     EXPECT_THROW(parser.getAsBool(bad_bool, "bar"), DhcpConfigError);
     EXPECT_THROW(parser.getAsBool(bad_bool, "bar"), DhcpConfigError);
 }
 }
 
 
+// This test exercises the getIOAddress
+TEST_F(SimpleParserTest, getIOAddress) {
+
+    SimpleParserClassTest parser;
+
+    // getAddress checks it can be found
+    ElementPtr not_found = Element::fromJSON("{ \"bar\": 1 }");
+    EXPECT_THROW(parser.getAddress(not_found, "foo"), DhcpConfigError);
+
+    // getAddress checks if it is a string
+    ElementPtr not_addr = Element::fromJSON("{ \"foo\": 1234 }");
+    EXPECT_THROW(parser.getAddress(not_addr, "foo"), DhcpConfigError);
+
+    // checks if getAddress can return the expected value of v4 address
+    ElementPtr v4 = Element::fromJSON("{ \"foo\": \"192.0.2.1\" }");
+    IOAddress val("::");
+    EXPECT_NO_THROW(val = parser.getAddress(v4, "foo"));
+    EXPECT_EQ("192.0.2.1" , val.toText());
+
+    // checks if getAddress can return the expected value of v4 address
+    ElementPtr v6 = Element::fromJSON("{ \"foo\": \"2001:db8::1\" }");
+    EXPECT_NO_THROW(val = parser.getAddress(v6, "foo"));
+    EXPECT_EQ("2001:db8::1" , val.toText());
+}

+ 13 - 0
src/lib/dhcpsrv/cfg_subnets4.cc

@@ -196,6 +196,19 @@ CfgSubnets4::selectSubnet(const std::string& iface,
 }
 }
 
 
 Subnet4Ptr
 Subnet4Ptr
+CfgSubnets4::getSubnet(const SubnetID id) const {
+
+    /// @todo: Once this code is migrated to multi-index container, use
+    /// an index rather than full scan.
+    for (auto subnet = subnets_.begin(); subnet != subnets_.end(); ++subnet) {
+        if ((*subnet)->getID() == id) {
+            return (*subnet);
+        }
+    }
+    return (Subnet4Ptr());
+}
+
+Subnet4Ptr
 CfgSubnets4::selectSubnet(const IOAddress& address,
 CfgSubnets4::selectSubnet(const IOAddress& address,
                  const ClientClasses& client_classes) const {
                  const ClientClasses& client_classes) const {
     for (Subnet4Collection::const_iterator subnet = subnets_.begin();
     for (Subnet4Collection::const_iterator subnet = subnets_.begin();

+ 8 - 0
src/lib/dhcpsrv/cfg_subnets4.h

@@ -97,6 +97,14 @@ public:
     /// or they are insufficient to select a subnet.
     /// or they are insufficient to select a subnet.
     Subnet4Ptr selectSubnet(const SubnetSelector& selector) const;
     Subnet4Ptr selectSubnet(const SubnetSelector& selector) const;
 
 
+    /// @brief Returns subnet with specified subnet-id value
+    ///
+    /// Warning: this method uses full scan. Its use is not recommended for
+    /// packet processing.
+    ///
+    /// @return Subnet (or NULL)
+    Subnet4Ptr getSubnet(const SubnetID id) const;
+
     /// @brief Returns a pointer to a subnet if provided address is in its range.
     /// @brief Returns a pointer to a subnet if provided address is in its range.
     ///
     ///
     /// This method returns a pointer to the subnet if the address passed in
     /// This method returns a pointer to the subnet if the address passed in

+ 14 - 0
src/lib/dhcpsrv/cfg_subnets6.cc

@@ -166,6 +166,20 @@ CfgSubnets6::selectSubnet(const OptionPtr& interface_id,
     return (Subnet6Ptr());
     return (Subnet6Ptr());
 }
 }
 
 
+Subnet6Ptr
+CfgSubnets6::getSubnet(const SubnetID id) const {
+
+    /// @todo: Once this code is migrated to multi-index container, use
+    /// an index rather than full scan.
+    for (auto subnet = subnets_.begin(); subnet != subnets_.end(); ++subnet) {
+        if ((*subnet)->getID() == id) {
+            return (*subnet);
+        }
+    }
+    return (Subnet6Ptr());
+}
+
+
 bool
 bool
 CfgSubnets6::isDuplicate(const Subnet6& subnet) const {
 CfgSubnets6::isDuplicate(const Subnet6& subnet) const {
     for (Subnet6Collection::const_iterator subnet_it = subnets_.begin();
     for (Subnet6Collection::const_iterator subnet_it = subnets_.begin();

+ 8 - 0
src/lib/dhcpsrv/cfg_subnets6.h

@@ -87,6 +87,14 @@ public:
     /// @return Pointer to the selected subnet or NULL if no subnet found.
     /// @return Pointer to the selected subnet or NULL if no subnet found.
     Subnet6Ptr selectSubnet(const SubnetSelector& selector) const;
     Subnet6Ptr selectSubnet(const SubnetSelector& selector) const;
 
 
+    /// @brief Returns subnet with specified subnet-id value
+    ///
+    /// Warning: this method uses full scan. Its use is not recommended for
+    /// packet processing.
+    ///
+    /// @return Subnet (or NULL)
+    Subnet6Ptr getSubnet(const SubnetID id) const;
+
     /// @brief Selects the subnet using a specified address.
     /// @brief Selects the subnet using a specified address.
     ///
     ///
     /// This method searches for the subnet using the specified address. If
     /// This method searches for the subnet using the specified address. If

+ 57 - 1
src/lib/dhcpsrv/lease.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -10,6 +10,7 @@
 #include <iostream>
 #include <iostream>
 
 
 using namespace isc::util;
 using namespace isc::util;
+using namespace isc::data;
 using namespace std;
 using namespace std;
 
 
 namespace isc {
 namespace isc {
@@ -220,6 +221,30 @@ Lease4::operator=(const Lease4& other) {
     return (*this);
     return (*this);
 }
 }
 
 
+isc::data::ElementPtr
+Lease4::toElement() const {
+    // Prepare the map
+    ElementPtr map = Element::createMap();
+    map->set("ip-address", Element::create(addr_.toText()));
+    map->set("subnet-id", Element::create(static_cast<long int>(subnet_id_)));
+    map->set("hw-address", Element::create(hwaddr_->toText(false)));
+
+    if (client_id_) {
+        map->set("client-id", Element::create(client_id_->toText()));
+    }
+
+    map->set("cltt", Element::create(cltt_));
+    map->set("valid-lft", Element::create(static_cast<long int>(valid_lft_)));
+
+    map->set("fqdn-fwd", Element::create(fqdn_fwd_));
+    map->set("fqdn-rev", Element::create(fqdn_rev_));
+    map->set("hostname", Element::create(hostname_));
+
+    map->set("state", Element::create(static_cast<int>(state_)));
+
+    return (map);
+}
+
 Lease6::Lease6(Lease::Type type, const isc::asiolink::IOAddress& addr,
 Lease6::Lease6(Lease::Type type, const isc::asiolink::IOAddress& addr,
                DuidPtr duid, uint32_t iaid, uint32_t preferred, uint32_t valid,
                DuidPtr duid, uint32_t iaid, uint32_t preferred, uint32_t valid,
                uint32_t t1, uint32_t t2, SubnetID subnet_id,
                uint32_t t1, uint32_t t2, SubnetID subnet_id,
@@ -362,6 +387,37 @@ Lease6::operator==(const Lease6& other) const {
             state_ == other.state_);
             state_ == other.state_);
 }
 }
 
 
+isc::data::ElementPtr
+Lease6::toElement() const {
+    // Prepare the map
+    ElementPtr map = Element::createMap();
+    map->set("ip-address", Element::create(addr_.toText()));
+    map->set("type", Element::create(typeToText(type_)));
+    if (type_ == Lease::TYPE_PD) {
+        map->set("prefix-len", Element::create(prefixlen_));
+    }
+    map->set("iaid", Element::create(static_cast<long int>(iaid_)));
+    map->set("duid", Element::create(duid_->toText()));
+    map->set("subnet-id", Element::create(static_cast<long int>(subnet_id_)));
+
+    map->set("cltt", Element::create(cltt_));
+    map->set("preferred-lft", Element::create(static_cast<long int>(preferred_lft_)));
+    map->set("valid-lft", Element::create(static_cast<long int>(valid_lft_)));
+
+    map->set("fqdn-fwd", Element::create(fqdn_fwd_));
+    map->set("fqdn-rev", Element::create(fqdn_rev_));
+    map->set("hostname", Element::create(hostname_));
+
+    if (hwaddr_) {
+        map->set("hw-address", Element::create(hwaddr_->toText(false)));
+    }
+
+    map->set("state", Element::create(static_cast<long int>(state_)));
+
+    return (map);
+}
+
+
 std::ostream&
 std::ostream&
 operator<<(std::ostream& os, const Lease& lease) {
 operator<<(std::ostream& os, const Lease& lease) {
     os << lease.toText();
     os << lease.toText();

+ 8 - 1
src/lib/dhcpsrv/lease.h

@@ -11,6 +11,7 @@
 #include <dhcp/duid.h>
 #include <dhcp/duid.h>
 #include <dhcp/option.h>
 #include <dhcp/option.h>
 #include <dhcp/hwaddr.h>
 #include <dhcp/hwaddr.h>
+#include <cc/cfg_to_element.h>
 
 
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
@@ -25,7 +26,7 @@ typedef uint32_t SubnetID;
 ///
 ///
 /// This structure holds all information that is common between IPv4 and IPv6
 /// This structure holds all information that is common between IPv4 and IPv6
 /// leases.
 /// leases.
-struct Lease {
+struct Lease : public isc::data::CfgToElement {
 
 
     /// @brief Type of lease or pool
     /// @brief Type of lease or pool
     typedef enum {
     typedef enum {
@@ -400,6 +401,9 @@ struct Lease4 : public Lease {
     /// @param probation_period valid lifetime will be set to this value
     /// @param probation_period valid lifetime will be set to this value
     void decline(uint32_t probation_period);
     void decline(uint32_t probation_period);
 
 
+    /// @brief Return the JSON representation of a lease
+    virtual isc::data::ElementPtr toElement() const;
+
     /// @todo: Add DHCPv4 failover related fields here
     /// @todo: Add DHCPv4 failover related fields here
 };
 };
 
 
@@ -532,6 +536,9 @@ struct Lease6 : public Lease {
     ///
     ///
     /// @return String form of the lease
     /// @return String form of the lease
     virtual std::string toText() const;
     virtual std::string toText() const;
+
+    /// @brief Return the JSON representation of a lease
+    virtual isc::data::ElementPtr toElement() const;
 };
 };
 
 
 /// @brief Pointer to a Lease6 structure.
 /// @brief Pointer to a Lease6 structure.

+ 2 - 21
src/lib/dhcpsrv/parsers/dhcp_parsers.cc

@@ -615,23 +615,11 @@ RelayInfoParser::RelayInfoParser(const Option::Universe& family)
     : family_(family) {
     : family_(family) {
 };
 };
 
 
-// Can't use a constructor as a function
-namespace {
-IOAddress buildIOAddress(const std::string& str) { return (IOAddress(str)); }
-};
-
-IOAddress
-RelayInfoParser::getIOAddress(ConstElementPtr scope,
-                              const std::string& name) {
-    return (getAndConvert<IOAddress,
-            buildIOAddress>(scope, name, "address"));
-}
-
 void
 void
 RelayInfoParser::parse(const isc::dhcp::Subnet::RelayInfoPtr& cfg,
 RelayInfoParser::parse(const isc::dhcp::Subnet::RelayInfoPtr& cfg,
                        ConstElementPtr relay_info) {
                        ConstElementPtr relay_info) {
     // There is only one parameter which is mandatory
     // There is only one parameter which is mandatory
-    IOAddress ip = getIOAddress(relay_info, "ip-address");
+    IOAddress ip = getAddress(relay_info, "ip-address");
 
 
     // Check if the address family matches.
     // Check if the address family matches.
     if ((ip.isV4() && family_ != Option::V4) ||
     if ((ip.isV4() && family_ != Option::V4) ||
@@ -919,13 +907,6 @@ SubnetConfigParser::createSubnet(ConstElementPtr params) {
 
 
 //**************************** D2ClientConfigParser **********************
 //**************************** D2ClientConfigParser **********************
 
 
-IOAddress
-D2ClientConfigParser::getIOAddress(ConstElementPtr scope,
-                                   const std::string& name) {
-    return (getAndConvert<IOAddress,
-            buildIOAddress>(scope, name, "address"));
-}
-
 dhcp_ddns::NameChangeProtocol
 dhcp_ddns::NameChangeProtocol
 D2ClientConfigParser::getProtocol(ConstElementPtr scope,
 D2ClientConfigParser::getProtocol(ConstElementPtr scope,
                                   const std::string& name) {
                                   const std::string& name) {
@@ -957,7 +938,7 @@ D2ClientConfigParser::parse(isc::data::ConstElementPtr client_config) {
     // Get all parameters that are needed to create the D2ClientConfig.
     // Get all parameters that are needed to create the D2ClientConfig.
     bool enable_updates = getBoolean(client_config, "enable-updates");
     bool enable_updates = getBoolean(client_config, "enable-updates");
 
 
-    IOAddress server_ip = getIOAddress(client_config, "server-ip");
+    IOAddress server_ip = getAddress(client_config, "server-ip");
 
 
     uint32_t server_port = getUint32(client_config, "server-port");
     uint32_t server_port = getUint32(client_config, "server-port");
 
 

+ 0 - 20
src/lib/dhcpsrv/parsers/dhcp_parsers.h

@@ -639,16 +639,6 @@ public:
 
 
 private:
 private:
 
 
-    /// @brief Returns a value converted to IOAddress
-    ///
-    /// Instantiation of getAndConvert() to IOAddress
-    ///
-    /// @param scope specified parameter will be extracted from this scope
-    /// @param name name of the parameter
-    /// @return an IOAddress value
-    isc::asiolink::IOAddress
-    getIOAddress(isc::data::ConstElementPtr scope, const std::string& name);
-
     /// Protocol family (IPv4 or IPv6)
     /// Protocol family (IPv4 or IPv6)
     Option::Universe family_;
     Option::Universe family_;
 };
 };
@@ -792,16 +782,6 @@ public:
 
 
 private:
 private:
 
 
-    /// @brief Returns a value converted to IOAddress
-    ///
-    /// Instantiation of getAndConvert() to IOAddress
-    ///
-    /// @param scope specified parameter will be extracted from this scope
-    /// @param name name of the parameter
-    /// @return an IOAddress value
-    isc::asiolink::IOAddress
-    getIOAddress(isc::data::ConstElementPtr scope, const std::string& name);
-
     /// @brief Returns a value converted to NameChangeProtocol
     /// @brief Returns a value converted to NameChangeProtocol
     ///
     ///
     /// Instantiation of getAndConvert() to NameChangeProtocol
     /// Instantiation of getAndConvert() to NameChangeProtocol

+ 21 - 1
src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc

@@ -524,7 +524,7 @@ TEST(CfgSubnets4Test, unparsePool) {
     subnet->addPool(pool1);
     subnet->addPool(pool1);
     subnet->addPool(pool2);
     subnet->addPool(pool2);
     cfg.add(subnet);
     cfg.add(subnet);
-    
+
     // Unparse
     // Unparse
     std::string expected = "[\n"
     std::string expected = "[\n"
         "{\n"
         "{\n"
@@ -555,4 +555,24 @@ TEST(CfgSubnets4Test, unparsePool) {
     runToElementTest<CfgSubnets4>(expected, cfg);
     runToElementTest<CfgSubnets4>(expected, cfg);
 }
 }
 
 
+// This test verifies that it is possible to retrieve a subnet using subnet-id.
+TEST(CfgSubnets4Test, getSubnet) {
+    CfgSubnets4 cfg;
+
+    // Create 3 subnets.
+    Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), 26, 1, 2, 3, 100));
+    Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.64"), 26, 1, 2, 3, 200));
+    Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"), 26, 1, 2, 3, 300));
+
+    // Add one subnet and make sure it is returned.
+    cfg.add(subnet1);
+    cfg.add(subnet2);
+    cfg.add(subnet3);
+
+    EXPECT_EQ(subnet1, cfg.getSubnet(100));
+    EXPECT_EQ(subnet2, cfg.getSubnet(200));
+    EXPECT_EQ(subnet3, cfg.getSubnet(300));
+    EXPECT_EQ(Subnet4Ptr(), cfg.getSubnet(400)); // no such subnet
+}
+
 } // end of anonymous namespace
 } // end of anonymous namespace

+ 21 - 3
src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015,2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -434,7 +434,7 @@ TEST(CfgSubnets6Test, unparsePool) {
     subnet->addPool(pool1);
     subnet->addPool(pool1);
     subnet->addPool(pool2);
     subnet->addPool(pool2);
     cfg.add(subnet);
     cfg.add(subnet);
-    
+
     // Unparse
     // Unparse
     std::string expected = "[\n"
     std::string expected = "[\n"
         "{\n"
         "{\n"
@@ -480,7 +480,7 @@ TEST(CfgSubnets6Test, unparsePdPool) {
     subnet->addPool(pdpool1);
     subnet->addPool(pdpool1);
     subnet->addPool(pdpool2);
     subnet->addPool(pdpool2);
     cfg.add(subnet);
     cfg.add(subnet);
-    
+
     // Unparse
     // Unparse
     std::string expected = "[\n"
     std::string expected = "[\n"
         "{\n"
         "{\n"
@@ -518,4 +518,22 @@ TEST(CfgSubnets6Test, unparsePdPool) {
     runToElementTest<CfgSubnets6>(expected, cfg);
     runToElementTest<CfgSubnets6>(expected, cfg);
 }
 }
 
 
+// This test verifies that it is possible to retrieve a subnet using subnet-id.
+TEST(CfgSubnets6Test, getSubnet) {
+    CfgSubnets6 cfg;
+
+    // Let's configure 3 subnets
+    Subnet6Ptr subnet1(new Subnet6(IOAddress("2001:db8:1::"), 48, 1, 2, 3, 4, 100));
+    Subnet6Ptr subnet2(new Subnet6(IOAddress("2001:db8:2::"), 48, 1, 2, 3, 4, 200));
+    Subnet6Ptr subnet3(new Subnet6(IOAddress("2001:db8:3::"), 48, 1, 2, 3, 4, 300));
+    cfg.add(subnet1);
+    cfg.add(subnet2);
+    cfg.add(subnet3);
+
+    EXPECT_EQ(subnet1, cfg.getSubnet(100));
+    EXPECT_EQ(subnet2, cfg.getSubnet(200));
+    EXPECT_EQ(subnet3, cfg.getSubnet(300));
+    EXPECT_EQ(Subnet6Ptr(), cfg.getSubnet(400)); // no such subnet
+}
+
 } // end of anonymous namespace
 } // end of anonymous namespace

+ 166 - 1
src/lib/dhcpsrv/tests/lease_unittest.cc

@@ -9,6 +9,8 @@
 #include <dhcp/duid.h>
 #include <dhcp/duid.h>
 #include <dhcpsrv/lease.h>
 #include <dhcpsrv/lease.h>
 #include <util/pointer_util.h>
 #include <util/pointer_util.h>
+#include <testutils/test_to_element.h>
+#include <cc/data.h>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 #include <vector>
 #include <vector>
 #include <sstream>
 #include <sstream>
@@ -16,6 +18,8 @@
 using namespace isc;
 using namespace isc;
 using namespace isc::asiolink;
 using namespace isc::asiolink;
 using namespace isc::dhcp;
 using namespace isc::dhcp;
+using namespace isc::data;
+using namespace isc::test;
 
 
 namespace {
 namespace {
 
 
@@ -423,6 +427,46 @@ TEST_F(Lease4Test, toText) {
     EXPECT_EQ(expected.str(), lease.toText());
     EXPECT_EQ(expected.str(), lease.toText());
 }
 }
 
 
+// Verify that Lease4 structure can be converted to JSON properly.
+TEST_F(Lease4Test, toElement) {
+
+    const time_t current_time = 12345678;
+    Lease4 lease(IOAddress("192.0.2.3"), hwaddr_, clientid_, 3600, 123,
+                 456, current_time, 789, true, true, "urania.example.org");
+
+    std::string expected = "{"
+        "\"client-id\": \"17:34:e2:ff:09:92:54\","
+        "\"cltt\": 12345678,"
+        "\"fqdn-fwd\": true,"
+        "\"fqdn-rev\": true,"
+        "\"hostname\": \"urania.example.org\","
+        "\"hw-address\": \"08:00:2b:02:3f:4e\","
+        "\"ip-address\": \"192.0.2.3\","
+        "\"state\": 0,"
+        "\"subnet-id\": 789,"
+        "\"valid-lft\": 3600 "
+        "}";
+
+    runToElementTest<Lease4>(expected, lease);
+
+    // Now let's try with a lease without client-id.
+    lease.client_id_.reset();
+
+    expected = "{"
+        "\"cltt\": 12345678,"
+        "\"fqdn-fwd\": true,"
+        "\"fqdn-rev\": true,"
+        "\"hostname\": \"urania.example.org\","
+        "\"hw-address\": \"08:00:2b:02:3f:4e\","
+        "\"ip-address\": \"192.0.2.3\","
+        "\"state\": 0,"
+        "\"subnet-id\": 789,"
+        "\"valid-lft\": 3600 "
+        "}";
+
+    runToElementTest<Lease4>(expected, lease);
+}
+
 // Verify that decline() method properly clears up specific fields.
 // Verify that decline() method properly clears up specific fields.
 TEST_F(Lease4Test, decline) {
 TEST_F(Lease4Test, decline) {
 
 
@@ -803,7 +847,7 @@ TEST(Lease6Test, hasIdenticalFqdn) {
                                                      false, false)));
                                                      false, false)));
 }
 }
 
 
-// Verify that toText() method reports Lease4 structure properly.
+// Verify that toText() method reports Lease6 structure properly.
 TEST(Lease6Test, toText) {
 TEST(Lease6Test, toText) {
 
 
     HWAddrPtr hwaddr(new HWAddr(HWADDR, sizeof(HWADDR), HTYPE_ETHER));
     HWAddrPtr hwaddr(new HWAddr(HWADDR, sizeof(HWADDR), HTYPE_ETHER));
@@ -848,6 +892,127 @@ TEST(Lease6Test, toText) {
     EXPECT_EQ(expected.str(), lease.toText());
     EXPECT_EQ(expected.str(), lease.toText());
 }
 }
 
 
+// Verify that Lease6 structure can be converted to JSON properly.
+// This tests an address lease conversion.
+TEST(Lease6Test, toElementAddress) {
+
+    HWAddrPtr hwaddr(new HWAddr(HWADDR, sizeof(HWADDR), HTYPE_ETHER));
+
+    uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+    DuidPtr duid(new DUID(llt, sizeof(llt)));
+
+    Lease6 lease(Lease::TYPE_NA, IOAddress("2001:db8::1"), duid, 123456,
+                 400, 800, 100, 200, 5678, hwaddr, 128);
+    lease.cltt_ = 12345678;
+    lease.state_ = Lease::STATE_DECLINED;
+    lease.hostname_ = "urania.example.org";
+
+    std::string expected = "{"
+        "\"cltt\": 12345678,"
+        "\"duid\": \"00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f\","
+        "\"fqdn-fwd\": false,"
+        "\"fqdn-rev\": false,"
+        "\"hostname\": \"urania.example.org\","
+        "\"hw-address\": \"08:00:2b:02:3f:4e\","
+        "\"iaid\": 123456,"
+        "\"ip-address\": \"2001:db8::1\","
+        "\"preferred-lft\": 400,"
+        "\"state\": 1,"
+        "\"subnet-id\": 5678,"
+        "\"type\": \"IA_NA\","
+        "\"valid-lft\": 800"
+        "}";
+    
+    runToElementTest<Lease6>(expected, lease);
+
+    // Now let's try with a lease without hardware address.
+    lease.hwaddr_.reset();
+
+    expected = "{"
+        "\"cltt\": 12345678,"
+        "\"duid\": \"00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f\","
+        "\"fqdn-fwd\": false,"
+        "\"fqdn-rev\": false,"
+        "\"hostname\": \"urania.example.org\","
+        "\"iaid\": 123456,"
+        "\"ip-address\": \"2001:db8::1\","
+        "\"preferred-lft\": 400,"
+        "\"state\": 1,"
+        "\"subnet-id\": 5678,"
+        "\"type\": \"IA_NA\","
+        "\"valid-lft\": 800"
+        "}";
+    
+    runToElementTest<Lease6>(expected, lease);
+}
+
+// Verify that Lease6 structure can be converted to JSON properly.
+// This tests an address lease conversion.
+TEST(Lease6Test, toElementPrefix) {
+
+    HWAddrPtr hwaddr(new HWAddr(HWADDR, sizeof(HWADDR), HTYPE_ETHER));
+
+    uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+    DuidPtr duid(new DUID(llt, sizeof(llt)));
+
+    Lease6 lease(Lease::TYPE_PD, IOAddress("2001:db8::"), duid, 123456,
+                 400, 800, 100, 200, 5678, hwaddr, 56);
+    lease.cltt_ = 12345678;
+    lease.state_ = Lease::STATE_DEFAULT;
+    lease.hostname_ = "urania.example.org";
+
+    ElementPtr l = lease.toElement();
+
+    ASSERT_TRUE(l);
+
+    ASSERT_TRUE(l->contains("ip-address"));
+    EXPECT_EQ("2001:db8::", l->get("ip-address")->stringValue());
+
+    ASSERT_TRUE(l->contains("type"));
+    EXPECT_EQ("IA_PD", l->get("type")->stringValue());
+
+    // This is a prefix lease, it must have a prefix length.
+    ASSERT_TRUE(l->contains("prefix-len"));
+    EXPECT_EQ(56, l->get("prefix-len")->intValue());
+
+    ASSERT_TRUE(l->contains("iaid"));
+    EXPECT_EQ(123456, l->get("iaid")->intValue());
+
+    ASSERT_TRUE(l->contains("preferred-lft"));
+    EXPECT_EQ(400, l->get("preferred-lft")->intValue());
+
+    ASSERT_TRUE(l->contains("valid-lft"));
+    EXPECT_EQ(800, l->get("valid-lft")->intValue());
+
+    ASSERT_TRUE(l->contains("duid"));
+    EXPECT_EQ("00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f",
+              l->get("duid")->stringValue());
+
+    ASSERT_TRUE(l->contains("hw-address"));
+    EXPECT_EQ(hwaddr->toText(false), l->get("hw-address")->stringValue());
+
+    ASSERT_TRUE(l->contains("subnet-id"));
+    EXPECT_EQ(5678, l->get("subnet-id")->intValue());
+
+    ASSERT_TRUE(l->contains("state"));
+    EXPECT_EQ(static_cast<int>(Lease::STATE_DEFAULT),
+              l->get("state")->intValue());
+
+    ASSERT_TRUE(l->contains("fqdn-fwd"));
+    EXPECT_FALSE(l->get("fqdn-fwd")->boolValue());
+
+    ASSERT_TRUE(l->contains("fqdn-rev"));
+    EXPECT_FALSE(l->get("fqdn-rev")->boolValue());
+
+    ASSERT_TRUE(l->contains("hostname"));
+    EXPECT_EQ("urania.example.org", l->get("hostname")->stringValue());
+
+    // Now let's try with a lease without hardware address.
+    lease.hwaddr_.reset();
+    l = lease.toElement();
+    EXPECT_FALSE(l->contains("hw-address"));
+}
+
 // Verify that the lease states are correctly returned in the textual format.
 // Verify that the lease states are correctly returned in the textual format.
 TEST(Lease6Test, stateToText) {
 TEST(Lease6Test, stateToText) {
     EXPECT_EQ("default", Lease6::statesToText(Lease::STATE_DEFAULT));
     EXPECT_EQ("default", Lease6::statesToText(Lease::STATE_DEFAULT));