Commit 721eff49 authored by Stephen Morris's avatar Stephen Morris

[2981] Checkpoint

Now awaiting the merging of #2980 (the hooks code) so that the
configuration validation code can check if the libraries are valid
before accepting the configuration.
parent ed363b36
...@@ -1426,6 +1426,7 @@ AC_OUTPUT([doc/version.ent ...@@ -1426,6 +1426,7 @@ AC_OUTPUT([doc/version.ent
src/bin/d2/tests/test_data_files_config.h src/bin/d2/tests/test_data_files_config.h
src/bin/tests/process_rename_test.py src/bin/tests/process_rename_test.py
src/lib/config/tests/data_def_unittests_config.h src/lib/config/tests/data_def_unittests_config.h
src/lib/dhcpsrv/tests/test_libraries.h
src/lib/python/isc/config/tests/config_test src/lib/python/isc/config/tests/config_test
src/lib/python/isc/cc/tests/cc_test src/lib/python/isc/cc/tests/cc_test
src/lib/python/isc/notify/tests/notify_out_test src/lib/python/isc/notify/tests/notify_out_test
......
...@@ -50,7 +50,9 @@ namespace dhcp { ...@@ -50,7 +50,9 @@ namespace dhcp {
/// this means when there are no packets being activly processed. Rather than /// this means when there are no packets being activly processed. Rather than
/// take a chance that the configuration code will do the unload/load at the /// take a chance that the configuration code will do the unload/load at the
/// right time, the configuration code sets the names of the new libraries in /// right time, the configuration code sets the names of the new libraries in
/// this object and the server decides when to reconfigure the hooks. /// this object and the server decides when to reconfigure the hooks. The
/// presence or absence of the names of the hooks libraries here is an
/// indication of whether the libraries should be reloaded.
/// ///
/// Below is a sketch of configuration inheritance (not implemented yet). /// Below is a sketch of configuration inheritance (not implemented yet).
/// Let's investigate the following configuration: /// Let's investigate the following configuration:
......
...@@ -41,6 +41,7 @@ ParserContext::ParserContext(Option::Universe universe): ...@@ -41,6 +41,7 @@ ParserContext::ParserContext(Option::Universe universe):
string_values_(new StringStorage()), string_values_(new StringStorage()),
options_(new OptionStorage()), options_(new OptionStorage()),
option_defs_(new OptionDefStorage()), option_defs_(new OptionDefStorage()),
hooks_libraries_(new HooksLibrariesStorage()),
universe_(universe) { universe_(universe) {
} }
...@@ -50,6 +51,7 @@ ParserContext::ParserContext(const ParserContext& rhs): ...@@ -50,6 +51,7 @@ ParserContext::ParserContext(const ParserContext& rhs):
string_values_(new StringStorage(*(rhs.string_values_))), string_values_(new StringStorage(*(rhs.string_values_))),
options_(new OptionStorage(*(rhs.options_))), options_(new OptionStorage(*(rhs.options_))),
option_defs_(new OptionDefStorage(*(rhs.option_defs_))), option_defs_(new OptionDefStorage(*(rhs.option_defs_))),
hooks_libraries_(new HooksLibrariesStorage(*(rhs.hooks_libraries_))),
universe_(rhs.universe_) { universe_(rhs.universe_) {
} }
...@@ -65,6 +67,9 @@ ParserContext::operator=(const ParserContext& rhs) { ...@@ -65,6 +67,9 @@ ParserContext::operator=(const ParserContext& rhs) {
options_ = OptionStoragePtr(new OptionStorage(*(rhs.options_))); options_ = OptionStoragePtr(new OptionStorage(*(rhs.options_)));
option_defs_ = option_defs_ =
OptionDefStoragePtr(new OptionDefStorage(*(rhs.option_defs_))); OptionDefStoragePtr(new OptionDefStorage(*(rhs.option_defs_)));
hooks_libraries_ =
HooksLibrariesStoragePtr(new HooksLibrariesStorage(
*(rhs.hooks_libraries_)));
universe_ = rhs.universe_; universe_ = rhs.universe_;
} }
return (*this); return (*this);
...@@ -161,6 +166,63 @@ InterfaceListConfigParser::commit() { ...@@ -161,6 +166,63 @@ InterfaceListConfigParser::commit() {
/// on all interfaces. /// on all interfaces.
} }
// ******************** HooksLibrariesParser *************************
HooksLibrariesParser::HooksLibrariesParser(const std::string& param_name,
ParserContextPtr global_context)
: libraries_(), global_context_(global_context) {
// 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);
}
}
void
HooksLibrariesParser::build(ConstElementPtr value) {
/// Extract the list of libraries.
HooksLibrariesStoragePtr libraries(new HooksLibrariesStorage());
BOOST_FOREACH(ConstElementPtr iface, value->listValue()) {
string libname = iface->str();
boost::erase_all(libname, "\"");
libraries->push_back(libname);
}
/// @todo A two-stage process. The first stage checks if the libraries
/// element has changed. If not, nothing is done - the command
/// "DhcpN reload_hooks" is required to reload the same libraries (this
/// prevents needless reloads when anything in the configuration is
/// changed).
///
/// If the libraries have changed, the next step is to validate each of the
/// libraries. This should be a method on HooksManager which should create
/// a LibraryManager for it and call a new method "validateLibrary()".
/// That method will open a library (verifying that it exists) and check
/// version() (both that it exists and returned the right value). If these
/// checks succeed, it is considered a success. The library is closed when
/// the LibraryManager is deleted.
/// @TODO Validate the library list
/// The library list has changed, so store the new list. (This clears the
/// local pointer libraries as a side-effect, but as that is being
/// destroyed on exit, it is not an issue).
libraries_.swap(libraries);
}
void
HooksLibrariesParser::commit() {
/// Commits the list of libraries to the configuration manager storage.
/// Note that the list stored here could be empty, which will signify
/// no change.
///
/// We use "swap" to reduce overhead - as this parser is being destroyed
/// after the commit, there is no reason to retain a pointer to the hooks
/// library data in it.
global_context_->hooks_libraries_.swap(libraries_);
}
// **************************** OptionDataParser ************************* // **************************** OptionDataParser *************************
OptionDataParser::OptionDataParser(const std::string&, OptionStoragePtr options, OptionDataParser::OptionDataParser(const std::string&, OptionStoragePtr options,
ParserContextPtr global_context) ParserContextPtr global_context)
......
...@@ -33,7 +33,6 @@ namespace dhcp { ...@@ -33,7 +33,6 @@ namespace dhcp {
/// @brief Storage for option definitions. /// @brief Storage for option definitions.
typedef OptionSpaceContainer<OptionDefContainer, typedef OptionSpaceContainer<OptionDefContainer,
OptionDefinitionPtr> OptionDefStorage; OptionDefinitionPtr> OptionDefStorage;
/// @brief Shared pointer to option definitions storage. /// @brief Shared pointer to option definitions storage.
typedef boost::shared_ptr<OptionDefStorage> OptionDefStoragePtr; typedef boost::shared_ptr<OptionDefStorage> OptionDefStoragePtr;
...@@ -44,11 +43,18 @@ typedef OptionSpaceContainer<Subnet::OptionContainer, ...@@ -44,11 +43,18 @@ typedef OptionSpaceContainer<Subnet::OptionContainer,
/// @brief Shared pointer to option storage. /// @brief Shared pointer to option storage.
typedef boost::shared_ptr<OptionStorage> OptionStoragePtr; typedef boost::shared_ptr<OptionStorage> OptionStoragePtr;
/// @brief Storage for hooks libraries
typedef std::vector<std::string> HooksLibrariesStorage;
/// @brief Pointer to storage for hooks libraries
typedef boost::shared_ptr<HooksLibrariesStorage> HooksLibrariesStoragePtr;
/// @brief A template class that stores named elements of a given data type. /// @brief A template class that stores named elements of a given data type.
/// ///
/// This template class is provides data value storage for configuration parameters /// This template class is provides data value storage for configuration
/// of a given data type. The values are stored by parameter name and as instances /// parameters of a given data type. The values are stored by parameter name
/// of type "ValueType". /// and as instances of type "ValueType".
/// ///
/// @param ValueType is the data type of the elements to store. /// @param ValueType is the data type of the elements to store.
template<typename ValueType> template<typename ValueType>
...@@ -151,6 +157,26 @@ public: ...@@ -151,6 +157,26 @@ public:
/// @brief Storage for option definitions. /// @brief Storage for option definitions.
OptionDefStoragePtr option_defs_; OptionDefStoragePtr option_defs_;
/// @brief Hooks libraries pointer.
///
/// The hooks libraries information is a vector of strings, each containing
/// the name of a library. Hooks libraries should only be reloaded if the
/// list of names has changed, so the list of current DHCP parameters
/// (in isc::dhcp::CfgMgr) contains an indication as to whether the list has
/// altered. This indication is implemented by storing a pointer to the
/// list of library names which is cleared when the libraries are loaded.
/// So either the pointer is null (meaning don't reload the libraries and
/// the list of current names can be obtained from the HooksManager) or it
/// is non-null (this is the new list of names, reload the libraries when
/// possible).
///
/// The same applies to the parser context. The pointer is null (which
/// means that the isc::dhcp::HooksLibrariesParser::build method has
/// compared the library list in the configuration against the library
/// list in the HooksManager and has found no change) or it is non-null
/// (in which case this is the list of new library names).
HooksLibrariesStoragePtr hooks_libraries_;
/// @brief The parsing universe of this context. /// @brief The parsing universe of this context.
Option::Universe universe_; Option::Universe universe_;
...@@ -306,6 +332,44 @@ private: ...@@ -306,6 +332,44 @@ private:
std::vector<std::string> interfaces_; std::vector<std::string> interfaces_;
}; };
/// @brief parser for hooks library list
///
/// This parser handles the list of hooks libraries. This is an optional list,
/// which may be empty.
class HooksLibrariesParser : public DhcpConfigParser {
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.
/// @param GERBIL
/// @throw BadValue if supplied parameter name is not "hooks_libraries"
HooksLibrariesParser(const std::string& param_name,
ParserContextPtr global_context);
/// @brief parses parameters value
///
/// Parses configuration entry (list of parameters) and adds each element
/// to the hooks libraries list.
///
/// @param value pointer to the content of parsed values
virtual void build(isc::data::ConstElementPtr value);
/// @brief commits hooks libraries data
virtual void commit();
private:
/// List of hooks libraries. This will be NULL if there is no change to
/// the list.
HooksLibrariesStoragePtr libraries_;
/// Parsing context which contains global values, options and option
/// definitions.
ParserContextPtr global_context_;
};
/// @brief Parser for option data value. /// @brief Parser for option data value.
/// ///
......
// 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 Basic callout library
///
/// This is source of a test library for various test (LibraryManager and
/// HooksManager). The characteristics of the library produced from this
/// file are:
///
/// - Only the "version" framework function is supplied.
///
/// - A context_create callout is supplied.
///
/// - Three "standard" callouts are supplied corresponding to the hooks
/// "lm_one", "lm_two", "lm_three". All do some trivial calculations
/// on the arguments supplied to it and the context variables, returning
/// intermediate results through the "result" argument. The result of
/// executing all four callouts in order is:
///
/// @f[ (10 + data_1) * data_2 - data_3 @f]
///
/// ...where data_1, data_2 and data_3 are the values passed in arguments of
/// the same name to the three callouts (data_1 passed to lm_one, data_2 to
/// lm_two etc.) and the result is returned in the argument "result".
#include <hooks/hooks.h>
#include <fstream>
using namespace isc::hooks;
using namespace std;
extern "C" {
// Callouts. All return their result through the "result" argument.
int
context_create(CalloutHandle& handle) {
handle.setContext("result", static_cast<int>(10));
handle.setArgument("result", static_cast<int>(10));
return (0);
}
// First callout adds the passed "data_1" argument to the initialized context
// value of 10. (Note that the value set by context_create is accessed through
// context and not the argument, so checking that context is correctly passed
// between callouts in the same library.)
int
lm_one(CalloutHandle& handle) {
int data;
handle.getArgument("data_1", data);
int result;
handle.getArgument("result", result);
result += data;
handle.setArgument("result", result);
return (0);
}
// Second callout multiplies the current context value by the "data_2"
// argument.
int
lm_two(CalloutHandle& handle) {
int data;
handle.getArgument("data_2", data);
int result;
handle.getArgument("result", result);
result *= data;
handle.setArgument("result", result);
return (0);
}
// Final callout subtracts the result in "data_3".
int
lm_three(CalloutHandle& handle) {
int data;
handle.getArgument("data_3", data);
int result;
handle.getArgument("result", result);
result -= data;
handle.setArgument("result", result);
return (0);
}
// Framework functions. Only version() is supplied here.
int
version() {
return (BIND10_HOOKS_VERSION);
}
};
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <dhcpsrv/cfgmgr.h> #include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/subnet.h> #include <dhcpsrv/subnet.h>
#include <dhcpsrv/dhcp_parsers.h> #include <dhcpsrv/dhcp_parsers.h>
#include <dhcpsrv/tests/test_libraries.h>
#include <exceptions/exceptions.h> #include <exceptions/exceptions.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
...@@ -332,6 +333,8 @@ public: ...@@ -332,6 +333,8 @@ public:
} else if (config_id.compare("option-def") == 0) { } else if (config_id.compare("option-def") == 0) {
parser = new OptionDefListParser(config_id, parser = new OptionDefListParser(config_id,
parser_context_->option_defs_); parser_context_->option_defs_);
} else if (config_id.compare("hooks_libraries") == 0) {
parser = new HooksLibrariesParser(config_id, parser_context_);
} else { } else {
isc_throw(NotImplemented, isc_throw(NotImplemented,
"Parser error: configuration parameter not supported: " "Parser error: configuration parameter not supported: "
...@@ -349,7 +352,7 @@ public: ...@@ -349,7 +352,7 @@ public:
/// ///
/// @return retuns 0 if the configuration parsed successfully, /// @return retuns 0 if the configuration parsed successfully,
/// non-zero otherwise failure. /// non-zero otherwise failure.
int parseConfiguration (std::string &config) { int parseConfiguration(const std::string& config) {
int rcode_ = 1; int rcode_ = 1;
// Turn config into elements. // Turn config into elements.
// Test json just to make sure its valid. // Test json just to make sure its valid.
...@@ -421,6 +424,14 @@ public: ...@@ -421,6 +424,14 @@ public:
return (option_ptr); return (option_ptr);
} }
/// @brief Returns hooks libraries from the parser context
///
/// Returns the pointer to the vector of strings from the parser context.
HooksLibrariesStoragePtr getHooksLibrariesPtr() {
return (parser_context_->hooks_libraries_);
}
/// @brief Wipes the contents of the context to allowing another parsing /// @brief Wipes the contents of the context to allowing another parsing
/// during a given test if needed. /// during a given test if needed.
void reset_context(){ void reset_context(){
...@@ -517,3 +528,100 @@ TEST_F(ParseConfigTest, basicOptionDataTest) { ...@@ -517,3 +528,100 @@ TEST_F(ParseConfigTest, basicOptionDataTest) {
}; // Anonymous namespace }; // Anonymous namespace
/// @brief Check Basic parsing of hooks libraries
///
/// These tests check basic operation of the HooksLibrariesParser.
TEST_F(ParseConfigTest, emptyHooksLibrariesTest) {
// @todo Initialize global library context to null
// Configuration string. This contains a valid library.
const std::string config =
"{ \"hooks_libraries\": [ "
" ]"
"}";
// Verify that the configuration string parses.
int rcode = parseConfiguration(config);
ASSERT_TRUE(rcode == 0);
// @todo modify after the hooks check has been added. At the moment, the
// string should parse to an empty string.
HooksLibrariesStoragePtr ptr = getHooksLibrariesPtr();
EXPECT_TRUE(ptr);
EXPECT_EQ(0, ptr->size());
}
TEST_F(ParseConfigTest, validHooksLibrariesTest) {
// @todo Initialize global library context to null
// Configuration string. This contains a valid library.
const std::string quote("\"");
const std::string comma(", ");
const std::string config =
std::string("{ ") +
std::string("\"hooks_libraries\": [") +
quote + std::string(BASIC_CALLOUT_LIBRARY) + quote + comma +
quote + std::string(FULL_CALLOUT_LIBRARY) + quote +
std::string("]") +
std::string("}");
// Verify that the configuration string parses.
int rcode = parseConfiguration(config);
ASSERT_TRUE(rcode == 0);
// @todo modify after the hooks check has been added. At the moment, the
// string should parse to an empty string.
HooksLibrariesStoragePtr ptr = getHooksLibrariesPtr();
EXPECT_TRUE(ptr);
// The expected libraries should be the list of libraries specified
// in the given order.
std::vector<std::string> expected;
expected.push_back(BASIC_CALLOUT_LIBRARY);
expected.push_back(FULL_CALLOUT_LIBRARY);
ASSERT_TRUE(ptr);
EXPECT_TRUE(expected == *ptr);
}
// Now parse
TEST_F(ParseConfigTest, invalidHooksLibrariesTest) {
// @todo Initialize global library context to null
// Configuration string. This contains an invalid library which should
// trigger an error in the "build" stage.
const std::string quote("\"");
const std::string comma(", ");
const std::string config =
std::string("{ ") +
std::string("\"hooks_libraries\": [") +
quote + std::string(BASIC_CALLOUT_LIBRARY) + quote + comma +
quote + std::string(NOT_PRESENT_LIBRARY) + quote + comma +
quote + std::string(FULL_CALLOUT_LIBRARY) + quote +
std::string("]") +
std::string("}");
// Verify that the configuration string parses.
int rcode = parseConfiguration(config);
ASSERT_TRUE(rcode == 0);
// @todo modify after the hooks check has been added. At the moment, the
// string should parse to an empty string.
HooksLibrariesStoragePtr ptr = getHooksLibrariesPtr();
EXPECT_TRUE(ptr);
// The expected libraries should be the list of libraries specified
// in the given order.
std::vector<std::string> expected;
expected.push_back(BASIC_CALLOUT_LIBRARY);
expected.push_back(NOT_PRESENT_LIBRARY);
expected.push_back(FULL_CALLOUT_LIBRARY);
ASSERT_TRUE(ptr);
EXPECT_TRUE(expected == *ptr);
}
// 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 Full callout library
///
/// This is source of a test library for various test (LibraryManager and
/// HooksManager). The characteristics of the library produced from this
/// file are:
///
/// The characteristics of this library are:
///
/// - All three framework functions are supplied (version(), load() and
/// unload()), with unload() creating a marker file. The test code checks
/// for the presence of this file, so verifying that unload() has been run.
///
/// - One standard and two non-standard callouts are supplied, with the latter
/// being registered by the load() function.
///
/// All callouts do trivial calculations, the result of all being called in
/// sequence being
///
/// @f[ ((7 * data_1) - data_2) * data_3 @f]
///
/// ...where data_1, data_2 and data_3 are the values passed in arguments of
/// the same name to the three callouts (data_1 passed to lm_one, data_2 to
/// lm_two etc.) and the result is returned in the argument "result".
#include <hooks/hooks.h>
#include <hooks/tests/marker_file.h>
#include <fstream>
using namespace isc::hooks;
extern "C" {
// Callouts
int
context_create(CalloutHandle& handle) {
handle.setContext("result", static_cast<int>(7));
handle.setArgument("result", static_cast<int>(7));
return (0);
}
// First callout adds the passed "data_1" argument to the initialized context
// value of 7. (Note that the value set by context_create is accessed through
// context and not the argument, so checking that context is correctly passed
// between callouts in the same library.)
int
lm_one(CalloutHandle& handle) {
int data;
handle.getArgument("data_1", data);
int result;
handle.getArgument("result", result);
result *= data;
handle.setArgument("result", result);
return (0);
}
// Second callout subtracts the passed value of data_2 from the current
// running total.
static int
lm_nonstandard_two(CalloutHandle& handle) {
int data;
handle.getArgument("data_2", data);
int result;
handle.getArgument("result", result);