From: Tomek Mrugalski Date: Tue, 22 Apr 2014 13:21:26 +0000 (+0200) Subject: [3400] Initial JSON file read support added in b10-dhcp6 X-Git-Tag: trac3434_base~32^2~26 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c8899cc4eb47d66a16fc4aa1d7f161f5a2922816;p=thirdparty%2Fkea.git [3400] Initial JSON file read support added in b10-dhcp6 - added --with-kea-config=JSON,BIND10 added to configure.ac - JSON-file config backend added for b10-dhcp6 - Config file can now be read from a JSON file --- diff --git a/configure.ac b/configure.ac index 8ebc1f7a2d..640555faea 100644 --- a/configure.ac +++ b/configure.ac @@ -565,6 +565,9 @@ AC_SUBST(PYCOVERAGE) AC_SUBST(PYCOVERAGE_RUN) AC_SUBST(USE_PYCOVERAGE) + + + enable_gtest="no" GTEST_INCLUDES= @@ -1282,6 +1285,32 @@ AC_SUBST(PERL) AC_PATH_PROGS(AWK, gawk awk) AC_SUBST(AWK) + +# Kea configuration backend section +# Currently there are 2 backends available: BIND10 and JSON +# It is possible that we may extend this to accept additional backends. +AC_ARG_WITH(kea-config, + AC_HELP_STRING([--with-kea-config], + [Selects configuration backend; currently available options are: BIND10 (default) or JSON]), + [CONFIG_BACKEND="$withval"], + [CONFIG_BACKEND=BIND10]) + +AM_CONDITIONAL(CONFIG_BACKEND_BIND10, test "x$CONFIG_BACKEND" = "xBIND10") +AM_CONDITIONAL(CONFIG_BACKEND_JSON, test "x$CONFIG_BACKEND" = "xJSON") + +if test "x$CONFIG_BACKEND" = "xBIND10"; then + AC_DEFINE(CONFIG_BACKEND_BIND10, 1, [Define to 1 if Kea config was set to BIND10]) +fi + +if test "x$CONFIG_BACKEND" = "xJSON"; then + AC_DEFINE(CONFIG_BACKEND_JSON, 1, [Define to 1 if Kea config was set to JSON]) +fi + +# Let's sanity check if the specified backend value is allowed +if test "x$CONFIG_BACKEND" != "xBIND10" && test "x$CONFIG_BACKEND" != "xJSON"; then + AC_MSG_ERROR("Invalid configuration backend specified: $CONFIG_BACKEND. The only supported are: BIND10 JSON") +fi + AC_ARG_ENABLE(generate_docs, [AC_HELP_STRING([--enable-generate-docs], [regenerate documentation using Docbook [default=no]])], enable_generate_docs=$enableval, enable_generate_docs=no) @@ -1619,6 +1648,10 @@ SQLite: SQLITE_VERSION: ${SQLITE_VERSION} SQLITE_CFLAGS: ${SQLITE_CFLAGS} SQLITE_LIBS: ${SQLITE_LIBS} + +Kea config backend: + CONFIG_BACKEND: ${CONFIG_BACKEND} + END # Avoid confusion on DNS/DHCP and only mention MySQL if it diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am index 0b4a75fc54..1db81831ff 100644 --- a/src/bin/dhcp6/Makefile.am +++ b/src/bin/dhcp6/Makefile.am @@ -52,10 +52,17 @@ BUILT_SOURCES = spec_config.h dhcp6_messages.h dhcp6_messages.cc pkglibexec_PROGRAMS = b10-dhcp6 b10_dhcp6_SOURCES = main.cc -b10_dhcp6_SOURCES += ctrl_dhcp6_srv.cc ctrl_dhcp6_srv.h -b10_dhcp6_SOURCES += config_parser.cc config_parser.h b10_dhcp6_SOURCES += dhcp6_log.cc dhcp6_log.h b10_dhcp6_SOURCES += dhcp6_srv.cc dhcp6_srv.h +b10_dhcp6_SOURCES += ctrl_dhcp6_srv.h + +if CONFIG_BACKEND_BIND10 +b10_dhcp6_SOURCES += json_config_parser.cc json_config_parser.h ctrl_bind10_dhcp6_srv.cc +endif + +if CONFIG_BACKEND_JSON +b10_dhcp6_SOURCES += json_config_parser.cc json_config_parser.h ctrl_json_dhcp6_srv.cc +endif nodist_b10_dhcp6_SOURCES = dhcp6_messages.h dhcp6_messages.cc EXTRA_DIST += dhcp6_messages.mes diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_bind10_dhcp6_srv.cc similarity index 93% rename from src/bin/dhcp6/ctrl_dhcp6_srv.cc rename to src/bin/dhcp6/ctrl_bind10_dhcp6_srv.cc index dea65ac3eb..0a0f5c629a 100644 --- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc +++ b/src/bin/dhcp6/ctrl_bind10_dhcp6_srv.cc @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include #include @@ -186,7 +186,14 @@ void ControlledDhcpv6Srv::sessionReader(void) { } } -void ControlledDhcpv6Srv::establishSession() { + +bool +ControlledDhcpv6Srv::init(const std::string& /* config_file*/) { + // This is BIND10 configuration backed. It established control session + // that is used to connect to BIND10 framework. + // + // Creates session that will be used to receive commands and updated + // configuration from cfgmgr (or indirectly from user via bindctl). string specfile; if (getenv("B10_FROM_BUILD")) { @@ -242,9 +249,11 @@ void ControlledDhcpv6Srv::establishSession() { LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_CCSESSION_STARTED) .arg(ctrl_socket); IfaceMgr::instance().addExternalSocket(ctrl_socket, sessionReader); + + return (true); } -void ControlledDhcpv6Srv::disconnectSession() { +void ControlledDhcpv6Srv::cleanup() { if (config_session_) { delete config_session_; config_session_ = NULL; @@ -273,7 +282,7 @@ void ControlledDhcpv6Srv::shutdown() { } ControlledDhcpv6Srv::~ControlledDhcpv6Srv() { - disconnectSession(); + cleanup(); server_ = NULL; // forget this instance. There should be no callback anymore // at this stage anyway. @@ -290,5 +299,12 @@ ControlledDhcpv6Srv::execDhcpv6ServerCommand(const std::string& command_id, } } +void +Daemon::loggerInit(const char* log_name, bool verbose, bool stand_alone) { + isc::log::initLogger(log_name, + (verbose ? isc::log::DEBUG : isc::log::INFO), + isc::log::MAX_DEBUG_LEVEL, NULL, !stand_alone); +} + }; }; diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.h b/src/bin/dhcp6/ctrl_dhcp6_srv.h index 62c1813cb6..2cc09f82f8 100644 --- a/src/bin/dhcp6/ctrl_dhcp6_srv.h +++ b/src/bin/dhcp6/ctrl_dhcp6_srv.h @@ -46,11 +46,15 @@ public: /// @brief Destructor. ~ControlledDhcpv6Srv(); - /// @brief Establishes msgq session. + /// @brief Initializes the server. /// - /// Creates session that will be used to receive commands and updated - /// configuration from cfgmgr (or indirectly from user via bindctl). - void establishSession(); + /// Depending on the configuration backend, it establishes msgq session, + /// reads the JSON file from disk or may perform any other setup + /// operation. For specific details, see actual implementation in + /// ctrl_*_dhcp6_srv.cc + /// + /// @return true if initialization was successful, false if it failed + bool init(const std::string& config_file); /// @brief Terminates existing msgq session. /// @@ -59,7 +63,7 @@ public: /// may be received. /// /// It is ok to call this method when session is disconnected already. - void disconnectSession(); + void cleanup(); /// @brief Initiates shutdown procedure for the whole DHCPv6 server. void shutdown(); diff --git a/src/bin/dhcp6/ctrl_json_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_json_dhcp6_srv.cc new file mode 100644 index 0000000000..e351a13057 --- /dev/null +++ b/src/bin/dhcp6/ctrl_json_dhcp6_srv.cc @@ -0,0 +1,137 @@ +// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace isc::asiolink; +using namespace isc::cc; +using namespace isc::config; +using namespace isc::data; +using namespace isc::dhcp; +using namespace isc::log; +using namespace isc::util; +using namespace std; + +namespace isc { +namespace dhcp { + +// Need to provide dummy reader. JSON-file backend does not use any control +// readers for now. Eventually, we may consider having a socket (named socket?) +// that other processes (like IPAM) could write to, triggering specific actions. +// For now, it's a no-op method. +void ControlledDhcpv6Srv::sessionReader(void) { +} + +bool +ControlledDhcpv6Srv::init(const std::string& file_name) { + // This is a configuration backend implementation that reads the + // configuration from a JSON file. + + isc::data::ConstElementPtr json; + isc::data::ConstElementPtr result; + + // Basic sanity check: file name must not be empty. + if (file_name.empty()) { + isc_throw(BadValue, "JSON configuration file not specified"); + } + + try { + // Read contents of the file + string config = readFile(file_name); + + // Try to parse it as JSON + json = Element::fromJSON(config); + + // Use parsed JSON structures to configure the server + result = configureDhcp6Server(*this, json); + + } catch (const std::exception& ex) { + LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL).arg(ex.what()); + isc_throw(BadValue, "Unable to process JSON configuration file:" + + file_name); + } + + if (!result) { + // Undetermined status of the configuration. This should never happen, + // but as the configureDhcp6Server returns a pointer, it is theoretically + // possible that it will return NULL. + LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL) + .arg("Configuration failed: Undefined result of configureDhcp6Server" + "() function after attempting to read " + file_name); + return (false); + } + + // Now check is the returned result is successful (rcode=0) or not + ConstElementPtr comment; /// see @ref isc::config::parseAnswer + int rcode; + comment = parseAnswer(rcode, result); + if (rcode != 0) { + string reason = ""; + if (comment) { + reason = string(" (") + comment->stringValue() + string(")"); + } + LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL).arg(reason); + return (false); + } + + // Configuration may disable or enable interfaces so we have to + // reopen sockets according to new configuration. + openActiveSockets(getPort()); + + // Server will start DDNS communications if its enabled. + this->startD2(); + + return (true); +} + +void ControlledDhcpv6Srv::cleanup() { + // Nothing to do here. No need to disconnect from anything. +} + +ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t port) + : Dhcpv6Srv(port), cc_session_(NULL), config_session_(NULL) { +} + +void ControlledDhcpv6Srv::shutdown() { + + // Stop ASIO transmissions. Even though we didn't use it for + // configuration reading, there may be on-going transmissions + // with D2. + io_service_.stop(); + + Dhcpv6Srv::shutdown(); // Initiate DHCPv6 shutdown procedure. +} + +ControlledDhcpv6Srv::~ControlledDhcpv6Srv() { + cleanup(); +} + +}; +}; diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes index e3903a41c0..828ab65f04 100644 --- a/src/bin/dhcp6/dhcp6_messages.mes +++ b/src/bin/dhcp6/dhcp6_messages.mes @@ -499,10 +499,11 @@ to recover. The IPv6 DHCP server has encountered a fatal error and is terminating. The reason for the failure is included in the message. -% DHCP6_SESSION_FAIL failed to establish BIND 10 session (%1), running stand-alone -The server has failed to establish communication with the rest of BIND -10 and is running in stand-alone mode. (This behavior will change once -the IPv6 DHCP server is properly integrated with the rest of BIND 10.) +% DHCP6_INIT_FAIL failed to initialize Kea server: %1 +The server has failed to establish communication with the rest of BIND 10, +failed to read JSON configuration file or excountered any other critical +issue that prevents it from starting up properly. Attached error message +provides more details about the issue. % DHCP6_SHUTDOWN server shutdown The IPv6 DHCP server has terminated normally. diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h index 24655291d0..bac344dea7 100644 --- a/src/bin/dhcp6/dhcp6_srv.h +++ b/src/bin/dhcp6/dhcp6_srv.h @@ -27,8 +27,7 @@ #include #include #include - -#include +#include #include #include @@ -52,11 +51,7 @@ public: /// that is going to be used as server-identifier, receives incoming /// packets, processes them, manages leases assignment and generates /// appropriate responses. -/// -/// @note Only one instance of this class is instantiated as it encompasses -/// the whole operation of the server. Nothing, however, enforces the -/// singleton status of the object. -class Dhcpv6Srv : public boost::noncopyable { +class Dhcpv6Srv : public Daemon { public: /// @brief defines if certain option may, must or must not appear diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/json_config_parser.cc similarity index 99% rename from src/bin/dhcp6/config_parser.cc rename to src/bin/dhcp6/json_config_parser.cc index 272fe0a8a8..499761f9b5 100644 --- a/src/bin/dhcp6/config_parser.cc +++ b/src/bin/dhcp6/json_config_parser.cc @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/bin/dhcp6/config_parser.h b/src/bin/dhcp6/json_config_parser.h similarity index 100% rename from src/bin/dhcp6/config_parser.h rename to src/bin/dhcp6/json_config_parser.h diff --git a/src/bin/dhcp6/main.cc b/src/bin/dhcp6/main.cc index 269eae4290..34fffa4f7a 100644 --- a/src/bin/dhcp6/main.cc +++ b/src/bin/dhcp6/main.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -38,13 +39,16 @@ using namespace std; namespace { const char* const DHCP6_NAME = "b10-dhcp6"; +const char* const DHCP6_LOGGER_NAME = "bind10"; + void usage() { - cerr << "Usage: " << DHCP6_NAME << " [-v] [-s] [-p number]" << endl; + cerr << "Usage: " << DHCP6_NAME << " [-v] [-s] [-p port_number] [-c cfgfile]" << endl; cerr << " -v: verbose output" << endl; cerr << " -s: stand-alone mode (don't connect to BIND10)" << endl; cerr << " -p number: specify non-standard port number 1-65535 " << "(useful for testing only)" << endl; + cerr << " -c file: specify configuration file" << endl; exit(EXIT_FAILURE); } } // end of anonymous namespace @@ -57,17 +61,20 @@ main(int argc, char* argv[]) { bool stand_alone = false; // Should be connect to BIND10 msgq? bool verbose_mode = false; // Should server be verbose? - while ((ch = getopt(argc, argv, "vsp:")) != -1) { + // The standard config file + std::string config_file(""); + + while ((ch = getopt(argc, argv, "vsp:c:")) != -1) { switch (ch) { case 'v': verbose_mode = true; break; - case 's': + case 's': // stand-alone stand_alone = true; break; - case 'p': + case 'p': // port number try { port_number = boost::lexical_cast(optarg); } catch (const boost::bad_lexical_cast &) { @@ -82,6 +89,10 @@ main(int argc, char* argv[]) { } break; + case 'c': // config file + config_file = optarg; + break; + default: usage(); } @@ -92,39 +103,52 @@ main(int argc, char* argv[]) { usage(); } - // Initialize logging. If verbose, we'll use maximum verbosity. - // If standalone is enabled, do not buffer initial log messages - isc::log::initLogger(DHCP6_NAME, - (verbose_mode ? isc::log::DEBUG : isc::log::INFO), - isc::log::MAX_DEBUG_LEVEL, NULL, !stand_alone); - LOG_INFO(dhcp6_logger, DHCP6_STARTING); - LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_START_INFO) - .arg(getpid()).arg(port_number).arg(verbose_mode ? "yes" : "no") - .arg(stand_alone ? "yes" : "no" ); int ret = EXIT_SUCCESS; + bool status = true; try { + // Initialize logging. If verbose, we'll use maximum verbosity. + // If standalone is enabled, do not buffer initial log messages + Daemon::loggerInit(DHCP6_LOGGER_NAME, verbose_mode, stand_alone); + + LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_START_INFO) + .arg(getpid()).arg(port_number).arg(verbose_mode ? "yes" : "no") + .arg(stand_alone ? "yes" : "no" ); + + LOG_INFO(dhcp6_logger, DHCP6_STARTING); + ControlledDhcpv6Srv server(port_number); + if (!stand_alone) { try { - server.establishSession(); + // Initialize the server, i.e. establish control session + // if BIND10 backend is used or read a configuration file + // + status = server.init(config_file); } catch (const std::exception& ex) { - LOG_ERROR(dhcp6_logger, DHCP6_SESSION_FAIL).arg(ex.what()); - // Let's continue. It is useful to have the ability to run - // DHCP server in stand-alone mode, e.g. for testing - // We do need to make sure logging is no longer buffered - // since then it would not print until dhcp6 is stopped + LOG_ERROR(dhcp6_logger, DHCP6_INIT_FAIL).arg(ex.what()); + + // We should not continue if were told to configure (either read + // config file or establish BIND10 control session). isc::log::LoggerManager log_manager; log_manager.process(); + + cerr << "Failed to initialize server: " << ex.what() << endl; + return (EXIT_FAILURE); } } else { LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_STANDALONE); } + if (!status) { + isc_throw(isc::Unexpected, "Failed to initialize configuration backend."); + } + server.run(); LOG_INFO(dhcp6_logger, DHCP6_SHUTDOWN); } catch (const std::exception& ex) { LOG_FATAL(dhcp6_logger, DHCP6_SERVER_FAILED).arg(ex.what()); + cerr << "Fatal error during start up: " << ex.what() << endl; ret = EXIT_FAILURE; } diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am index 8084a17894..1debf568c2 100644 --- a/src/bin/dhcp6/tests/Makefile.am +++ b/src/bin/dhcp6/tests/Makefile.am @@ -75,18 +75,26 @@ dhcp6_unittests_SOURCES += dhcp6_srv_unittest.cc dhcp6_unittests_SOURCES += fqdn_unittest.cc dhcp6_unittests_SOURCES += hooks_unittest.cc dhcp6_unittests_SOURCES += dhcp6_test_utils.cc dhcp6_test_utils.h -dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc -dhcp6_unittests_SOURCES += config_parser_unittest.cc dhcp6_unittests_SOURCES += d2_unittest.cc d2_unittest.h dhcp6_unittests_SOURCES += marker_file.cc dhcp6_unittests_SOURCES += ../dhcp6_srv.h ../dhcp6_srv.cc dhcp6_unittests_SOURCES += ../dhcp6_log.h ../dhcp6_log.cc -dhcp6_unittests_SOURCES += ../ctrl_dhcp6_srv.cc -dhcp6_unittests_SOURCES += ../config_parser.cc ../config_parser.h dhcp6_unittests_SOURCES += wireshark.cc dhcp6_unittests_SOURCES += dhcp6_client.cc dhcp6_client.h dhcp6_unittests_SOURCES += rebind_unittest.cc +if CONFIG_BACKEND_BIND10 +dhcp6_unittests_SOURCES += ../json_config_parser.cc ../json_config_parser.h ../ctrl_bind10_dhcp6_srv.cc +dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc +dhcp6_unittests_SOURCES += config_parser_unittest.cc +endif + +if CONFIG_BACKEND_JSON +dhcp6_unittests_SOURCES += ../json_config_parser.cc ../json_config_parser.h ../ctrl_json_dhcp6_srv.cc +dhcp6_unittests_SOURCES += config_parser_unittest.cc +endif + + nodist_dhcp6_unittests_SOURCES = ../dhcp6_messages.h ../dhcp6_messages.cc nodist_dhcp6_unittests_SOURCES += marker_file.h test_libraries.h diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc index 42ba1128e5..71a8a90f54 100644 --- a/src/bin/dhcp6/tests/config_parser_unittest.cc +++ b/src/bin/dhcp6/tests/config_parser_unittest.cc @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -600,6 +600,11 @@ TEST_F(Dhcp6ParserTest, multipleSubnets) { ElementPtr json = Element::fromJSON(config); + ofstream out("config.json"); + out << config; + out.close(); + + do { EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json)); ASSERT_TRUE(x); diff --git a/src/bin/dhcp6/tests/d2_unittest.cc b/src/bin/dhcp6/tests/d2_unittest.cc index 73c718ef48..136cdfe486 100644 --- a/src/bin/dhcp6/tests/d2_unittest.cc +++ b/src/bin/dhcp6/tests/d2_unittest.cc @@ -13,7 +13,7 @@ // PERFORMANCE OF THIS SOFTWARE. #include -#include +#include #include #include diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc index 231464bb71..01e5826251 100644 --- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include @@ -197,12 +197,12 @@ TEST_F(Dhcpv6SrvTest, basic) { ASSERT_NO_THROW( { // Skip opening any sockets - srv.reset(new Dhcpv6Srv(0)); + srv.reset(new NakedDhcpv6Srv(0)); }); srv.reset(); ASSERT_NO_THROW({ // open an unpriviledged port - srv.reset(new Dhcpv6Srv(DHCP6_SERVER_PORT + 10000)); + srv.reset(new NakedDhcpv6Srv(DHCP6_SERVER_PORT + 10000)); }); } diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.cc b/src/bin/dhcp6/tests/dhcp6_test_utils.cc index a318a70940..fe722404a3 100644 --- a/src/bin/dhcp6/tests/dhcp6_test_utils.cc +++ b/src/bin/dhcp6/tests/dhcp6_test_utils.cc @@ -14,7 +14,7 @@ #include #include -#include +#include using namespace isc::data; using namespace isc::dhcp; diff --git a/src/bin/dhcp6/tests/hooks_unittest.cc b/src/bin/dhcp6/tests/hooks_unittest.cc index 3b3a66a954..139ef38e9f 100644 --- a/src/bin/dhcp6/tests/hooks_unittest.cc +++ b/src/bin/dhcp6/tests/hooks_unittest.cc @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/bin/dhcp6/tests/rebind_unittest.cc b/src/bin/dhcp6/tests/rebind_unittest.cc index 2de10d1b4f..b6262f7f15 100644 --- a/src/bin/dhcp6/tests/rebind_unittest.cc +++ b/src/bin/dhcp6/tests/rebind_unittest.cc @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am index d19afb01bf..90ba35b2b0 100644 --- a/src/lib/dhcpsrv/Makefile.am +++ b/src/lib/dhcpsrv/Makefile.am @@ -3,6 +3,7 @@ SUBDIRS = . tests dhcp_data_dir = @localstatedir@/@PACKAGE@ AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib -DDHCP_DATA_DIR="\"$(dhcp_data_dir)\"" +AM_CPPFLAGS += -DTOP_BUILDDIR="\"$(top_builddir)\"" AM_CPPFLAGS += $(BOOST_INCLUDES) if HAVE_MYSQL AM_CPPFLAGS += $(MYSQL_CPPFLAGS) @@ -48,6 +49,7 @@ libkea_dhcpsrv_la_SOURCES += csv_lease_file4.cc csv_lease_file4.h libkea_dhcpsrv_la_SOURCES += csv_lease_file6.cc csv_lease_file6.h libkea_dhcpsrv_la_SOURCES += d2_client_cfg.cc d2_client_cfg.h libkea_dhcpsrv_la_SOURCES += d2_client_mgr.cc d2_client_mgr.h +libkea_dhcpsrv_la_SOURCES += daemon.cc daemon.h libkea_dhcpsrv_la_SOURCES += dbaccess_parser.cc dbaccess_parser.h libkea_dhcpsrv_la_SOURCES += dhcpsrv_log.cc dhcpsrv_log.h libkea_dhcpsrv_la_SOURCES += cfgmgr.cc cfgmgr.h @@ -58,6 +60,7 @@ libkea_dhcpsrv_la_SOURCES += lease.cc lease.h libkea_dhcpsrv_la_SOURCES += lease_mgr.cc lease_mgr.h libkea_dhcpsrv_la_SOURCES += lease_mgr_factory.cc lease_mgr_factory.h libkea_dhcpsrv_la_SOURCES += memfile_lease_mgr.cc memfile_lease_mgr.h + if HAVE_MYSQL libkea_dhcpsrv_la_SOURCES += mysql_lease_mgr.cc mysql_lease_mgr.h endif diff --git a/src/lib/dhcpsrv/daemon.cc b/src/lib/dhcpsrv/daemon.cc new file mode 100644 index 0000000000..360f414324 --- /dev/null +++ b/src/lib/dhcpsrv/daemon.cc @@ -0,0 +1,103 @@ +// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/// @brief provides default implementation for basic daemon operations +/// +/// This file provides stub implementations that are expected to be redefined +/// in derived classes (e.g. ControlledDhcpv6Srv) +namespace isc { +namespace dhcp { + +Daemon::Daemon() { +} + +bool Daemon::init(const std::string&) { + return false; +} + +void Daemon::cleanup() { + +} + +void Daemon::shutdown() { + +} + +Daemon::~Daemon() { +} + +std::string Daemon::readFile(const std::string& file_name) { + + // This is the fastest method to read a file according to: + // http://insanecoding.blogspot.com/2011/11/how-to-read-in-file-in-c.html + + std::ifstream infile(file_name.c_str(), std::ios::in | std::ios::binary); + if (infile.is_open()) + { + std::string contents; + infile.seekg(0, std::ios::end); + contents.resize(infile.tellg()); + infile.seekg(0, std::ios::beg); + infile.read(&contents[0], contents.size()); + infile.close(); + return(contents); + } + isc_throw(InvalidOperation, "Failed to read file " << file_name << ",error:" + << errno); +} + +void Daemon::loggerInit(const char* log_name, bool verbose, bool ) { + // This method configures logger. For now it is very simple. + // We'll make it more robust once we add support for JSON-based logging + // configuration. + + using namespace isc::log; + + Severity severity = b10LoggerSeverity(verbose ? isc::log::DEBUG : isc::log::INFO); + + // Set a directory for creating lockfiles when running tests + // @todo: Find out why this is needed. Without this, the logger doesn't + // work. + setenv("B10_LOCKFILE_DIR_FROM_BUILD", TOP_BUILDDIR, 1); + + // Initialize logging + initLogger(log_name, severity, isc::log::MAX_DEBUG_LEVEL, NULL); + + // Now configure logger output to stdout. + /// @todo: Make this configurable as part of #3427. + LoggerSpecification spec(log_name, severity, + b10LoggerDbglevel(isc::log::MAX_DEBUG_LEVEL)); + OutputOption option; + option.destination = OutputOption::DEST_CONSOLE; + option.stream = OutputOption::STR_STDOUT; + + spec.addOutputOption(option); + LoggerManager manager; + manager.process(spec); +} + +}; +}; diff --git a/src/lib/dhcpsrv/daemon.h b/src/lib/dhcpsrv/daemon.h new file mode 100644 index 0000000000..18ea678de9 --- /dev/null +++ b/src/lib/dhcpsrv/daemon.h @@ -0,0 +1,94 @@ +// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include +#include + + +namespace isc { +namespace dhcp { + +/// @brief Base class for all services +/// +/// This is the base class that all daemons (DHCPv4, DHCPv6, D2 and possibly +/// others) are derived from. It provides a standard interface for starting up, +/// reconfiguring, shutting down and several other operations. It also covers +/// some common operations. +/// +/// This class is not expected to be instantiated directly, but rather daemon +/// implementations should derive from it. +/// +/// Methods are not pure virtual, as we need to instantiate basic daemons (e.g. +/// Dhcpv6Srv) in tests, without going through the hassles of implemeting stub +/// methods. +/// +/// @note Only one instance of this class is instantiated as it encompasses +/// the whole operation of the server. Nothing, however, enforces the +/// singleton status of the object. +class Daemon : public boost::noncopyable { + +public: + /// @brief Default constructor + /// + /// Currently it does nothing. + Daemon(); + + /// @brief Initializes the server. + /// + /// Depending on the configuration backend, it establishes msgq session, + /// or reads the + /// Creates session that will be used to receive commands and updated + /// configuration from cfgmgr (or indirectly from user via bindctl). + /// @return true if initialization was successful, false if it failed + virtual bool init(const std::string& config_file); + + /// @brief Performs final deconfiguration. + /// + /// Performs configuration backend specific final clean-up. This is called + /// shortly before the daemon terminates. Depending on backend, it may + /// terminat existing msgq session, close LDAP connection or similar. + /// + /// The daemon is not expected to receive any further commands or + /// configuration updates as it is in final stages of shutdown. + virtual void cleanup(); + + /// @brief Initiates shutdown procedure for the whole DHCPv6 server. + virtual void shutdown(); + + /// @brief Desctructor + /// + /// Having virtual destructor ensures that all derived classes will have + /// virtual destructor as well. + virtual ~Daemon(); + + /// Initializez logger + /// + /// This method initializes logger. I + static void loggerInit(const char* log_name, bool verbose, bool stand_alone); + + protected: + + /// @brief reads file and return its content as a string + /// + /// This is an utility method that is expected to be used by several daemons. + /// It reads contents of a text file and return it as a string. + /// + /// @param file_name name of the file to read + /// @return content of the file + std::string readFile(const std::string& file_name); +}; + +}; // end of isc::dhcp namespace +}; // end of isc namespace diff --git a/src/lib/dhcpsrv/tests/Makefile.am b/src/lib/dhcpsrv/tests/Makefile.am index 8e2d9126ad..c99df9ff32 100644 --- a/src/lib/dhcpsrv/tests/Makefile.am +++ b/src/lib/dhcpsrv/tests/Makefile.am @@ -60,6 +60,7 @@ libdhcpsrv_unittests_SOURCES += csv_lease_file4_unittest.cc libdhcpsrv_unittests_SOURCES += csv_lease_file6_unittest.cc libdhcpsrv_unittests_SOURCES += d2_client_unittest.cc libdhcpsrv_unittests_SOURCES += d2_udp_unittest.cc +libdhcpsrv_unittests_SOURCES += daemon_unittest.cc libdhcpsrv_unittests_SOURCES += dbaccess_parser_unittest.cc libdhcpsrv_unittests_SOURCES += lease_file_io.cc lease_file_io.h libdhcpsrv_unittests_SOURCES += lease_unittest.cc diff --git a/src/lib/dhcpsrv/tests/daemon_unittest.cc b/src/lib/dhcpsrv/tests/daemon_unittest.cc new file mode 100644 index 0000000000..3c5b482c77 --- /dev/null +++ b/src/lib/dhcpsrv/tests/daemon_unittest.cc @@ -0,0 +1,65 @@ +// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include +#include +#include +#include + +using namespace isc; +using namespace isc::dhcp; + +/// Temporary file name used in some tests +const char* TEMP_FILE="temp-file.json"; + +namespace { + +/// @brief Test-friendly version of the daemon +/// +/// This class exposes internal Daemon class methods. +class NakedDaemon: public Daemon { +public: + + using Daemon::readFile; +}; + +TEST(Daemon, readFile) { + NakedDaemon x; + + const char* content = "Horse doesn't eat cucumber salad"; + + // Write sample content to disk + unlink(TEMP_FILE); + std::ofstream write_me(TEMP_FILE); + EXPECT_TRUE(write_me.is_open()); + write_me << content; + write_me.close(); + + // Check that the read content is correct + EXPECT_EQ(std::string(content), x.readFile(TEMP_FILE)); + + unlink(TEMP_FILE); +} + +TEST(Daemon, readFileError) { + NakedDaemon x; + + // Check that the read content is correct + EXPECT_THROW(x.readFile("no-such-file.txt"), isc::InvalidOperation); + + unlink(TEMP_FILE); +} + +};