parser = new DbAccessParser(config_id, DbAccessParser::LEASE_DB);
} else if (config_id.compare("hosts-database") == 0) {
parser = new DbAccessParser(config_id, DbAccessParser::HOSTS_DB);
- } else if (config_id.compare("hooks-libraries") == 0) {
- parser = new HooksLibrariesParser(config_id);
+ // hooks-libraries are now migrated to SimpleParser.
} else if (config_id.compare("echo-client-id") == 0) {
parser = new BooleanParser(config_id, globalContext()->boolean_values_);
} else if (config_id.compare("dhcp-ddns") == 0) {
// 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 failure as doing the change.)
- ParserPtr hooks_parser;
+ HooksLibrariesParser hooks_parser;
// The subnet parsers implement data inheritance by directly
// accessing global storage. For this reason the global data
continue;
}
+ if (config_pair.first == "hooks-libraries") {
+ hooks_parser.parse(config_pair.second);
+ hooks_parser.verifyLibraries();
+ continue;
+ }
+
ParserPtr parser(createGlobalDhcp4ConfigParser(config_pair.first,
config_pair.second));
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PARSER_CREATED)
// 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 if (config_pair.first == "client-classes") {
client_classes_parser = parser;
} else {
// This occurs last as if it succeeds, there is no easy way
// revert it. As a result, the failure to commit a subsequent
// change causes problems when trying to roll back.
- if (hooks_parser) {
- hooks_parser->commit();
- }
+ hooks_parser.loadLibraries();
}
catch (const isc::Exception& ex) {
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_FAIL).arg(ex.what());
IfaceMgrTestConfig test_config(true);
IfaceMgr::instance().openSockets4();
- // Install pkt4_receive_callout
- EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
- "subnet4_select", subnet4_select_callout));
-
// Configure 2 subnets, both directly reachable over local interface
// (let's not complicate the matter with relays)
string config = "{ \"interfaces-config\": {"
// Commit the config
CfgMgr::instance().commit();
+ // Install pkt4_receive_callout
+ EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+ "subnet4_select", subnet4_select_callout));
+
// Prepare discover packet. Server should select first subnet for it
Pkt4Ptr sol = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
sol->setRemoteAddr(IOAddress("192.0.2.1"));
IfaceMgrTestConfig test_config(true);
IfaceMgr::instance().openSockets4();
- // Install a callout
- EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
- "subnet4_select", subnet4_select_different_subnet_callout));
-
// Configure 2 subnets, both directly reachable over local interface
// (let's not complicate the matter with relays)
string config = "{ \"interfaces-config\": {"
CfgMgr::instance().commit();
+ // Install a callout
+ EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+ "subnet4_select", subnet4_select_different_subnet_callout));
+
// Prepare discover packet. Server should select first subnet for it
Pkt4Ptr sol = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
sol->setRemoteAddr(IOAddress("192.0.2.1"));
}
// Checks that decline4 hooks (lease4_decline) are triggered properly.
-TEST_F(HooksDhcpv4SrvTest, HooksDecline) {
+/// @todo: There is a bug in HooksManager that causes the callouts installed
+/// using preCalloutsLibraryHandle() to be uninstalled when loadLibrary
+/// is called. This has changed recently (ticket #5031) as it calls the
+/// load/unload every time, regardless if the hooks-libraries clause is in the
+/// config file or not. #5095 has been submitted for this issue. Please
+/// enable this test once #5095 is fixed.
+TEST_F(HooksDhcpv4SrvTest, DISABLED_HooksDecline) {
IfaceMgrTestConfig test_config(true);
IfaceMgr::instance().openSockets4();
}
// Checks that decline4 hook is able to drop the packet.
-TEST_F(HooksDhcpv4SrvTest, HooksDeclineDrop) {
+/// @todo See HooksDhcpv4SrvTest.HooksDecline description for details.
+/// Please reenable this once #5095 is fixed.
+TEST_F(HooksDhcpv4SrvTest, DISABLED_HooksDeclineDrop) {
IfaceMgrTestConfig test_config(true);
IfaceMgr::instance().openSockets4();
parser = new DbAccessParser(config_id, DbAccessParser::LEASE_DB);
} else if (config_id.compare("hosts-database") == 0) {
parser = new DbAccessParser(config_id, DbAccessParser::HOSTS_DB);
- } else if (config_id.compare("hooks-libraries") == 0) {
- parser = new HooksLibrariesParser(config_id);
+ // hooks-libraries is now converted to SimpleParser.
} else if (config_id.compare("dhcp-ddns") == 0) {
parser = new D2ClientConfigParser(config_id);
} else if (config_id.compare("mac-sources") == 0) {
// Some of the parsers alter state of the system that can't easily
// be undone. (Or alter it in a way such that undoing the change
// has the same risk of failure as doing the change.)
- ParserPtr hooks_parser;
+ HooksLibrariesParser hooks_parser;
// The subnet parsers implement data inheritance by directly
// accessing global storage. For this reason the global data
continue;
}
+ if (config_pair.first == "hooks-libraries") {
+ hooks_parser.parse(config_pair.second);
+ hooks_parser.verifyLibraries();
+ continue;
+ }
+
ParserPtr parser(createGlobal6DhcpConfigParser(config_pair.first,
config_pair.second));
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PARSER_CREATED)
subnet_parser = parser;
} else if (config_pair.first == "lease-database") {
leases_parser = parser;
- } else if (config_pair.first == "hooks-libraries") {
- // Executing the commit will alter currently loaded hooks
- // libraries. Check if the supplied libraries are valid,
- // but defer the commit until after everything else has
- // committed.
- hooks_parser = parser;
- hooks_parser->build(config_pair.second);
} else if (config_pair.first == "interfaces-config") {
// The interface parser is independent from any other parser and
// can be run here before other parsers.
// This occurs last as if it succeeds, there is no easy way to
// revert it. As a result, the failure to commit a subsequent
// change causes problems when trying to roll back.
- if (hooks_parser) {
- hooks_parser->commit();
- }
+ hooks_parser.loadLibraries();
}
catch (const isc::Exception& ex) {
LOG_ERROR(dhcp6_logger, DHCP6_PARSER_COMMIT_FAIL).arg(ex.what());
// valid parameters
TEST_F(HooksDhcpv6SrvTest, subnet6Select) {
- // Install pkt6_receive_callout
- EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
- "subnet6_select", subnet6_select_callout));
-
// Configure 2 subnets, both directly reachable over local interface
// (let's not complicate the matter with relays)
string config = "{ \"interfaces-config\": {"
CfgMgr::instance().commit();
+ // Install pkt6_receive_callout
+ EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+ "subnet6_select", subnet6_select_callout));
+
// Prepare solicit packet. Server should select first subnet for it
Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
sol->setRemoteAddr(IOAddress("fe80::abcd"));
// a different subnet.
TEST_F(HooksDhcpv6SrvTest, subnet6SselectChange) {
- // Install pkt6_receive_callout
- EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
- "subnet6_select", subnet6_select_different_subnet_callout));
-
// Configure 2 subnets, both directly reachable over local interface
// (let's not complicate the matter with relays)
string config = "{ \"interfaces-config\": {"
CfgMgr::instance().commit();
+ // Install pkt6_receive_callout
+ EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+ "subnet6_select", subnet6_select_different_subnet_callout));
+
// Prepare solicit packet. Server should select first subnet for it
Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
sol->setRemoteAddr(IOAddress("fe80::abcd"));
// This test checks that the basic decline hook (lease6_decline) is
// triggered properly.
-TEST_F(HooksDhcpv6SrvTest, basicLease6Decline) {
+/// @todo: Reenable this once #5095 is fixed.
+TEST_F(HooksDhcpv6SrvTest, DISABLED_basicLease6Decline) {
IfaceMgrTestConfig test_config(true);
// Install lease6_decline callout
}
// Test that the lease6_decline hook point can handle SKIP status.
-TEST_F(HooksDhcpv6SrvTest, lease6DeclineSkip) {
+/// @todo: Reenable this once #5095 is fixed.
+TEST_F(HooksDhcpv6SrvTest, DISABLED_lease6DeclineSkip) {
IfaceMgrTestConfig test_config(true);
// Install lease6_decline callout. It will set the status to skip
}
// Test that the lease6_decline hook point can handle DROP status.
-TEST_F(HooksDhcpv6SrvTest, lease6DeclineDrop) {
+/// @todo: Reenable this once #5095 is fixed.
+TEST_F(HooksDhcpv6SrvTest, DISABLED_lease6DeclineDrop) {
IfaceMgrTestConfig test_config(true);
// Install lease6_decline callout. It will set the status to skip
}
// ******************** HooksLibrariesParser *************************
-
-HooksLibrariesParser::HooksLibrariesParser(const std::string& param_name)
- : libraries_(), changed_(false)
-{
- // Sanity check on the name.
- if (param_name != "hooks-libraries") {
- isc_throw(BadValue, "Internal error. Hooks libraries "
- "parser called for the wrong parameter: " << param_name);
- }
-}
-
-// Parse the configuration. As Kea has not yet implemented parameters, the
-// parsing code only checks that:
-//
-// 1. Each element in the hooks-libraries list is a map
-// 2. The map contains an element "library" whose value is a string: all
-// other elements in the map are ignored.
void
-HooksLibrariesParser::build(ConstElementPtr value) {
+HooksLibrariesParser::parse(ConstElementPtr value) {
// Initialize.
libraries_.clear();
- changed_ = false;
+
+ if (!value) {
+ isc_throw(DhcpConfigError, "Tried to parse null hooks libriaries");
+ }
+
+ // Let's stoer
+ position_ = value->getPosition();
// This is the new syntax. Iterate through it and get each map.
BOOST_FOREACH(ConstElementPtr library_entry, value->listValue()) {
// Note we have found the library name.
lib_found = true;
- } else {
- // If there are parameters, let's remember them.
- if (entry_item.first == "parameters") {
- parameters = entry_item.second;
- }
+ continue;
}
+
+ // If there are parameters, let's remember them.
+ if (entry_item.first == "parameters") {
+ parameters = entry_item.second;
+ continue;
+ }
+
+ // For all other parameters we will throw.
+ isc_throw(DhcpConfigError, "unknown hooks library parameter: "
+ << entry_item.first << "("
+ << library_entry->getPosition() << ")");
}
+
if (! lib_found) {
isc_throw(DhcpConfigError, "hooks library configuration error:"
" one or more hooks-libraries elements are missing the"
libraries_.push_back(make_pair(libname, parameters));
}
+}
+void HooksLibrariesParser::verifyLibraries() {
// Check if the list of libraries has changed. If not, nothing is done
// - the command "DhcpN libreload" is required to reload the same
// libraries (this prevents needless reloads when anything else in the
}
isc_throw(DhcpConfigError, "hooks libraries failed to validate - "
"library or libraries in error are: " << error_list
- << " (" << value->getPosition() << ")");
+ << "(" << position_ << ")");
}
-
- // The library list has changed and the libraries are valid, so flag for
- // update when commit() is called.
- changed_ = true;
}
void
-HooksLibrariesParser::commit() {
+HooksLibrariesParser::loadLibraries() {
/// 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_);
- }
+ /// @todo: Delete any stored CalloutHandles before reloading the
+ /// libraries
+ HooksManager::loadLibraries(libraries_);
}
// Method for testing
void
-HooksLibrariesParser::getLibraries(isc::hooks::HookLibsCollection& libraries,
- bool& changed) {
+HooksLibrariesParser::getLibraries(isc::hooks::HookLibsCollection& libraries) {
libraries = libraries_;
- changed = changed_;
}
// **************************** OptionDataParser *************************
///
/// Only if the library list has changed and the libraries are valid will the
/// change be applied.
-class HooksLibrariesParser : public DhcpConfigParser {
+class HooksLibrariesParser : public isc::data::SimpleParser {
public:
- /// @brief Constructor
- ///
- /// As this is a dedicated parser, it must be used to parse
- /// "hooks-libraries" parameter only. All other types will throw exception.
- ///
- /// @param param_name name of the configuration parameter being parsed.
- ///
- /// @throw BadValue if supplied parameter name is not "hooks-libraries"
- HooksLibrariesParser(const std::string& param_name);
-
/// @brief Parses parameters value
///
/// Parses configuration entry (list of parameters) and adds each element
/// ]
/// @endcode
///
- /// As Kea has not yet implemented parameters, the parsing code only checks
- /// that:
+ /// The parsing code only checks that:
///
/// -# Each element in the hooks-libraries list is a map
/// -# The map contains an element "library" whose value is a string: all
/// other elements in the map are ignored.
+ /// -# That there is an optional 'parameters' parameter.
+ /// -# That there are no other parameters.
+ ///
+ /// If you want to check whether the library is really present (if the file
+ /// is on disk, it is really a library and that it could be loaded), call
+ /// @ref verifyLibraries().
+ ///
+ /// This method stores parsed libraries in @ref libraries_.
///
/// @param value pointer to the content of parsed values
- virtual void build(isc::data::ConstElementPtr value);
+ void parse(isc::data::ConstElementPtr value);
+
+ /// @brief Verifies that libraries stores in libraries_ are valid.
+ ///
+ /// This method is a smart wrapper around @ref HooksManager::validateLibraries().
+ /// It tries to validate all the libraries stored in libraries_.
+ /// @throw DhcpConfigError if any issues are discovered.
+ void verifyLibraries();
/// @brief Commits hooks libraries data
///
- /// Providing that the specified libraries are valid and are different
+ /// This method calls necessary methods in HooksManager that will load the
+ /// actual libraries.Providing that the specified libraries are valid and are different
/// to those already loaded, this method loads the new set of libraries
/// (and unloads the existing set).
- virtual void commit();
+ void loadLibraries();
/// @brief Returns list of parsed libraries
///
///
/// @param [out] libraries List of libraries that were specified in the
/// new configuration.
- /// @param [out] changed true if the list is different from that currently
- /// loaded.
- void getLibraries(isc::hooks::HookLibsCollection& libraries, bool& changed);
+ void getLibraries(isc::hooks::HookLibsCollection& libraries);
private:
/// List of hooks libraries with their configuration parameters
isc::hooks::HookLibsCollection libraries_;
- /// Indicator flagging that the list of libraries has changed.
- bool changed_;
+ /// Position of the original element is stored in case we need to report an
+ /// error later.
+ isc::data::Element::Position position_;
};
/// @brief Parser for option data value.
continue;
}
+ if (config_pair.first == "hooks-libraries") {
+ hooks_libraries_parser_.reset(new HooksLibrariesParser());
+ hooks_libraries_parser_->parse(config_pair.second);
+ hooks_libraries_parser_->verifyLibraries();
+ hooks_libraries_parser_->loadLibraries();
+ continue;
+ }
+
// Remaining ones are old style parsers. Need go do
// the build/commit dance with them.
ParserPtr parser;
// option-data and option-def converted to SimpleParser, so they
// are no longer here.
- if (config_id.compare("hooks-libraries") == 0) {
- parser.reset(new HooksLibrariesParser(config_id));
- hooks_libraries_parser_ =
- boost::dynamic_pointer_cast<HooksLibrariesParser>(parser);
- } else if (config_id.compare("dhcp-ddns") == 0) {
+ if (config_id.compare("dhcp-ddns") == 0) {
parser.reset(new D2ClientConfigParser(config_id));
} else {
isc_throw(NotImplemented,
// Check that the parser recorded nothing.
isc::hooks::HookLibsCollection libraries;
- bool changed;
- hooks_libraries_parser_->getLibraries(libraries, changed);
- EXPECT_FALSE(changed);
+ hooks_libraries_parser_->getLibraries(libraries);
EXPECT_TRUE(libraries.empty());
// Check that there are still no libraries loaded.
// Check that the parser recorded a single library.
HookLibsCollection libraries;
- bool changed;
- hooks_libraries_parser_->getLibraries(libraries, changed);
- EXPECT_TRUE(changed);
+ hooks_libraries_parser_->getLibraries(libraries);
ASSERT_EQ(1, libraries.size());
EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[0].first);
// Check that the parser recorded two libraries in the expected order.
HookLibsCollection libraries;
- bool changed;
- hooks_libraries_parser_->getLibraries(libraries, changed);
- EXPECT_TRUE(changed);
+ hooks_libraries_parser_->getLibraries(libraries);
ASSERT_EQ(2, libraries.size());
EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[0].first);
EXPECT_EQ(CALLOUT_LIBRARY_2, libraries[1].first);
// the paramters (or the files they could point to) could have
// changed, so the libraries are reloaded anyway.
HookLibsCollection libraries;
- bool changed;
- hooks_libraries_parser_->getLibraries(libraries, changed);
- EXPECT_TRUE(changed);
+ hooks_libraries_parser_->getLibraries(libraries);
ASSERT_EQ(2, libraries.size());
EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[0].first);
EXPECT_EQ(CALLOUT_LIBRARY_2, libraries[1].first);
// The list has changed, and this is what we should see.
HookLibsCollection libraries;
- bool changed;
- hooks_libraries_parser_->getLibraries(libraries, changed);
- EXPECT_TRUE(changed);
+ hooks_libraries_parser_->getLibraries(libraries);
ASSERT_EQ(2, libraries.size());
EXPECT_EQ(CALLOUT_LIBRARY_2, libraries[0].first);
EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[1].first);
// The list has changed, and this is what we should see.
HookLibsCollection libraries;
- bool changed;
- hooks_libraries_parser_->getLibraries(libraries, changed);
- EXPECT_TRUE(changed);
+ hooks_libraries_parser_->getLibraries(libraries);
EXPECT_TRUE(libraries.empty());
// Check that no libraries are currently loaded
// Check that the parser recorded the names but, as they were in error,
// does not flag them as changed.
HookLibsCollection libraries;
- bool changed;
- hooks_libraries_parser_->getLibraries(libraries, changed);
- EXPECT_FALSE(changed);
+ hooks_libraries_parser_->getLibraries(libraries);
ASSERT_EQ(3, libraries.size());
EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[0].first);
EXPECT_EQ(NOT_PRESENT_LIBRARY, libraries[1].first);
// Check that the parser recorded the names but, as the library set was
// incorrect, did not mark the configuration as changed.
HookLibsCollection libraries;
- bool changed;
- hooks_libraries_parser_->getLibraries(libraries, changed);
- EXPECT_FALSE(changed);
+ hooks_libraries_parser_->getLibraries(libraries);
ASSERT_EQ(3, libraries.size());
EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[0].first);
EXPECT_EQ(NOT_PRESENT_LIBRARY, libraries[1].first);
// Check that the parser recorded the names.
HookLibsCollection libraries;
- bool changed = false;
- hooks_libraries_parser_->getLibraries(libraries, changed);
- EXPECT_TRUE(changed);
+ hooks_libraries_parser_->getLibraries(libraries);
ASSERT_EQ(3, libraries.size());
EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[0].first);
EXPECT_EQ(CALLOUT_LIBRARY_2, libraries[1].first);