mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-31 05:55:28 +00:00
[(no branch, rebasing 1897-add-d2-server-hook-syntax)] [(no branch, rebasing 1897-add-d2-server-hook-syntax)] [#1897] Stopping arbritary at this point
This commit is contained in:
@@ -1671,6 +1671,7 @@ AC_CONFIG_FILES([src/bin/d2/Makefile])
|
||||
AC_CONFIG_FILES([src/bin/d2/tests/Makefile])
|
||||
AC_CONFIG_FILES([src/bin/d2/tests/d2_process_tests.sh],
|
||||
[chmod +x src/bin/d2/tests/d2_process_tests.sh])
|
||||
AC_CONFIG_FILES([src/bin/d2/tests/test_libraries.h])
|
||||
AC_CONFIG_FILES([src/bin/d2/tests/test_data_files_config.h])
|
||||
AC_CONFIG_FILES([src/bin/dhcp4/Makefile])
|
||||
AC_CONFIG_FILES([src/bin/dhcp4/tests/Makefile])
|
||||
|
@@ -39,6 +39,27 @@
|
||||
"socket-name": "/tmp/kea-ddns-ctrl-socket"
|
||||
},
|
||||
|
||||
//
|
||||
// ----------------- Hooks Libraries -----------------
|
||||
//
|
||||
|
||||
"hooks-libraries":
|
||||
[
|
||||
// Hook libraries list may contain more than one library.
|
||||
{
|
||||
// The only necessary parameter is the library filename.
|
||||
"library": "/opt/local/control-agent-commands.so",
|
||||
|
||||
// Some libraries may support parameters. Make sure you
|
||||
// type this section carefully, as the CA does not validate
|
||||
// it (because the format is library specific).
|
||||
"parameters":
|
||||
{
|
||||
"param1": "foo"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
//
|
||||
// ----------------- Forward DDNS ------------------
|
||||
//
|
||||
|
@@ -12,9 +12,33 @@
|
||||
#include <d2/d2_cfg_mgr.h>
|
||||
#include <d2/d2_controller.h>
|
||||
#include <d2/d2_process.h>
|
||||
#include <hooks/hooks.h>
|
||||
#include <hooks/hooks_manager.h>
|
||||
|
||||
using namespace isc::hooks;
|
||||
using namespace isc::process;
|
||||
|
||||
namespace {
|
||||
|
||||
/// Structure that holds registered hook indexes.
|
||||
struct D2ProcessHooks {
|
||||
int hooks_index_d2_srv_configured_;
|
||||
|
||||
/// Constructor that registers hook points for the DHCPv4 server.
|
||||
D2ProcessHooks() {
|
||||
hooks_index_d2_srv_configured_ = HooksManager::registerHook("d2_srv_configured");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Declare a Hooks object. As this is outside any function or method, it
|
||||
// will be instantiated (and the constructor run) when the module is loaded.
|
||||
// As a result, the hook indexes will be defined before any method in this
|
||||
// module is called.
|
||||
D2ProcessHooks Hooks;
|
||||
|
||||
}
|
||||
|
||||
namespace isc {
|
||||
namespace d2 {
|
||||
|
||||
@@ -242,6 +266,26 @@ D2Process::configure(isc::data::ConstElementPtr config_set, bool check_only) {
|
||||
// did some analysis to decide what if anything we need to do.)
|
||||
reconf_queue_flag_ = true;
|
||||
|
||||
// This hook point notifies hooks libraries that the configuration of the
|
||||
// D2 server has completed. It provides the hook library with the pointer
|
||||
// to the common IO service object, new server configuration in the JSON
|
||||
// format and with the pointer to the configuration storage where the
|
||||
// parsed configuration is stored.
|
||||
if (HooksManager::calloutsPresent(Hooks.hooks_index_d2_srv_configured_)) {
|
||||
CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
|
||||
|
||||
callout_handle->setArgument("io_context", getIoService());
|
||||
callout_handle->setArgument("json_config", config_set);
|
||||
callout_handle->setArgument("server_config",
|
||||
getD2CfgMgr()->getD2CfgContext());
|
||||
|
||||
HooksManager::callCallouts(Hooks.hooks_index_d2_srv_configured_,
|
||||
*callout_handle);
|
||||
|
||||
// Ignore status code as none of them would have an effect on further
|
||||
// operation.
|
||||
}
|
||||
|
||||
// If we are here, configuration was valid, at least it parsed correctly
|
||||
// and therefore contained no invalid values.
|
||||
// Return the success answer from above.
|
||||
@@ -439,5 +483,5 @@ D2Process::reconfigureCommandChannel() {
|
||||
current_control_socket_ = sock_cfg;
|
||||
}
|
||||
|
||||
}; // namespace isc::d2
|
||||
}; // namespace isc
|
||||
} // namespace isc::d2
|
||||
} // namespace isc
|
||||
|
@@ -22,6 +22,10 @@ D2ParserContext::D2ParserContext()
|
||||
{
|
||||
}
|
||||
|
||||
D2ParserContext::~D2ParserContext()
|
||||
{
|
||||
}
|
||||
|
||||
isc::data::ElementPtr
|
||||
D2ParserContext::parseString(const std::string& str, ParserType parser_type)
|
||||
{
|
||||
@@ -97,6 +101,21 @@ D2ParserContext::loc2pos(isc::d2::location& loc)
|
||||
return (isc::data::Element::Position(file, line, pos));
|
||||
}
|
||||
|
||||
void
|
||||
D2ParserContext::require(const std::string& name,
|
||||
isc::data::Element::Position open_loc,
|
||||
isc::data::Element::Position close_loc)
|
||||
{
|
||||
ConstElementPtr value = stack_.back()->get(name);
|
||||
if (!value) {
|
||||
isc_throw(D2ParseError,
|
||||
"missing parameter '" << name << "' ("
|
||||
<< stack_.back()->getPosition() << ") ["
|
||||
<< contextName() << " map between "
|
||||
<< open_loc << " and " << close_loc << "]");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
D2ParserContext::unique(const std::string& name,
|
||||
isc::data::Element::Position loc)
|
||||
|
@@ -78,6 +78,9 @@ public:
|
||||
/// @brief Default constructor.
|
||||
D2ParserContext();
|
||||
|
||||
/// @brief destructor.
|
||||
virtual ~D2ParserContext();
|
||||
|
||||
/// @brief JSON elements being parsed.
|
||||
std::vector<isc::data::ElementPtr> stack_;
|
||||
|
||||
@@ -168,6 +171,19 @@ public:
|
||||
/// @return Position in format accepted by Element
|
||||
isc::data::Element::Position loc2pos(isc::d2::location& loc);
|
||||
|
||||
/// @brief Check if a required parameter is present
|
||||
///
|
||||
/// Check if a required parameter is present in the map at the top
|
||||
/// of the stack and raise an error when it is not.
|
||||
///
|
||||
/// @param name name of the parameter to check
|
||||
/// @param open_loc location of the opening curly bracket
|
||||
/// @param close_loc location of the closing curly bracket
|
||||
/// @throw D2ParseError
|
||||
void require(const std::string& name,
|
||||
isc::data::Element::Position open_loc,
|
||||
isc::data::Element::Position close_loc);
|
||||
|
||||
/// @brief Check if a parameter is already present
|
||||
///
|
||||
/// Check if a parameter is already present in the map at the top
|
||||
|
@@ -28,6 +28,7 @@ AM_CPPFLAGS += -DSYNTAX_FILE=\"$(abs_srcdir)/../d2_parser.yy\"
|
||||
CLEANFILES = $(builddir)/interfaces.txt $(builddir)/logger_lockfile
|
||||
|
||||
DISTCLEANFILES = d2_process_tests.sh test_data_files_config.h
|
||||
DISTCLEANFILES += test_data_files_config.h test_libraries.h
|
||||
|
||||
AM_CXXFLAGS = $(KEA_CXXFLAGS)
|
||||
|
||||
@@ -41,6 +42,8 @@ TESTS_ENVIRONMENT = \
|
||||
TESTS =
|
||||
if HAVE_GTEST
|
||||
|
||||
noinst_LTLIBRARIES = libcallout.la
|
||||
|
||||
TESTS += d2_unittests
|
||||
|
||||
d2_unittests_SOURCES = d2_unittests.cc
|
||||
@@ -100,6 +103,17 @@ d2_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
|
||||
d2_unittests_LDADD += $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS)
|
||||
d2_unittests_LDADD += $(BOOST_LIBS) $(GTEST_LDADD)
|
||||
|
||||
# The basic callout library - contains standard callouts
|
||||
libcallout_la_SOURCES = callout_library.cc
|
||||
libcallout_la_CXXFLAGS = $(AM_CXXFLAGS)
|
||||
libcallout_la_CPPFLAGS = $(AM_CPPFLAGS)
|
||||
libcallout_la_LIBADD = $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
|
||||
libcallout_la_LIBADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
|
||||
libcallout_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la
|
||||
libcallout_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
|
||||
|
||||
nodist_d2_unittests_SOURCES = test_data_files_config.h test_libraries.h
|
||||
|
||||
endif
|
||||
|
||||
noinst_PROGRAMS = $(TESTS)
|
||||
|
72
src/bin/d2/tests/callout_library.cc
Normal file
72
src/bin/d2/tests/callout_library.cc
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
/// @file
|
||||
/// @brief Basic callout library
|
||||
///
|
||||
/// This is source of a test library for Control Agent.
|
||||
///
|
||||
/// - Only the "version" framework function is supplied.
|
||||
///
|
||||
/// - hookpt_one callout is supplied.
|
||||
|
||||
#include <config.h>
|
||||
#include <hooks/hooks.h>
|
||||
|
||||
using namespace isc::hooks;
|
||||
using namespace std;
|
||||
|
||||
namespace {
|
||||
|
||||
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 "integer" 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
|
||||
hookpt_one(CalloutHandle& handle) {
|
||||
int data;
|
||||
handle.getArgument("integer", data);
|
||||
|
||||
int result;
|
||||
handle.getArgument("result", result);
|
||||
|
||||
result += data;
|
||||
handle.setArgument("result", result);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
// Framework functions.
|
||||
|
||||
int
|
||||
version() {
|
||||
return (KEA_HOOKS_VERSION);
|
||||
}
|
||||
|
||||
// load() initializes the user library if the main image was statically linked.
|
||||
int
|
||||
load(isc::hooks::LibraryHandle&) {
|
||||
#ifdef USE_STATIC_LINK
|
||||
hooksStaticLinkInit();
|
||||
#endif
|
||||
return (0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <d2/d2_simple_parser.h>
|
||||
#include <d2/parser_context.h>
|
||||
#include <d2/tests/parser_unittest.h>
|
||||
#include <d2/tests/test_libraries.h>
|
||||
#include <dhcpsrv/testutils/config_result_check.h>
|
||||
#include <process/testutils/d_test_stubs.h>
|
||||
#include <test_data_files_config.h>
|
||||
@@ -23,6 +24,7 @@
|
||||
using namespace std;
|
||||
using namespace isc;
|
||||
using namespace isc::d2;
|
||||
using namespace isc::hooks;
|
||||
using namespace isc::process;
|
||||
|
||||
namespace {
|
||||
@@ -208,6 +210,16 @@ public:
|
||||
return ::testing::AssertionSuccess();
|
||||
}
|
||||
|
||||
/// @brief Replaces %LIBRARY% with specified library name
|
||||
///
|
||||
/// @param config input config text (should contain "%LIBRARY%" string)
|
||||
/// @param lib_name %LIBRARY% will be replaced with that name
|
||||
/// @return configuration text with library name replaced
|
||||
std::string pathReplacer(const char* config, const char* lib_name) {
|
||||
string txt(config);
|
||||
txt.replace(txt.find("%LIBRARY%"), strlen("%LIBRARY%"), string(lib_name));
|
||||
return (txt);
|
||||
}
|
||||
|
||||
/// @brief Pointer the D2Params most recently parsed.
|
||||
D2ParamsPtr d2_params_;
|
||||
@@ -471,6 +483,13 @@ TEST_F(D2CfgMgrTest, fullConfig) {
|
||||
" \"socket-type\" : \"unix\" ,"
|
||||
" \"socket-name\" : \"/tmp/d2-ctrl-channel\" "
|
||||
"},"
|
||||
"\"hooks-libraries\": ["
|
||||
"{"
|
||||
" \"library\": \"%LIBRARY%\" , "
|
||||
" \"parameters\": "
|
||||
" { \"param1\": \"foo\" } "
|
||||
"}"
|
||||
"],"
|
||||
"\"tsig-keys\": ["
|
||||
"{"
|
||||
" \"name\": \"d2_key.example.com\" , "
|
||||
@@ -521,8 +540,10 @@ TEST_F(D2CfgMgrTest, fullConfig) {
|
||||
" ] } "
|
||||
"] } }";
|
||||
|
||||
// Replace the library path.
|
||||
std::string pr_config = pathReplacer(config.c_str(), CALLOUT_LIBRARY);
|
||||
// Should parse without error.
|
||||
RUN_CONFIG_OK(config);
|
||||
RUN_CONFIG_OK(pr_config);
|
||||
|
||||
// Verify that the D2 context can be retrieved and is not null.
|
||||
D2CfgContextPtr context;
|
||||
@@ -549,6 +570,13 @@ TEST_F(D2CfgMgrTest, fullConfig) {
|
||||
ASSERT_TRUE(ctrl_sock->get("socket-name"));
|
||||
EXPECT_EQ("\"/tmp/d2-ctrl-channel\"", ctrl_sock->get("socket-name")->str());
|
||||
|
||||
// Verify that the hooks libraries can be retrieved.
|
||||
const HookLibsCollection libs = context->getHooksConfig().get();
|
||||
ASSERT_EQ(1, libs.size());
|
||||
EXPECT_EQ(string(CALLOUT_LIBRARY), libs[0].first);
|
||||
ASSERT_TRUE(libs[0].second);
|
||||
EXPECT_EQ("{ \"param1\": \"foo\" }", libs[0].second->str());
|
||||
|
||||
// Verify that the forward manager can be retrieved.
|
||||
DdnsDomainListMgrPtr mgr = context->getForwardMgr();
|
||||
ASSERT_TRUE(mgr);
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2017-2019 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2017-2021 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <sstream>
|
||||
|
||||
#include "test_data_files_config.h"
|
||||
#include "test_libraries.h"
|
||||
|
||||
using namespace isc::config;
|
||||
using namespace isc::d2;
|
||||
@@ -95,6 +96,17 @@ parseDHCPDDNS(const std::string& in, bool verbose = false) {
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Replace the library path
|
||||
void pathReplacer(ConstElementPtr d2_cfg) {
|
||||
ConstElementPtr hooks_libs = d2_cfg->get("hooks-libraries");
|
||||
if (!hooks_libs || hooks_libs->empty()) {
|
||||
return;
|
||||
}
|
||||
ElementPtr first_lib = hooks_libs->getNonConst(0);
|
||||
std::string lib_path(CALLOUT_LIBRARY);
|
||||
first_lib->set("library", Element::create(lib_path));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Test fixture class
|
||||
@@ -153,6 +165,9 @@ public:
|
||||
return (false);
|
||||
}
|
||||
|
||||
// update hooks-libraries
|
||||
pathReplacer(d2);
|
||||
|
||||
// try DHCPDDNS configure
|
||||
ConstElementPtr status;
|
||||
try {
|
||||
@@ -191,7 +206,7 @@ public:
|
||||
/// @brief Reset configuration database.
|
||||
///
|
||||
/// This function resets configuration data base by
|
||||
/// removing control sockets and domain lists. Reset must
|
||||
/// removing control sockets, hooks, etc. Reset must
|
||||
/// be performed after each test to make sure that
|
||||
/// contents of the database do not affect result of
|
||||
/// subsequent tests.
|
||||
@@ -250,6 +265,11 @@ TEST_F(D2GetConfigTest, sample1) {
|
||||
ASSERT_NO_THROW(jsonj = parseJSON(expected));
|
||||
// the generic JSON parser does not handle comments
|
||||
EXPECT_TRUE(isEquivalent(jsond, moveComments(jsonj)));
|
||||
// replace the path by its actual value
|
||||
ConstElementPtr d2;
|
||||
ASSERT_NO_THROW(d2 = jsonj->get("DhcpDdns"));
|
||||
ASSERT_TRUE(d2);
|
||||
pathReplacer(d2);
|
||||
// check that unparsed and expected values match
|
||||
EXPECT_TRUE(isEquivalent(unparsed, jsonj));
|
||||
// check on pretty prints too
|
||||
|
24
src/bin/d2/tests/test_libraries.h.in
Normal file
24
src/bin/d2/tests/test_libraries.h.in
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef D2_TEST_LIBRARIES_H
|
||||
#define D2_TEST_LIBRARIES_H
|
||||
|
||||
#include <config.h>
|
||||
|
||||
namespace {
|
||||
|
||||
// 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
|
||||
// .so file. Note that we access the .so file - libtool creates this as a
|
||||
// like to the real shared library.
|
||||
|
||||
// Basic callout library with context_create and three "standard" callouts.
|
||||
static const char* CALLOUT_LIBRARY = "@abs_builddir@/.libs/libcallout.so";
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
#endif // D2_TEST_LIBRARIES_H
|
8
src/bin/d2/tests/testdata/get_config.json
vendored
8
src/bin/d2/tests/testdata/get_config.json
vendored
@@ -33,6 +33,14 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"hooks-libraries": [
|
||||
{
|
||||
"library": "/tmp/k1897/src/bin/d2/tests/.libs/libcallout.so",
|
||||
"parameters": {
|
||||
"param1": "foo"
|
||||
}
|
||||
}
|
||||
],
|
||||
"ip-address": "127.0.0.1",
|
||||
"loggers": [
|
||||
{
|
||||
|
Reference in New Issue
Block a user