2
0
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:
Francis Dupont
2021-05-27 12:04:33 +00:00
parent 350aa05eaa
commit 4e412320e1
11 changed files with 273 additions and 6 deletions

View File

@@ -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])

View File

@@ -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 ------------------
//

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View 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);
}
}
}

View File

@@ -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);

View File

@@ -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

View 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

View File

@@ -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": [
{