BIND 10 trac2981, updated. aac023f8e10f6922400400d614d0a0b141e79c53 [2981] Added libreload functionality to DHCPv6 server
BIND 10 source code commits
bind10-changes at lists.isc.org
Thu Jul 25 12:51:45 UTC 2013
The branch, trac2981 has been updated
via aac023f8e10f6922400400d614d0a0b141e79c53 (commit)
via c7b293f631c53764081428f4723b8c30b5a85fa8 (commit)
via e11e8c1488cf3e418fc264f5121d1ef711118264 (commit)
from be68664ae1064cad2e9440fe89511b6166755424 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit aac023f8e10f6922400400d614d0a0b141e79c53
Author: Stephen Morris <stephen at isc.org>
Date: Thu Jul 25 13:39:58 2013 +0100
[2981] Added libreload functionality to DHCPv6 server
commit c7b293f631c53764081428f4723b8c30b5a85fa8
Author: Stephen Morris <stephen at isc.org>
Date: Thu Jul 25 13:20:02 2013 +0100
[2981] Added "libreload" command handling
commit e11e8c1488cf3e418fc264f5121d1ef711118264
Author: Stephen Morris <stephen at isc.org>
Date: Thu Jul 25 12:14:42 2013 +0100
[2981] Configuration changes now load and unload hooks libraries
-----------------------------------------------------------------------
Summary of changes:
configure.ac | 2 +
src/bin/dhcp4/config_parser.cc | 19 ++
src/bin/dhcp4/ctrl_dhcp4_srv.cc | 24 +-
src/bin/dhcp4/dhcp4.spec | 19 ++
src/bin/dhcp4/dhcp4_messages.mes | 5 +
src/bin/dhcp4/tests/Makefile.am | 13 ++
.../{dhcp6 => dhcp4}/tests/callout_library_1.cc | 0
.../{dhcp6 => dhcp4}/tests/callout_library_2.cc | 0
.../tests/callout_library_common.h | 0
src/bin/dhcp4/tests/config_parser_unittest.cc | 245 +++++++++++++++++---
src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc | 68 +++++-
src/bin/dhcp4/tests/marker_file.cc | 77 ++++++
src/bin/dhcp4/tests/marker_file.h.in | 71 ++++++
.../dhcp4}/tests/test_libraries.h.in | 6 +-
src/bin/dhcp6/config_parser.cc | 3 +-
src/bin/dhcp6/ctrl_dhcp6_srv.cc | 18 ++
src/bin/dhcp6/dhcp6_messages.mes | 5 +
src/bin/dhcp6/tests/Makefile.am | 1 +
src/bin/dhcp6/tests/config_parser_unittest.cc | 98 ++------
src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc | 82 ++++++-
src/bin/dhcp6/tests/marker_file.cc | 77 ++++++
src/bin/dhcp6/tests/marker_file.h.in | 49 +++-
src/bin/dhcp6/tests/test_libraries.h.in | 6 +-
src/lib/dhcpsrv/dhcp_parsers.cc | 2 +
src/lib/hooks/hooks_manager.h | 7 -
25 files changed, 760 insertions(+), 137 deletions(-)
copy src/bin/{dhcp6 => dhcp4}/tests/callout_library_1.cc (100%)
copy src/bin/{dhcp6 => dhcp4}/tests/callout_library_2.cc (100%)
copy src/bin/{dhcp6 => dhcp4}/tests/callout_library_common.h (100%)
create mode 100644 src/bin/dhcp4/tests/marker_file.cc
create mode 100644 src/bin/dhcp4/tests/marker_file.h.in
copy src/{lib/dhcpsrv => bin/dhcp4}/tests/test_libraries.h.in (87%)
create mode 100644 src/bin/dhcp6/tests/marker_file.cc
-----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 3a94efa..b14ff5f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1381,6 +1381,8 @@ AC_OUTPUT([doc/version.ent
src/bin/dbutil/run_dbutil.sh
src/bin/dbutil/tests/dbutil_test.sh
src/bin/ddns/ddns.py
+ src/bin/dhcp4/tests/marker_file.h
+ src/bin/dhcp4/tests/test_libraries.h
src/bin/dhcp6/tests/marker_file.h
src/bin/dhcp6/tests/test_libraries.h
src/bin/xfrin/tests/xfrin_test
diff --git a/src/bin/dhcp4/config_parser.cc b/src/bin/dhcp4/config_parser.cc
index 726a4a6..7827c02 100644
--- a/src/bin/dhcp4/config_parser.cc
+++ b/src/bin/dhcp4/config_parser.cc
@@ -364,6 +364,8 @@ DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id) {
globalContext()->string_values_);
} else if (config_id.compare("lease-database") == 0) {
parser = new DbAccessParser(config_id);
+ } else if (config_id.compare("hooks-libraries") == 0) {
+ parser = new HooksLibrariesParser(config_id);
} else {
isc_throw(NotImplemented,
"Parser error: Global configuration parameter not supported: "
@@ -399,6 +401,11 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
ParserPtr option_parser;
ParserPtr iface_parser;
+ // Some of the parsers alter the state of the system in a way that can't
+ // easily be undone. (Or alter it in a way such that undoing the change has
+ // the same risk of failurre as doing the change.)
+ ParserPtr hooks_parser_;
+
// The subnet parsers implement data inheritance by directly
// accessing global storage. For this reason the global data
// parsers must store the parsed data into global storages
@@ -434,6 +441,12 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
// parser and can be run here before any other parsers.
iface_parser = parser;
parser->build(config_pair.second);
+ } else if (config_pair.first == "hooks-libraries") {
+ // Executing commit will alter currently-loaded hooks
+ // libraries. Check if the supplied libraries are valid,
+ // but defer the commit until everything else has committed.
+ hooks_parser_ = parser;
+ parser->build(config_pair.second);
} else {
// Those parsers should be started before other
// parsers so we can call build straight away.
@@ -514,6 +527,12 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
return (answer);
}
+ // Now commit changes that have been validated but not yet committed,
+ // and which can't be rolled back.
+ if (hooks_parser_) {
+ hooks_parser_->commit();
+ }
+
LOG_INFO(dhcp4_logger, DHCP4_CONFIG_COMPLETE).arg(config_details);
// Everything was fine. Configuration is successful.
diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.cc b/src/bin/dhcp4/ctrl_dhcp4_srv.cc
index 11f76da..ad0c5ec 100644
--- a/src/bin/dhcp4/ctrl_dhcp4_srv.cc
+++ b/src/bin/dhcp4/ctrl_dhcp4_srv.cc
@@ -19,24 +19,28 @@
#include <cc/session.h>
#include <config/ccsession.h>
#include <dhcp/iface_mgr.h>
-#include <dhcpsrv/dhcp_config_parser.h>
-#include <dhcpsrv/cfgmgr.h>
+#include <dhcp4/config_parser.h>
#include <dhcp4/ctrl_dhcp4_srv.h>
#include <dhcp4/dhcp4_log.h>
#include <dhcp4/spec_config.h>
-#include <dhcp4/config_parser.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/dhcp_config_parser.h>
#include <exceptions/exceptions.h>
+#include <hooks/hooks_manager.h>
#include <util/buffer.h>
#include <cassert>
#include <iostream>
#include <sstream>
+#include <string>
+#include <vector>
using namespace isc::asiolink;
using namespace isc::cc;
using namespace isc::config;
using namespace isc::data;
using namespace isc::dhcp;
+using namespace isc::hooks;
using namespace isc::log;
using namespace isc::util;
using namespace std;
@@ -142,7 +146,19 @@ ControlledDhcpv4Srv::dhcp4CommandHandler(const string& command, ConstElementPtr
"Shutting down.");
return (answer);
} else if (command == "libreload") {
- // TODO Reload libraries
+ // TODO delete any stored CalloutHandles referring to the old libraries
+ // Get list of currently loaded libraries and reload them.
+ vector<string> loaded = HooksManager::getLibraryNames();
+ bool status = HooksManager::loadLibraries(loaded);
+ if (!status) {
+ LOG_ERROR(dhcp4_logger, DHCP4_RELOAD_FAIL);
+ ConstElementPtr answer = isc::config::createAnswer(1,
+ "Failed to reload hooks libraries.");
+ return (answer);
+ }
+ ConstElementPtr answer = isc::config::createAnswer(0,
+ "Hooks libraries successfully reloaded.");
+ return (answer);
}
ConstElementPtr answer = isc::config::createAnswer(1,
diff --git a/src/bin/dhcp4/dhcp4.spec b/src/bin/dhcp4/dhcp4.spec
index e940cb7..c62705e 100644
--- a/src/bin/dhcp4/dhcp4.spec
+++ b/src/bin/dhcp4/dhcp4.spec
@@ -3,6 +3,19 @@
"module_name": "Dhcp4",
"module_description": "DHCPv4 server daemon",
"config_data": [
+ {
+ "item_name": "hooks-libraries",
+ "item_type": "list",
+ "item_optional": true,
+ "item_default": [],
+ "list_item_spec":
+ {
+ "item_name": "hooks-library",
+ "item_type": "string",
+ "item_optional": true,
+ }
+ },
+
{ "item_name": "interfaces",
"item_type": "list",
"item_optional": false,
@@ -272,7 +285,13 @@
"item_optional": true
}
]
+ },
+
+ {
+ "command_name": "libreload",
+ "command_description": "Reloads the current hooks libraries.",
}
+
]
}
}
diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes
index 5af51f9..2eeda9f 100644
--- a/src/bin/dhcp4/dhcp4_messages.mes
+++ b/src/bin/dhcp4/dhcp4_messages.mes
@@ -226,6 +226,11 @@ a different hardware address. One possible reason for using different
hardware address is that a cloned virtual machine was not updated and
both clones use the same client-id.
+% DHCP4_RELOAD_FAIL reload of hooks libraries failed
+A "libreload" command was issued to reload the hooks libraries but for
+some reason the reload failed. Other error messages issued from the
+hooks framework will indicate the nature of the problem.
+
% DHCP4_RESPONSE_DATA responding with packet type %1, data is <%2>
A debug message listing the data returned to the client.
diff --git a/src/bin/dhcp4/tests/Makefile.am b/src/bin/dhcp4/tests/Makefile.am
index d3c7171..fc7eabf 100644
--- a/src/bin/dhcp4/tests/Makefile.am
+++ b/src/bin/dhcp4/tests/Makefile.am
@@ -32,6 +32,7 @@ AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/bin/dhcp6/tests\"
AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
CLEANFILES = $(builddir)/interfaces.txt $(builddir)/logger_lockfile
+CLEANFILES += $(builddir)/load_marker.txt $(builddir)/unload_marker.txt
AM_CXXFLAGS = $(B10_CXXFLAGS)
if USE_CLANGPP
@@ -48,6 +49,16 @@ TESTS_ENVIRONMENT = \
TESTS =
if HAVE_GTEST
+# Build shared libraries for testing.
+lib_LTLIBRARIES = libco1.la libco2.la
+
+libco1_la_SOURCES = callout_library_1.cc callout_library_common.h
+libco1_la_CXXFLAGS = $(AM_CXXFLAGS)
+libco1_la_CPPFLAGS = $(AM_CPPFLAGS)
+
+libco2_la_SOURCES = callout_library_2.cc callout_library_common.h
+libco2_la_CXXFLAGS = $(AM_CXXFLAGS)
+libco2_la_CPPFLAGS = $(AM_CPPFLAGS)
TESTS += dhcp4_unittests
@@ -58,7 +69,9 @@ dhcp4_unittests_SOURCES += dhcp4_unittests.cc
dhcp4_unittests_SOURCES += dhcp4_srv_unittest.cc
dhcp4_unittests_SOURCES += ctrl_dhcp4_srv_unittest.cc
dhcp4_unittests_SOURCES += config_parser_unittest.cc
+dhcp4_unittests_SOURCES += marker_file.cc
nodist_dhcp4_unittests_SOURCES = ../dhcp4_messages.h ../dhcp4_messages.cc
+nodist_dhcp4_unittests_SOURCES += marker_file.h test_libraries.h
dhcp4_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
dhcp4_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
diff --git a/src/bin/dhcp4/tests/callout_library_1.cc b/src/bin/dhcp4/tests/callout_library_1.cc
new file mode 100644
index 0000000..471bb6f
--- /dev/null
+++ b/src/bin/dhcp4/tests/callout_library_1.cc
@@ -0,0 +1,22 @@
+// Copyright (C) 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.
+
+/// @file
+/// @brief Marker file callout library
+///
+/// This is the source of a test library for the DHCP parser and configuration
+/// tests. See callout_common.cc for details.
+
+static const int LIBRARY_NUMBER = 1;
+#include "callout_library_common.h"
diff --git a/src/bin/dhcp4/tests/callout_library_2.cc b/src/bin/dhcp4/tests/callout_library_2.cc
new file mode 100644
index 0000000..b0b4637
--- /dev/null
+++ b/src/bin/dhcp4/tests/callout_library_2.cc
@@ -0,0 +1,22 @@
+// Copyright (C) 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.
+
+/// @file
+/// @brief Marker file callout library
+///
+/// This is the source of a test library for the DHCP parser and configuration
+/// tests. See callout_common.cc for details.
+
+static const int LIBRARY_NUMBER = 2;
+#include "callout_library_common.h"
diff --git a/src/bin/dhcp4/tests/callout_library_common.h b/src/bin/dhcp4/tests/callout_library_common.h
new file mode 100644
index 0000000..e8d4b5a
--- /dev/null
+++ b/src/bin/dhcp4/tests/callout_library_common.h
@@ -0,0 +1,80 @@
+// Copyright (C) 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.
+
+/// @file
+/// @brief Marker file callout library
+///
+/// This is the source of a test library for the DHCP parser and configuration
+/// tests.
+///
+/// To check that they libraries are loaded and unloaded correctly, the load
+/// and unload functions in this library maintain two marker files - the load
+/// marker file and the unload marker file. The functions append a single
+/// to the single line in the file, creating the file if need be. In
+/// this way, the test code can determine whether the load/unload functions
+/// have been run and, if so, in what order.
+///
+/// This file is the common library file for the tests. It will not compile
+/// by itself - it is included into each callout library which specifies the
+/// missing constant LIBRARY_NUMBER before the inclusion.
+
+#include <hooks/hooks.h>
+#include "marker_file.h"
+
+#include <fstream>
+
+using namespace isc::hooks;
+using namespace std;
+
+extern "C" {
+
+/// @brief Append digit to marker file
+///
+/// If the marker file does not exist, create it. Then append the single
+/// digit (given by the constant LIBRARY_NUMBER) defined earlier to it and
+/// close the file.
+///
+/// @param name Name of the file to open
+///
+/// @return 0 on success, non-zero on error.
+int
+appendDigit(const char* name) {
+ // Open the file and check if successful.
+ fstream file(name, fstream::out | fstream::app);
+ if (!file.good()) {
+ return (1);
+ }
+
+ // Add the library number to it and close.
+ file << LIBRARY_NUMBER;
+ file.close();
+
+ return (0);
+}
+
+// Framework functions
+int
+version() {
+ return (BIND10_HOOKS_VERSION);
+}
+
+int load(LibraryHandle&) {
+ return (appendDigit(LOAD_MARKER_FILE));
+}
+
+int unload() {
+ return (appendDigit(UNLOAD_MARKER_FILE));
+}
+
+};
diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc
index 05c951d..9b56501 100644
--- a/src/bin/dhcp4/tests/config_parser_unittest.cc
+++ b/src/bin/dhcp4/tests/config_parser_unittest.cc
@@ -25,6 +25,10 @@
#include <dhcp/option_int.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/cfgmgr.h>
+#include <hooks/hooks_manager.h>
+
+#include "marker_file.h"
+#include "test_libraries.h"
#include <boost/foreach.hpp>
#include <boost/scoped_ptr.hpp>
@@ -34,12 +38,14 @@
#include <sstream>
#include <limits.h>
-using namespace std;
using namespace isc;
-using namespace isc::dhcp;
using namespace isc::asiolink;
-using namespace isc::data;
using namespace isc::config;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+using namespace isc::hooks;
+using namespace std;
namespace {
@@ -78,6 +84,10 @@ public:
~Dhcp4ParserTest() {
resetConfiguration();
+
+ // ... and delete the hooks library marker files if present
+ unlink(LOAD_MARKER_FILE);
+ unlink(UNLOAD_MARKER_FILE);
};
/// @brief Create the simple configuration with single option.
@@ -236,50 +246,75 @@ public:
expected_data_len));
}
- /// @brief Reset configuration database.
+ /// @brief Parse and Execute configuration
///
- /// This function resets configuration data base by
- /// removing all subnets and option-data. Reset must
- /// be performed after each test to make sure that
- /// contents of the database do not affect result of
- /// subsequent tests.
- void resetConfiguration() {
+ /// Parses a configuration and executes a configuration of the server.
+ /// If the operation fails, the current test will register a failure.
+ ///
+ /// @param config Configuration to parse
+ /// @param operation Operation being performed. In the case of an error,
+ /// the error text will include the string "unable to <operation>.".
+ ///
+ /// @return true if the configuration succeeded, false if not. In the
+ /// latter case, a failure will have been added to the current test.
+ bool
+ executeConfiguration(const std::string& config, const char* operation) {
ConstElementPtr status;
-
- string config = "{ \"interfaces\": [ \"*\" ],"
- "\"rebind-timer\": 2000, "
- "\"renew-timer\": 1000, "
- "\"valid-lifetime\": 4000, "
- "\"subnet4\": [ ], "
- "\"option-def\": [ ], "
- "\"option-data\": [ ] }";
-
try {
ElementPtr json = Element::fromJSON(config);
status = configureDhcp4Server(*srv_, json);
} catch (const std::exception& ex) {
- FAIL() << "Fatal error: unable to reset configuration database"
- << " after the test. The following configuration was used"
- << " to reset database: " << std::endl
+ ADD_FAILURE() << "Unable to " << operation << ". "
+ << "The following configuration was used: " << std::endl
<< config << std::endl
<< " and the following error message was returned:"
<< ex.what() << std::endl;
+ return (false);
}
- // status object must not be NULL
+ // The status object must not be NULL
if (!status) {
- FAIL() << "Fatal error: unable to reset configuration database"
- << " after the test. Configuration function returned"
- << " NULL pointer" << std::endl;
+ ADD_FAILURE() << "Unable to " << operation << ". "
+ << "The configuration function returned a null pointer.";
+ return (false);
}
+ // Store the answer if we need it.
+
+ // Returned value should be 0 (configuration success)
comment_ = parseAnswer(rcode_, status);
- // returned value should be 0 (configuration success)
if (rcode_ != 0) {
- FAIL() << "Fatal error: unable to reset configuration database"
- << " after the test. Configuration function returned"
- << " error code " << rcode_ << std::endl;
+ string reason = "";
+ if (comment_) {
+ reason = string(" (") + comment_->stringValue() + string(")");
+ }
+ ADD_FAILURE() << "Unable to " << operation << ". "
+ << "The configuration function returned error code "
+ << rcode_ << reason;
+ return (false);
}
+
+ return (true);
+ }
+
+ /// @brief Reset configuration database.
+ ///
+ /// This function resets configuration data base by
+ /// removing all subnets and option-data. Reset must
+ /// be performed after each test to make sure that
+ /// contents of the database do not affect result of
+ /// subsequent tests.
+ void resetConfiguration() {
+ string config = "{ \"interfaces\": [ \"*\" ],"
+ "\"hooks-libraries\": [ ], "
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"valid-lifetime\": 4000, "
+ "\"subnet4\": [ ], "
+ "\"option-def\": [ ], "
+ "\"option-data\": [ ] }";
+ static_cast<void>(executeConfiguration(config,
+ "reset configuration database"));
}
boost::scoped_ptr<Dhcpv4Srv> srv_;
@@ -1750,6 +1785,156 @@ TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) {
EXPECT_FALSE(desc.option->getOption(3));
}
+// Tests of the hooks libraries configuration.
+
+// Helper function to return a configuration containing an arbitrary number
+// of hooks libraries.
+std::string
+buildHooksLibrariesConfig(const std::vector<std::string>& libraries) {
+ const string quote("\"");
+
+ // Create the first part of the configuration string.
+ string config =
+ "{ \"interfaces\": [ \"*\" ],"
+ "\"hooks-libraries\": [";
+
+ // Append the libraries (separated by commas if needed)
+ for (int i = 0; i < libraries.size(); ++i) {
+ if (i > 0) {
+ config += string(", ");
+ }
+ config += (quote + libraries[i] + quote);
+ }
+
+ // Append the remainder of the configuration.
+ config += string(
+ "],"
+ "\"rebind-timer\": 2000,"
+ "\"renew-timer\": 1000,"
+ "\"option-data\": [ {"
+ " \"name\": \"dhcp-message\","
+ " \"space\": \"dhcp4\","
+ " \"code\": 56,"
+ " \"data\": \"AB CDEF0105\","
+ " \"csv-format\": False"
+ " },"
+ " {"
+ " \"name\": \"foo\","
+ " \"space\": \"isc\","
+ " \"code\": 56,"
+ " \"data\": \"1234\","
+ " \"csv-format\": True"
+ " } ],"
+ "\"option-def\": [ {"
+ " \"name\": \"foo\","
+ " \"code\": 56,"
+ " \"type\": \"uint32\","
+ " \"array\": False,"
+ " \"record-types\": \"\","
+ " \"space\": \"isc\","
+ " \"encapsulate\": \"\""
+ " } ],"
+ "\"subnet4\": [ { "
+ " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+ " \"subnet\": \"192.0.2.0/24\""
+ " } ]"
+ "}");
+
+ return (config);
+}
+
+// Convenience function for creating hooks library configuration with one or
+// two character string constants.
+std::string
+buildHooksLibrariesConfig(const char* library1 = NULL,
+ const char* library2 = NULL) {
+ std::vector<std::string> libraries;
+ if (library1 != NULL) {
+ libraries.push_back(string(library1));
+ if (library2 != NULL) {
+ libraries.push_back(string(library2));
+ }
+ }
+ return (buildHooksLibrariesConfig(libraries));
+}
+
+
+// The goal of this test is to verify the configuration of hooks libraries if
+// none are specified.
+TEST_F(Dhcp4ParserTest, NoHooksLibraries) {
+ // Ensure that no libraries are loaded at the start of the test.
+ std::vector<std::string> libraries = HooksManager::getLibraryNames();
+ ASSERT_TRUE(libraries.empty());
+
+ // Parse a configuration containing no names.
+ string config = buildHooksLibrariesConfig();
+ if (!executeConfiguration(config,
+ "set configuration with no hooks libraries")) {
+ return;
+ }
+
+ // No libraries should be loaded at the end of the test.
+ libraries = HooksManager::getLibraryNames();
+ ASSERT_TRUE(libraries.empty());
+}
+
+// Verify parsing fails with one library that will fail validation.
+TEST_F(Dhcp4ParserTest, InvalidLibrary) {
+ // Ensure that no libraries are loaded at the start of the test.
+ std::vector<std::string> libraries = HooksManager::getLibraryNames();
+ ASSERT_TRUE(libraries.empty());
+
+ // Parse a configuration containing a failing library.
+ string config = buildHooksLibrariesConfig(NOT_PRESENT_LIBRARY);
+
+ ConstElementPtr status;
+ ElementPtr json = Element::fromJSON(config);
+ ASSERT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+
+ // The status object must not be NULL
+ ASSERT_TRUE(status);
+
+ // Returned value should not be 0
+ comment_ = parseAnswer(rcode_, status);
+ EXPECT_NE(0, rcode_);
+}
+
+// Verify the configuration of hooks libraries with two being specified.
+TEST_F(Dhcp4ParserTest, LibrariesSpecified) {
+ // Ensure that no libraries are loaded at the start of the test.
+ std::vector<std::string> libraries = HooksManager::getLibraryNames();
+ ASSERT_TRUE(libraries.empty());
+
+ // Marker files should not be present.
+ EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, NULL));
+ EXPECT_TRUE(checkMarkerFile(UNLOAD_MARKER_FILE, NULL));
+
+ // Set up the configuration with two libraries and load them.
+ string config = buildHooksLibrariesConfig(CALLOUT_LIBRARY_1,
+ CALLOUT_LIBRARY_2);
+ ASSERT_TRUE(executeConfiguration(config,
+ "load two valid libraries"));
+
+ // Expect two libraries to be loaded in the correct order (load marker file
+ // is present, no unload marker file).
+ libraries = HooksManager::getLibraryNames();
+ ASSERT_EQ(2, libraries.size());
+ EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "12"));
+ EXPECT_TRUE(checkMarkerFile(UNLOAD_MARKER_FILE, NULL));
+
+ // Unload the libraries. The load file should not have changed, but
+ // the unload one should indicate the unload() functions have been run.
+ config = buildHooksLibrariesConfig();
+ ASSERT_TRUE(executeConfiguration(config, "unloading libraries"));
+ EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "12"));
+ EXPECT_TRUE(checkMarkerFile(UNLOAD_MARKER_FILE, "21"));
+
+ // Expect the hooks system to say that none are loaded.
+ libraries = HooksManager::getLibraryNames();
+ EXPECT_TRUE(libraries.empty());
+
+}
+
// This test verifies that it is possible to select subset of interfaces
// on which server should listen.
TEST_F(Dhcp4ParserTest, selectedInterfaces) {
diff --git a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc
index 13b57be..201322e 100644
--- a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc
+++ b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc
@@ -17,6 +17,10 @@
#include <config/ccsession.h>
#include <dhcp/dhcp4.h>
#include <dhcp4/ctrl_dhcp4_srv.h>
+#include <hooks/hooks_manager.h>
+
+#include "marker_file.h"
+#include "test_libraries.h"
#include <boost/scoped_ptr.hpp>
#include <gtest/gtest.h>
@@ -26,13 +30,16 @@
#include <sstream>
#include <arpa/inet.h>
+#include <unistd.h>
using namespace std;
using namespace isc;
-using namespace isc::dhcp;
using namespace isc::asiolink;
-using namespace isc::data;
using namespace isc::config;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+using namespace isc::hooks;
namespace {
@@ -45,10 +52,25 @@ public:
class CtrlDhcpv4SrvTest : public ::testing::Test {
public:
CtrlDhcpv4SrvTest() {
+ reset();
}
~CtrlDhcpv4SrvTest() {
+ reset();
};
+
+ /// @brief Reset hooks data
+ ///
+ /// Resets the data for the hooks-related portion of the test by ensuring
+ /// that no libraries are loaded and that any marker files are deleted.
+ void reset() {
+ // Unload any previously-loaded libraries.
+ HooksManager::unloadLibraries();
+
+ // Get rid of any marker files.
+ static_cast<void>(unlink(LOAD_MARKER_FILE));
+ static_cast<void>(unlink(UNLOAD_MARKER_FILE));
+ }
};
TEST_F(CtrlDhcpv4SrvTest, commands) {
@@ -82,4 +104,46 @@ TEST_F(CtrlDhcpv4SrvTest, commands) {
EXPECT_EQ(0, rcode); // expect success
}
+// Check that the "libreload" command will reload libraries
+
+TEST_F(CtrlDhcpv4SrvTest, libreload) {
+ // Ensure no marker files to start with.
+ ASSERT_FALSE(checkMarkerFileExists(LOAD_MARKER_FILE));
+ ASSERT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
+
+ // Load two libraries
+ std::vector<std::string> libraries;
+ libraries.push_back(CALLOUT_LIBRARY_1);
+ libraries.push_back(CALLOUT_LIBRARY_2);
+ HooksManager::loadLibraries(libraries);
+
+ // Check they are loaded.
+ std::vector<std::string> loaded_libraries =
+ HooksManager::getLibraryNames();
+ ASSERT_TRUE(libraries == loaded_libraries);
+
+ // ... which also included checking that the marker file created by the
+ // load functions exists.
+ EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "12"));
+ EXPECT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
+
+ // Now execute the "libreload" command. This should cause the libraries
+ // to unload and to reload.
+
+ // Use empty parameters list
+ ElementPtr params(new isc::data::MapElement());
+ int rcode = -1;
+
+ ConstElementPtr result =
+ ControlledDhcpv4Srv::execDhcpv4ServerCommand("libreload", params);
+ ConstElementPtr comment = parseAnswer(rcode, result);
+ EXPECT_EQ(0, rcode); // Expect success
+
+ // Check that the libraries have unloaded and reloaded. The libraries are
+ // unloaded in the reverse order to which they are loaded. When they load,
+ // they should append information to the loading marker file.
+ EXPECT_TRUE(checkMarkerFile(UNLOAD_MARKER_FILE, "21"));
+ EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "1212"));
+}
+
} // End of anonymous namespace
diff --git a/src/bin/dhcp4/tests/marker_file.cc b/src/bin/dhcp4/tests/marker_file.cc
new file mode 100644
index 0000000..d05ff60
--- /dev/null
+++ b/src/bin/dhcp4/tests/marker_file.cc
@@ -0,0 +1,77 @@
+// Copyright (C) 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 "marker_file.h"
+
+#include <gtest/gtest.h>
+
+#include <fstream>
+#include <string>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+using namespace std;
+
+// Check the marker file.
+
+bool
+checkMarkerFile(const char* name, const char* expected) {
+ // Open the file for input
+ fstream file(name, fstream::in);
+
+ // Is it open?
+ if (!file.is_open()) {
+
+ // No. This is OK if we don't expected is to be present but is
+ // a failure otherwise.
+ if (expected == NULL) {
+ return (true);
+ }
+ ADD_FAILURE() << "Unable to open " << name << ". It was expected "
+ << "to be present and to contain the string '"
+ << expected << "'";
+ return (false);
+ } else if (expected == NULL) {
+
+ // File is open but we don't expect it to be present.
+ ADD_FAILURE() << "Opened " << name << " but it is not expected to "
+ << "be present.";
+ return (false);
+ }
+
+ // OK, is open, so read the data and see what we have. Compare it
+ // against what is expected.
+ string content;
+ getline(file, content);
+
+ string expected_str(expected);
+ EXPECT_EQ(expected_str, content) << "Data was read from " << name;
+ file.close();
+
+ return (expected_str == content);
+}
+
+// Check if the marker file exists - this is a wrapper for "access(2)" and
+// really tests if the file exists and is accessible
+
+bool
+checkMarkerFileExists(const char* name) {
+ return (access(name, F_OK) == 0);
+}
+
+} // namespace test
+} // namespace dhcp
+} // namespace isc
diff --git a/src/bin/dhcp4/tests/marker_file.h.in b/src/bin/dhcp4/tests/marker_file.h.in
new file mode 100644
index 0000000..45ccbdd
--- /dev/null
+++ b/src/bin/dhcp4/tests/marker_file.h.in
@@ -0,0 +1,71 @@
+// Copyright (C) 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.
+
+#ifndef MARKER_FILE_H
+#define MARKER_FILE_H
+
+/// @file
+/// Define a marker file that is used in tests to prove that an "unload"
+/// function has been called
+
+namespace {
+const char* const LOAD_MARKER_FILE = "@abs_builddir@/load_marker.txt";
+const char* const UNLOAD_MARKER_FILE = "@abs_builddir@/unload_marker.txt";
+}
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+/// @brief Check marker file
+///
+/// This function is used in some of the DHCP server tests.
+///
+/// Marker files are used by the load/unload functions in the hooks
+/// libraries in these tests to signal whether they have been loaded or
+/// unloaded. The file (if present) contains a single line holding
+/// a set of characters.
+///
+/// This convenience function checks the file to see if the characters
+/// are those expected.
+///
+/// @param name Name of the marker file.
+/// @param expected Characters expected. If a marker file is present,
+/// it is expected to contain characters. Therefore a value of NULL
+/// is used to signify that the marker file is not expected to be
+/// present.
+///
+/// @return true if all tests pass, false if not (in which case a failure
+/// will have been logged).
+bool
+checkMarkerFile(const char* name, const char* expected);
+
+/// @brief Check marker file exists
+///
+/// This function is used in some of the DHCP server tests.
+///
+/// Checkes that the specified file does NOT exist.
+///
+/// @param name Name of the marker file.
+///
+/// @return true if file exists, false if not.
+bool
+checkMarkerFileExists(const char* name);
+
+} // namespace test
+} // namespace dhcp
+} // namespace isc
+
+#endif // MARKER_FILE_H
+
diff --git a/src/bin/dhcp4/tests/test_libraries.h.in b/src/bin/dhcp4/tests/test_libraries.h.in
new file mode 100644
index 0000000..8b03dc2
--- /dev/null
+++ b/src/bin/dhcp4/tests/test_libraries.h.in
@@ -0,0 +1,51 @@
+// Copyright (C) 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.
+
+#ifndef TEST_LIBRARIES_H
+#define TEST_LIBRARIES_H
+
+#include <config.h>
+
+namespace {
+
+
+// Take care of differences in DLL naming between operating systems.
+
+#ifdef OS_OSX
+#define DLL_SUFFIX ".dylib"
+
+#else
+#define DLL_SUFFIX ".so"
+
+#endif
+
+
+// Names of the libraries used in these tests. These libraries are built using
+// libtool, so we need to look in the hidden ".libs" directory to locate the
+// shared library.
+
+// Library with load/unload functions creating marker files to check their
+// operation.
+const char* const CALLOUT_LIBRARY_1 = "@abs_builddir@/.libs/libco1"
+ DLL_SUFFIX;
+const char* const CALLOUT_LIBRARY_2 = "@abs_builddir@/.libs/libco2"
+ DLL_SUFFIX;
+
+// Name of a library which is not present.
+const char* const NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere"
+ DLL_SUFFIX;
+} // anonymous namespace
+
+
+#endif // TEST_LIBRARIES_H
diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc
index fb92f88..5117d52 100644
--- a/src/bin/dhcp6/config_parser.cc
+++ b/src/bin/dhcp6/config_parser.cc
@@ -595,7 +595,8 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
return (answer);
}
- // Now commit any changes that have been validated but not yet committed.
+ // Now commit any changes that have been validated but not yet committed,
+ // but which can't be rolled back.
if (hooks_parser) {
hooks_parser->commit();
}
diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc
index dffac61..0d07d8b 100644
--- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc
+++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc
@@ -26,16 +26,20 @@
#include <dhcp6/dhcp6_log.h>
#include <dhcp6/spec_config.h>
#include <exceptions/exceptions.h>
+#include <hooks/hooks_manager.h>
#include <util/buffer.h>
#include <cassert>
#include <iostream>
+#include <string>
+#include <vector>
using namespace isc::asiolink;
using namespace isc::cc;
using namespace isc::config;
using namespace isc::data;
using namespace isc::dhcp;
+using namespace isc::hooks;
using namespace isc::log;
using namespace isc::util;
using namespace std;
@@ -141,6 +145,20 @@ ControlledDhcpv6Srv::dhcp6CommandHandler(const string& command, ConstElementPtr
ConstElementPtr answer = isc::config::createAnswer(0,
"Shutting down.");
return (answer);
+ } else if (command == "libreload") {
+ // TODO delete any stored CalloutHandles referring to the old libraries
+ // Get list of currently loaded libraries and reload them.
+ vector<string> loaded = HooksManager::getLibraryNames();
+ bool status = HooksManager::loadLibraries(loaded);
+ if (!status) {
+ LOG_ERROR(dhcp6_logger, DHCP6_RELOAD_FAIL);
+ ConstElementPtr answer = isc::config::createAnswer(1,
+ "Failed to reload hooks libraries.");
+ return (answer);
+ }
+ ConstElementPtr answer = isc::config::createAnswer(0,
+ "Hooks libraries successfully reloaded.");
+ return (answer);
}
ConstElementPtr answer = isc::config::createAnswer(1,
diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes
index f85fcb4..f4dc8fc 100644
--- a/src/bin/dhcp6/dhcp6_messages.mes
+++ b/src/bin/dhcp6/dhcp6_messages.mes
@@ -246,6 +246,11 @@ mandatory client-id option. This is most likely caused by a buggy client
(or a relay that malformed forwarded message). This request will not be
processed and a response with error status code will be sent back.
+% DHCP6_RELOAD_FAIL reload of hooks libraries failed
+A "libreload" command was issued to reload the hooks libraries but for
+some reason the reload failed. Other error messages issued from the
+hooks framework will indicate the nature of the problem.
+
% DHCP6_RENEW_UNKNOWN_SUBNET RENEW message received from client on unknown subnet (duid=%1, iaid=%2)
A warning message indicating that a client is attempting to renew his lease,
but the server does not have any information about the subnet this client belongs
diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am
index be40ea5..a08d9d1 100644
--- a/src/bin/dhcp6/tests/Makefile.am
+++ b/src/bin/dhcp6/tests/Makefile.am
@@ -61,6 +61,7 @@ dhcp6_unittests_SOURCES = dhcp6_unittests.cc
dhcp6_unittests_SOURCES += dhcp6_srv_unittest.cc
dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc
dhcp6_unittests_SOURCES += config_parser_unittest.cc
+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
diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc
index 389ffea..958aa6c 100644
--- a/src/bin/dhcp6/tests/config_parser_unittest.cc
+++ b/src/bin/dhcp6/tests/config_parser_unittest.cc
@@ -42,13 +42,14 @@
#include <arpa/inet.h>
#include <unistd.h>
-using namespace std;
using namespace isc;
-using namespace isc::dhcp;
using namespace isc::asiolink;
-using namespace isc::data;
using namespace isc::config;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
using namespace isc::hooks;
+using namespace std;
namespace {
@@ -185,7 +186,7 @@ public:
return (stream.str());
}
- /// @brief Parse configuration
+ /// @brief Parse and Execute configuration
///
/// Parses a configuration and executes a configuration of the server.
/// If the operation fails, the current test will register a failure.
@@ -323,61 +324,6 @@ public:
expected_data_len));
}
- /// @brief Check marker file
- ///
- /// Marker files are used by the load/unload functions in the hooks
- /// libraries in these tests to signal whether they have been loaded or
- /// unloaded. The file (if present) contains a single line holding
- /// a set of characters.
- ///
- /// This convenience function checks the file to see if the characters
- /// are those expected.
- ///
- /// @param name Name of the marker file.
- /// @param expected Characters expected. If a marker file is present,
- /// it is expected to contain characters. Therefore a value of NULL
- /// is used to signify that the marker file is not expected to be
- /// present.
- ///
- /// @return true if all tests pass, false if not (in which case a failure
- /// will have been logged).
- bool
- checkMarkerFile(const char* name, const char* expected) {
- // Open the file for input
- fstream file(name, fstream::in);
-
- // Is it open?
- if (!file.is_open()) {
-
- // No. This is OK if we don't expected is to be present but is
- // a failure otherwise.
- if (expected == NULL) {
- return (true);
- }
- ADD_FAILURE() << "Unable to open " << name << ". It was expected "
- << "to be present and to contain the string '"
- << expected << "'";
- return (false);
- } else if (expected == NULL) {
-
- // File is open but we don't expect it to be present.
- ADD_FAILURE() << "Opened " << name << " but it is not expected to "
- << "be present.";
- return (false);
- }
-
- // OK, is open, so read the data and see what we have. Compare it
- // against what is expected.
- string content;
- getline(file, content);
-
- string expected_str(expected);
- EXPECT_EQ(expected_str, content) << "Data was read from " << name;
- file.close();
-
- return (expected_str == content);
- }
-
int rcode_; ///< Return code (see @ref isc::config::parseAnswer)
Dhcpv6Srv srv_; ///< Instance of the Dhcp6Srv used during tests
ConstElementPtr comment_; ///< Comment (see @ref isc::config::parseAnswer)
@@ -2031,8 +1977,9 @@ buildHooksLibrariesConfig(const char* library1 = NULL,
// The goal of this test is to verify the configuration of hooks libraries if
// none are specified.
TEST_F(Dhcp6ParserTest, NoHooksLibraries) {
-// std::vector<std::string> libraries = HooksManager::getLibraryNames();
-// ASSERT_TRUE(libraries.empty());
+ // Ensure that no libraries are loaded at the start of the test.
+ std::vector<std::string> libraries = HooksManager::getLibraryNames();
+ ASSERT_TRUE(libraries.empty());
// Parse a configuration containing no names.
string config = buildHooksLibrariesConfig();
@@ -2040,14 +1987,17 @@ TEST_F(Dhcp6ParserTest, NoHooksLibraries) {
"set configuration with no hooks libraries")) {
return;
}
-// libraries = HooksManager::getLibraryNames();
-// ASSERT_TRUE(libraries.empty());
+
+ // No libraries should be loaded at the end of the test.
+ libraries = HooksManager::getLibraryNames();
+ ASSERT_TRUE(libraries.empty());
}
// Verify parsing fails with one library that will fail validation.
TEST_F(Dhcp6ParserTest, InvalidLibrary) {
-// std::vector<std::string> libraries = HooksManager::getLibraryNames();
-// ASSERT_TRUE(libraries.empty());
+ // Ensure that no libraries are loaded at the start of the test.
+ std::vector<std::string> libraries = HooksManager::getLibraryNames();
+ ASSERT_TRUE(libraries.empty());
// Parse a configuration containing a failing library.
string config = buildHooksLibrariesConfig(NOT_PRESENT_LIBRARY);
@@ -2062,13 +2012,13 @@ TEST_F(Dhcp6ParserTest, InvalidLibrary) {
// Returned value should not be 0
comment_ = parseAnswer(rcode_, status);
EXPECT_NE(0, rcode_);
- std::cerr << "Reason for success: " << comment_;
}
// Verify the configuration of hooks libraries with two being specified.
TEST_F(Dhcp6ParserTest, LibrariesSpecified) {
-// std::vector<std::string> libraries = HooksManager::getLibraryNames();
-// ASSERT_TRUE(libraries.empty());
+ // Ensure that no libraries are loaded at the start of the test.
+ std::vector<std::string> libraries = HooksManager::getLibraryNames();
+ ASSERT_TRUE(libraries.empty());
// Marker files should not be present.
EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, NULL));
@@ -2082,8 +2032,8 @@ TEST_F(Dhcp6ParserTest, LibrariesSpecified) {
// Expect two libraries to be loaded in the correct order (load marker file
// is present, no unload marker file).
-// std::vector<std::string> libraries = HooksManager::getLibraryNames();
-// ASSERT_EQ(2, libraries.size());
+ libraries = HooksManager::getLibraryNames();
+ ASSERT_EQ(2, libraries.size());
EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "12"));
EXPECT_TRUE(checkMarkerFile(UNLOAD_MARKER_FILE, NULL));
@@ -2095,12 +2045,10 @@ TEST_F(Dhcp6ParserTest, LibrariesSpecified) {
EXPECT_TRUE(checkMarkerFile(UNLOAD_MARKER_FILE, "21"));
// Expect the hooks system to say that none are loaded.
- // libraries = HooksManager::getLibraryNames();
- // EXPECT_TRUE(libraries.empty());
+ libraries = HooksManager::getLibraryNames();
+ EXPECT_TRUE(libraries.empty());
- }
-// libraries = HooksManager::getLibraryNames();
-// ASSERT_TRUE(libraries.empty());
+}
// This test verifies that it is possible to select subset of interfaces on
diff --git a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc
index 9209de6..5111e54 100644
--- a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc
+++ b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc
@@ -14,30 +14,37 @@
#include <config.h>
+#include <config/ccsession.h>
#include <dhcp/dhcp6.h>
#include <dhcp6/ctrl_dhcp6_srv.h>
-#include <config/ccsession.h>
+#include <hooks/hooks_manager.h>
+
+#include "marker_file.h"
+#include "test_libraries.h"
#include <boost/scoped_ptr.hpp>
#include <gtest/gtest.h>
-#include <iostream>
#include <fstream>
+#include <iostream>
#include <sstream>
#include <arpa/inet.h>
+#include <unistd.h>
using namespace std;
using namespace isc;
-using namespace isc::dhcp;
using namespace isc::asiolink;
-using namespace isc::data;
using namespace isc::config;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+using namespace isc::hooks;
namespace {
class NakedControlledDhcpv6Srv: public ControlledDhcpv6Srv {
- // "naked" DHCPv6 server, exposes internal fields
+ // "Naked" DHCPv6 server, exposes internal fields
public:
NakedControlledDhcpv6Srv():ControlledDhcpv6Srv(DHCP6_SERVER_PORT + 10000) { }
};
@@ -45,10 +52,25 @@ public:
class CtrlDhcpv6SrvTest : public ::testing::Test {
public:
CtrlDhcpv6SrvTest() {
+ reset();
}
~CtrlDhcpv6SrvTest() {
+ reset();
};
+
+ /// @brief Reset hooks data
+ ///
+ /// Resets the data for the hooks-related portion of the test by ensuring
+ /// that no libraries are loaded and that any marker files are deleted.
+ void reset() {
+ // Unload any previously-loaded libraries.
+ HooksManager::unloadLibraries();
+
+ // Get rid of any marker files.
+ static_cast<void>(unlink(LOAD_MARKER_FILE));
+ static_cast<void>(unlink(UNLOAD_MARKER_FILE));
+ }
};
TEST_F(CtrlDhcpv6SrvTest, commands) {
@@ -62,12 +84,12 @@ TEST_F(CtrlDhcpv6SrvTest, commands) {
ElementPtr params(new isc::data::MapElement());
int rcode = -1;
- // case 1: send bogus command
+ // Case 1: send bogus command
ConstElementPtr result = ControlledDhcpv6Srv::execDhcpv6ServerCommand("blah", params);
ConstElementPtr comment = parseAnswer(rcode, result);
EXPECT_EQ(1, rcode); // expect failure (no such command as blah)
- // case 2: send shutdown command without any parameters
+ // Case 2: send shutdown command without any parameters
result = ControlledDhcpv6Srv::execDhcpv6ServerCommand("shutdown", params);
comment = parseAnswer(rcode, result);
EXPECT_EQ(0, rcode); // expect success
@@ -76,10 +98,52 @@ TEST_F(CtrlDhcpv6SrvTest, commands) {
ConstElementPtr x(new isc::data::IntElement(pid));
params->set("pid", x);
- // case 3: send shutdown command with 1 parameter: pid
+ // Case 3: send shutdown command with 1 parameter: pid
result = ControlledDhcpv6Srv::execDhcpv6ServerCommand("shutdown", params);
comment = parseAnswer(rcode, result);
EXPECT_EQ(0, rcode); // Expect success
}
-} // end of anonymous namespace
+// Check that the "libreload" command will reload libraries
+
+TEST_F(CtrlDhcpv6SrvTest, libreload) {
+ // Ensure no marker files to start with.
+ ASSERT_FALSE(checkMarkerFileExists(LOAD_MARKER_FILE));
+ ASSERT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
+
+ // Load two libraries
+ std::vector<std::string> libraries;
+ libraries.push_back(CALLOUT_LIBRARY_1);
+ libraries.push_back(CALLOUT_LIBRARY_2);
+ HooksManager::loadLibraries(libraries);
+
+ // Check they are loaded.
+ std::vector<std::string> loaded_libraries =
+ HooksManager::getLibraryNames();
+ ASSERT_TRUE(libraries == loaded_libraries);
+
+ // ... which also included checking that the marker file created by the
+ // load functions exists.
+ EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "12"));
+ EXPECT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
+
+ // Now execute the "libreload" command. This should cause the libraries
+ // to unload and to reload.
+
+ // Use empty parameters list
+ ElementPtr params(new isc::data::MapElement());
+ int rcode = -1;
+
+ ConstElementPtr result =
+ ControlledDhcpv6Srv::execDhcpv6ServerCommand("libreload", params);
+ ConstElementPtr comment = parseAnswer(rcode, result);
+ EXPECT_EQ(0, rcode); // Expect success
+
+ // Check that the libraries have unloaded and reloaded. The libraries are
+ // unloaded in the reverse order to which they are loaded. When they load,
+ // they should append information to the loading marker file.
+ EXPECT_TRUE(checkMarkerFile(UNLOAD_MARKER_FILE, "21"));
+ EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "1212"));
+}
+
+} // End of anonymous namespace
diff --git a/src/bin/dhcp6/tests/marker_file.cc b/src/bin/dhcp6/tests/marker_file.cc
new file mode 100644
index 0000000..d05ff60
--- /dev/null
+++ b/src/bin/dhcp6/tests/marker_file.cc
@@ -0,0 +1,77 @@
+// Copyright (C) 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 "marker_file.h"
+
+#include <gtest/gtest.h>
+
+#include <fstream>
+#include <string>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+using namespace std;
+
+// Check the marker file.
+
+bool
+checkMarkerFile(const char* name, const char* expected) {
+ // Open the file for input
+ fstream file(name, fstream::in);
+
+ // Is it open?
+ if (!file.is_open()) {
+
+ // No. This is OK if we don't expected is to be present but is
+ // a failure otherwise.
+ if (expected == NULL) {
+ return (true);
+ }
+ ADD_FAILURE() << "Unable to open " << name << ". It was expected "
+ << "to be present and to contain the string '"
+ << expected << "'";
+ return (false);
+ } else if (expected == NULL) {
+
+ // File is open but we don't expect it to be present.
+ ADD_FAILURE() << "Opened " << name << " but it is not expected to "
+ << "be present.";
+ return (false);
+ }
+
+ // OK, is open, so read the data and see what we have. Compare it
+ // against what is expected.
+ string content;
+ getline(file, content);
+
+ string expected_str(expected);
+ EXPECT_EQ(expected_str, content) << "Data was read from " << name;
+ file.close();
+
+ return (expected_str == content);
+}
+
+// Check if the marker file exists - this is a wrapper for "access(2)" and
+// really tests if the file exists and is accessible
+
+bool
+checkMarkerFileExists(const char* name) {
+ return (access(name, F_OK) == 0);
+}
+
+} // namespace test
+} // namespace dhcp
+} // namespace isc
diff --git a/src/bin/dhcp6/tests/marker_file.h.in b/src/bin/dhcp6/tests/marker_file.h.in
index 11b98ee..45ccbdd 100644
--- a/src/bin/dhcp6/tests/marker_file.h.in
+++ b/src/bin/dhcp6/tests/marker_file.h.in
@@ -17,12 +17,55 @@
/// @file
/// Define a marker file that is used in tests to prove that an "unload"
-/// function has been called.
+/// function has been called
namespace {
-const char* LOAD_MARKER_FILE = "@abs_builddir@/load_marker.txt";
-const char* UNLOAD_MARKER_FILE = "@abs_builddir@/unload_marker.txt";
+const char* const LOAD_MARKER_FILE = "@abs_builddir@/load_marker.txt";
+const char* const UNLOAD_MARKER_FILE = "@abs_builddir@/unload_marker.txt";
}
+namespace isc {
+namespace dhcp {
+namespace test {
+
+/// @brief Check marker file
+///
+/// This function is used in some of the DHCP server tests.
+///
+/// Marker files are used by the load/unload functions in the hooks
+/// libraries in these tests to signal whether they have been loaded or
+/// unloaded. The file (if present) contains a single line holding
+/// a set of characters.
+///
+/// This convenience function checks the file to see if the characters
+/// are those expected.
+///
+/// @param name Name of the marker file.
+/// @param expected Characters expected. If a marker file is present,
+/// it is expected to contain characters. Therefore a value of NULL
+/// is used to signify that the marker file is not expected to be
+/// present.
+///
+/// @return true if all tests pass, false if not (in which case a failure
+/// will have been logged).
+bool
+checkMarkerFile(const char* name, const char* expected);
+
+/// @brief Check marker file exists
+///
+/// This function is used in some of the DHCP server tests.
+///
+/// Checkes that the specified file does NOT exist.
+///
+/// @param name Name of the marker file.
+///
+/// @return true if file exists, false if not.
+bool
+checkMarkerFileExists(const char* name);
+
+} // namespace test
+} // namespace dhcp
+} // namespace isc
+
#endif // MARKER_FILE_H
diff --git a/src/bin/dhcp6/tests/test_libraries.h.in b/src/bin/dhcp6/tests/test_libraries.h.in
index b5e80a0..8b03dc2 100644
--- a/src/bin/dhcp6/tests/test_libraries.h.in
+++ b/src/bin/dhcp6/tests/test_libraries.h.in
@@ -37,13 +37,13 @@ namespace {
// Library with load/unload functions creating marker files to check their
// operation.
-static const char* CALLOUT_LIBRARY_1 = "@abs_builddir@/.libs/libco1"
+const char* const CALLOUT_LIBRARY_1 = "@abs_builddir@/.libs/libco1"
DLL_SUFFIX;
-static const char* CALLOUT_LIBRARY_2 = "@abs_builddir@/.libs/libco2"
+const char* const CALLOUT_LIBRARY_2 = "@abs_builddir@/.libs/libco2"
DLL_SUFFIX;
// Name of a library which is not present.
-static const char* NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere"
+const char* const NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere"
DLL_SUFFIX;
} // anonymous namespace
diff --git a/src/lib/dhcpsrv/dhcp_parsers.cc b/src/lib/dhcpsrv/dhcp_parsers.cc
index f824082..fb1b3a0 100644
--- a/src/lib/dhcpsrv/dhcp_parsers.cc
+++ b/src/lib/dhcpsrv/dhcp_parsers.cc
@@ -274,6 +274,8 @@ HooksLibrariesParser::commit() {
/// Commits the list of libraries to the configuration manager storage if
/// the list of libraries has changed.
if (changed_) {
+ // TODO Delete any stored CalloutHandles before reloading the
+ // libraries
HooksManager::loadLibraries(libraries_);
}
}
diff --git a/src/lib/hooks/hooks_manager.h b/src/lib/hooks/hooks_manager.h
index 3abb68c..53a2525 100644
--- a/src/lib/hooks/hooks_manager.h
+++ b/src/lib/hooks/hooks_manager.h
@@ -86,13 +86,6 @@ public:
/// In the latter case, an error message will have been output.
static void unloadLibraries();
- /// @brief Reload libraries
- ///
- /// Reloads the current libraries. This causes all the libraries' "unload"
- /// functions to run, the libraries to be closed, reopened, and all the
- /// "load" functions run.
- static void reloadLibraries();
-
/// @brief Are callouts present?
///
/// Checks loaded libraries and returns true if at lease one callout
More information about the bind10-changes
mailing list