Commit aac023f8 authored by Stephen Morris's avatar Stephen Morris
Browse files

[2981] Added libreload functionality to DHCPv6 server

parent c7b293f6
......@@ -137,7 +137,7 @@ TEST_F(CtrlDhcpv4SrvTest, libreload) {
ConstElementPtr result =
ControlledDhcpv4Srv::execDhcpv4ServerCommand("libreload", params);
ConstElementPtr comment = parseAnswer(rcode, result);
EXPECT_EQ(0, rcode); // expect success
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,
......
......@@ -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;
......@@ -142,7 +146,19 @@ ControlledDhcpv6Srv::dhcp6CommandHandler(const string& command, ConstElementPtr
"Shutting down.");
return (answer);
} else if (command == "libreload") {
// TODO - add library reloading
// 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,
......
......@@ -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
......
......@@ -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
......
......@@ -47,6 +47,7 @@ using namespace isc::asiolink;
using namespace isc::config;
using namespace isc::data;
using namespace isc::dhcp;
using namespace isc::dhcp::test;
using namespace isc::hooks;
using namespace std;
......@@ -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)
......
......@@ -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
// 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
......@@ -17,16 +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
......@@ -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
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment