|
@@ -24,22 +24,32 @@
|
|
#include <dhcp6/dhcp6_srv.h>
|
|
#include <dhcp6/dhcp6_srv.h>
|
|
#include <dhcpsrv/cfgmgr.h>
|
|
#include <dhcpsrv/cfgmgr.h>
|
|
#include <dhcpsrv/subnet.h>
|
|
#include <dhcpsrv/subnet.h>
|
|
|
|
+#include <hooks/hooks_manager.h>
|
|
|
|
+
|
|
|
|
+#include "test_libraries.h"
|
|
|
|
+#include "marker_file.h"
|
|
|
|
|
|
#include <boost/foreach.hpp>
|
|
#include <boost/foreach.hpp>
|
|
#include <gtest/gtest.h>
|
|
#include <gtest/gtest.h>
|
|
|
|
|
|
#include <fstream>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <iostream>
|
|
|
|
+#include <fstream>
|
|
#include <sstream>
|
|
#include <sstream>
|
|
|
|
+#include <string>
|
|
|
|
+#include <vector>
|
|
|
|
|
|
#include <arpa/inet.h>
|
|
#include <arpa/inet.h>
|
|
|
|
+#include <unistd.h>
|
|
|
|
|
|
-using namespace std;
|
|
|
|
using namespace isc;
|
|
using namespace isc;
|
|
-using namespace isc::dhcp;
|
|
|
|
using namespace isc::asiolink;
|
|
using namespace isc::asiolink;
|
|
-using namespace isc::data;
|
|
|
|
using namespace isc::config;
|
|
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 {
|
|
namespace {
|
|
|
|
|
|
@@ -71,7 +81,22 @@ public:
|
|
resetConfiguration();
|
|
resetConfiguration();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // Check that no hooks libraries are loaded. This is a pre-condition for
|
|
|
|
+ // a number of tests, so is checked in one place. As this uses an
|
|
|
|
+ // ASSERT call - and it is not clear from the documentation that Gtest
|
|
|
|
+ // predicates can be used in a constructor - the check is placed in SetUp.
|
|
|
|
+ void SetUp() {
|
|
|
|
+ std::vector<std::string> libraries = HooksManager::getLibraryNames();
|
|
|
|
+ ASSERT_TRUE(libraries.empty());
|
|
|
|
+ }
|
|
|
|
+
|
|
~Dhcp6ParserTest() {
|
|
~Dhcp6ParserTest() {
|
|
|
|
+ // Reset configuration database after each test.
|
|
|
|
+ resetConfiguration();
|
|
|
|
+
|
|
|
|
+ // ... and delete the hooks library marker files if present
|
|
|
|
+ unlink(LOAD_MARKER_FILE);
|
|
|
|
+ unlink(UNLOAD_MARKER_FILE);
|
|
};
|
|
};
|
|
|
|
|
|
// Checks if config_result (result of DHCP server configuration) has
|
|
// Checks if config_result (result of DHCP server configuration) has
|
|
@@ -170,51 +195,75 @@ public:
|
|
return (stream.str());
|
|
return (stream.str());
|
|
}
|
|
}
|
|
|
|
|
|
- /// @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 before each test to make sure that
|
|
|
|
- /// contents of the database do not affect result of
|
|
|
|
- /// the test being executed.
|
|
|
|
- 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;
|
|
ConstElementPtr status;
|
|
-
|
|
|
|
- string config = "{ \"interfaces\": [ \"*\" ],"
|
|
|
|
- "\"preferred-lifetime\": 3000,"
|
|
|
|
- "\"rebind-timer\": 2000, "
|
|
|
|
- "\"renew-timer\": 1000, "
|
|
|
|
- "\"valid-lifetime\": 4000, "
|
|
|
|
- "\"subnet6\": [ ], "
|
|
|
|
- "\"option-def\": [ ], "
|
|
|
|
- "\"option-data\": [ ] }";
|
|
|
|
-
|
|
|
|
try {
|
|
try {
|
|
ElementPtr json = Element::fromJSON(config);
|
|
ElementPtr json = Element::fromJSON(config);
|
|
status = configureDhcp6Server(srv_, json);
|
|
status = configureDhcp6Server(srv_, json);
|
|
} catch (const std::exception& ex) {
|
|
} 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
|
|
<< config << std::endl
|
|
<< " and the following error message was returned:"
|
|
<< " and the following error message was returned:"
|
|
<< ex.what() << std::endl;
|
|
<< ex.what() << std::endl;
|
|
|
|
+ return (false);
|
|
}
|
|
}
|
|
|
|
|
|
- // status object must not be NULL
|
|
|
|
|
|
+ // The status object must not be NULL
|
|
if (!status) {
|
|
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);
|
|
comment_ = parseAnswer(rcode_, status);
|
|
- // returned value should be 0 (configuration success)
|
|
|
|
if (rcode_ != 0) {
|
|
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
|
|
|
|
+ /// option-data, and hooks libraries. The reset must be performed after each
|
|
|
|
+ /// test to make sure that contents of the database do not affect the
|
|
|
|
+ /// results of subsequent tests.
|
|
|
|
+ void resetConfiguration() {
|
|
|
|
+ string config = "{ \"interfaces\": [ \"all\" ],"
|
|
|
|
+ "\"hooks-libraries\": [ ],"
|
|
|
|
+ "\"preferred-lifetime\": 3000,"
|
|
|
|
+ "\"rebind-timer\": 2000, "
|
|
|
|
+ "\"renew-timer\": 1000, "
|
|
|
|
+ "\"valid-lifetime\": 4000, "
|
|
|
|
+ "\"subnet6\": [ ], "
|
|
|
|
+ "\"option-def\": [ ], "
|
|
|
|
+ "\"option-data\": [ ] }";
|
|
|
|
+ static_cast<void>(executeConfiguration(config,
|
|
|
|
+ "reset configuration database"));
|
|
// The default setting is to listen on all interfaces. In order to
|
|
// The default setting is to listen on all interfaces. In order to
|
|
// properly test interface configuration we disable listening on
|
|
// properly test interface configuration we disable listening on
|
|
// all interfaces before each test and later check that this setting
|
|
// all interfaces before each test and later check that this setting
|
|
@@ -284,13 +333,11 @@ public:
|
|
expected_data_len));
|
|
expected_data_len));
|
|
}
|
|
}
|
|
|
|
|
|
- int rcode_; ///< return core (see @ref isc::config::parseAnswer)
|
|
|
|
- Dhcpv6Srv srv_; ///< instance of the Dhcp6Srv used during tests
|
|
|
|
-
|
|
|
|
- ConstElementPtr comment_; ///< comment (see @ref isc::config::parseAnswer)
|
|
|
|
-
|
|
|
|
- string valid_iface_; ///< name of a valid network interface (present in system)
|
|
|
|
- string bogus_iface_; ///< name of a invalid network interface (not present in system)
|
|
|
|
|
|
+ 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)
|
|
|
|
+ string valid_iface_; ///< Valid network interface name (present in system)
|
|
|
|
+ string bogus_iface_; ///< invalid network interface name (not in system)
|
|
};
|
|
};
|
|
|
|
|
|
// Goal of this test is a verification if a very simple config update
|
|
// Goal of this test is a verification if a very simple config update
|
|
@@ -1857,6 +1904,154 @@ TEST_F(Dhcp6ParserTest, stdOptionDataEncapsulate) {
|
|
EXPECT_FALSE(desc.option->getOption(112));
|
|
EXPECT_FALSE(desc.option->getOption(112));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+// Tests of the hooks libraries configuration. All tests have the pre-
|
|
|
|
+// condition (checked in the test fixture's SetUp() method) that no hooks
|
|
|
|
+// libraries are loaded at the start of the tests.
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// 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\": [ \"all\" ],"
|
|
|
|
+ "\"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\": \"foo\","
|
|
|
|
+ " \"space\": \"vendor-opts-space\","
|
|
|
|
+ " \"code\": 110,"
|
|
|
|
+ " \"data\": \"1234\","
|
|
|
|
+ " \"csv-format\": True"
|
|
|
|
+ " },"
|
|
|
|
+ " {"
|
|
|
|
+ " \"name\": \"foo2\","
|
|
|
|
+ " \"space\": \"vendor-opts-space\","
|
|
|
|
+ " \"code\": 111,"
|
|
|
|
+ " \"data\": \"192.168.2.1\","
|
|
|
|
+ " \"csv-format\": True"
|
|
|
|
+ " } ],"
|
|
|
|
+ "\"option-def\": [ {"
|
|
|
|
+ " \"name\": \"foo\","
|
|
|
|
+ " \"code\": 110,"
|
|
|
|
+ " \"type\": \"uint32\","
|
|
|
|
+ " \"array\": False,"
|
|
|
|
+ " \"record-types\": \"\","
|
|
|
|
+ " \"space\": \"vendor-opts-space\","
|
|
|
|
+ " \"encapsulate\": \"\""
|
|
|
|
+ " },"
|
|
|
|
+ " {"
|
|
|
|
+ " \"name\": \"foo2\","
|
|
|
|
+ " \"code\": 111,"
|
|
|
|
+ " \"type\": \"ipv4-address\","
|
|
|
|
+ " \"array\": False,"
|
|
|
|
+ " \"record-types\": \"\","
|
|
|
|
+ " \"space\": \"vendor-opts-space\","
|
|
|
|
+ " \"encapsulate\": \"\""
|
|
|
|
+ " } ]"
|
|
|
|
+ "}");
|
|
|
|
+
|
|
|
|
+ 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(Dhcp6ParserTest, NoHooksLibraries) {
|
|
|
|
+ // Parse a configuration containing no names.
|
|
|
|
+ string config = buildHooksLibrariesConfig();
|
|
|
|
+ if (!executeConfiguration(config,
|
|
|
|
+ "set configuration with no hooks libraries")) {
|
|
|
|
+ FAIL() << "Unable to execute configuration";
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+ // No libraries should be loaded at the end of the test.
|
|
|
|
+ std::vector<std::string> libraries = HooksManager::getLibraryNames();
|
|
|
|
+ EXPECT_TRUE(libraries.empty());
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// Verify parsing fails with one library that will fail validation.
|
|
|
|
+TEST_F(Dhcp6ParserTest, InvalidLibrary) {
|
|
|
|
+ // Parse a configuration containing a failing library.
|
|
|
|
+ string config = buildHooksLibrariesConfig(NOT_PRESENT_LIBRARY);
|
|
|
|
+
|
|
|
|
+ ConstElementPtr status;
|
|
|
|
+ ElementPtr json = Element::fromJSON(config);
|
|
|
|
+ ASSERT_NO_THROW(status = configureDhcp6Server(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(Dhcp6ParserTest, LibrariesSpecified) {
|
|
|
|
+ // Marker files should not be present.
|
|
|
|
+ EXPECT_FALSE(checkMarkerFileExists(LOAD_MARKER_FILE));
|
|
|
|
+ EXPECT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
|
|
|
|
+
|
|
|
|
+ // 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).
|
|
|
|
+ std::vector<std::string> libraries = HooksManager::getLibraryNames();
|
|
|
|
+ ASSERT_EQ(2, libraries.size());
|
|
|
|
+ EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "12"));
|
|
|
|
+ EXPECT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
|
|
|
|
+
|
|
|
|
+ // 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
|
|
// This test verifies that it is possible to select subset of interfaces on
|
|
// which server should listen.
|
|
// which server should listen.
|
|
TEST_F(Dhcp6ParserTest, selectedInterfaces) {
|
|
TEST_F(Dhcp6ParserTest, selectedInterfaces) {
|