2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-30 05:27:55 +00:00

[219-allow-an-option-value-to-be-set-from-an-expression] Checkpoint: wrote flex option code, to do tests, to finish doc

This commit is contained in:
Francis Dupont 2019-10-01 17:37:21 +02:00
parent 6775f8a466
commit 248b058afc
19 changed files with 971 additions and 1 deletions

View File

@ -1694,6 +1694,8 @@ AC_CONFIG_FILES([Makefile
src/bin/shell/tests/shell_unittest.py
src/hooks/Makefile
src/hooks/dhcp/Makefile
src/hooks/dhcp/flex_option/Makefile
src/hooks/dhcp/flex_option/tests/Makefile
src/hooks/dhcp/high_availability/Makefile
src/hooks/dhcp/high_availability/tests/Makefile
src/hooks/dhcp/lease_cmds/Makefile

View File

@ -4,4 +4,4 @@ if HAVE_MYSQL
SUBDIRS += mysql_cb
endif
SUBDIRS += stat_cmds user_chk
SUBDIRS += stat_cmds flex_option user_chk

1
src/hooks/dhcp/flex_option/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/html

View File

@ -0,0 +1,82 @@
SUBDIRS = . tests
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CXXFLAGS = $(KEA_CXXFLAGS)
# Ensure that the message file and doxygen file is included in the distribution
EXTRA_DIST = flex_option_messages.mes
EXTRA_DIST += flex_option.dox
CLEANFILES = *.gcno *.gcda
# convenience archive
noinst_LTLIBRARIES = libflex_option.la
libflex_option_la_SOURCES = flex_option.cc flex_option.h
libflex_option_la_SOURCES += flex_option_callouts.cc
libflex_option_la_SOURCES += flex_option_log.cc flex_option_log.h
libflex_option_la_SOURCES += flex_option_messages.cc flex_option_messages.h
libflex_option_la_SOURCES += version.cc
libflex_option_la_CXXFLAGS = $(AM_CXXFLAGS)
libflex_option_la_CPPFLAGS = $(AM_CPPFLAGS)
# install the shared object into $(libdir)/kea/hooks
lib_hooksdir = $(libdir)/kea/hooks
lib_hooks_LTLIBRARIES = libdhcp_flex_option.la
libdhcp_flex_option_la_SOURCES =
libdhcp_flex_option_la_LDFLAGS = $(AM_LDFLAGS)
libdhcp_flex_option_la_LDFLAGS += -avoid-version -export-dynamic -module
libdhcp_flex_option_la_LIBADD = libflex_option.la
libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la
libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/eval/libkea-eval.la
libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/database/libkea-database.la
libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la
libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/dns/libkea-dns++.la
libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la
libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la
libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la
libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
libdhcp_flex_option_la_LIBADD += $(LOG4CPLUS_LIBS)
libdhcp_flex_option_la_LIBADD += $(CRYPTO_LIBS)
libdhcp_flex_option_la_LIBADD += $(BOOST_LIBS)
# If we want to get rid of all generated messages files, we need to use
# make maintainer-clean. The proper way to introduce custom commands for
# that operation is to define maintainer-clean-local target. However,
# make maintainer-clean also removes Makefile, so running configure script
# is required. To make it easy to rebuild messages without going through
# reconfigure, a new target messages-clean has been added.
maintainer-clean-local:
rm -f flex_option_messages.h flex_option_messages.cc
# To regenerate messages files, one can do:
#
# make messages-clean
# make messages
#
# This is needed only when a .mes file is modified.
messages-clean: maintainer-clean-local
if GENERATE_MESSAGES
# Define rule to build logging source files from message file
messages: flex_option_messages.h flex_option_messages.cc
@echo Message files regenerated
flex_option_messages.h flex_option_messages.cc: flex_option_messages.mes
$(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/hooks/dhcp/flex_option/flex_option_messages.mes
else
messages flex_option_messages.h flex_option_messages.cc:
@echo Messages generation disabled. Configure with --enable-generate-messages to enable it.
endif

View File

@ -0,0 +1,223 @@
// Copyright (C) 2019 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/.
#include <config.h>
#include <flex_option.h>
#include <cc/simple_parser.h>
#include <dhcp/dhcp4.h>
#include <dhcp/libdhcp++.h>
#include <dhcp/option_definition.h>
#include <dhcp/option_space.h>
#include <dhcpsrv/cfgmgr.h>
#include <eval/eval_context.h>
using namespace isc;
using namespace isc::data;
using namespace isc::dhcp;
using namespace isc::eval;
using namespace std;
namespace isc {
namespace flex_option {
FlexOptionImpl::OptionConfig::OptionConfig(uint16_t code) : code_(code) {
}
FlexOptionImpl::OptionConfig::~OptionConfig() {
}
FlexOptionImpl::FlexOptionImpl() {
}
FlexOptionImpl::~FlexOptionImpl() {
option_config_map_.clear();
}
void
FlexOptionImpl::configure(ConstElementPtr options) {
if (!options) {
isc_throw(BadValue, "'options' parameter is mandatory");
}
if (options->getType() != Element::list) {
isc_throw(BadValue, "'options' parameter must be a list");
}
if (options->empty()) {
return;
}
for (auto option : options->listValue()) {
parseOptionConfig(option);
}
}
void
FlexOptionImpl::parseOptionConfig(ConstElementPtr option) {
uint16_t family = CfgMgr::instance().getFamily();
if (!option) {
isc_throw(BadValue, "null option element");
}
if (option->getType() != Element::map) {
isc_throw(BadValue, "option element is not a map");
}
ConstElementPtr code_elem = option->get("code");
ConstElementPtr name_elem = option->get("name");
if (!code_elem && !name_elem) {
isc_throw(BadValue, "'code' or 'name' must be specified: "
<< option->str());
}
uint16_t code;
if (code_elem) {
if (code_elem->getType() != Element::integer) {
isc_throw(BadValue, "'code' must be an integer: "
<< code_elem->str());
}
int64_t value = code_elem->intValue();
int64_t max_code;
if (family == AF_INET) {
max_code = numeric_limits<uint8_t>::max();
} else {
max_code = numeric_limits<uint16_t>::max();
}
if ((value < 0) || (value > max_code)) {
isc_throw(OutOfRange, "invalid 'code' value " << value
<< " not in [0.." << max_code << "]");
}
if (family == AF_INET) {
if (value == DHO_PAD) {
isc_throw(BadValue,
"invalid 'code' value 0: reserved for PAD");
} else if (value == DHO_END) {
isc_throw(BadValue,
"invalid 'code' value 255: reserved for END");
}
} else {
if (value == 0) {
isc_throw(BadValue, "invalid 'code' value 0: reserved");
}
}
code = static_cast<uint16_t>(value);
}
if (name_elem) {
if (name_elem->getType() != Element::string) {
isc_throw(BadValue, "'name' must be a string: "
<< name_elem->str());
}
string name = name_elem->stringValue();
if (name.empty()) {
isc_throw(BadValue, "'name' must not be empty");
}
string space;
if (family == AF_INET) {
space = DHCP4_OPTION_SPACE;
} else {
space = DHCP6_OPTION_SPACE;
}
OptionDefinitionPtr def = LibDHCP::getOptionDef(space, name);
if (!def) {
def = LibDHCP::getRuntimeOptionDef(space, name);
}
if (!def) {
def = LibDHCP::getLastResortOptionDef(space, name);
}
if (!def) {
isc_throw(BadValue, "no known '" << name << "' option in '"
<< space << "' space");
}
if (code_elem && (def->getCode() != code)) {
isc_throw(BadValue, "option '" << name << "' has code "
<< def->getCode() << " but 'code' is " << code);
}
code = def->getCode();
}
if (option_config_map_.count(code)) {
isc_throw(BadValue, "option " << code << " was already specified");
}
Option::Universe universe;
if (family == AF_INET) {
universe = Option::V4;
} else {
universe = Option::V6;
}
OptionConfigPtr opt_cfg(new OptionConfig(code));
ConstElementPtr add_elem = option->get("add");
if (add_elem) {
if (add_elem->getType() != Element::string) {
isc_throw(BadValue, "'add' must be a string: "
<< add_elem->str());
}
string add = add_elem->stringValue();
if (add.empty()) {
isc_throw(BadValue, "'add' must not be empty");
}
opt_cfg->setAction(ADD);
opt_cfg->setText(add);
try {
EvalContext eval_ctx(universe);
eval_ctx.parseString(add, EvalContext::PARSER_STRING);
ExpressionPtr expr(new Expression(eval_ctx.expression));
opt_cfg->setExpr(expr);
} catch (const std::exception& ex) {
isc_throw(BadValue, "can't parse add expression ["
<< add << "] error: " << ex.what());
}
}
ConstElementPtr supersede_elem = option->get("supersede");
if (supersede_elem) {
if (supersede_elem->getType() != Element::string) {
isc_throw(BadValue, "'supersede' must be a string: "
<< supersede_elem->str());
}
string supersede = supersede_elem->stringValue();
if (supersede.empty()) {
isc_throw(BadValue, "'supersede' must not be empty");
}
if (opt_cfg->getAction() != NONE) {
isc_throw(BadValue, "multiple actions: " << option->str());
}
opt_cfg->setAction(SUPERSEDE);
opt_cfg->setText(supersede);
try {
EvalContext eval_ctx(universe);
eval_ctx.parseString(supersede, EvalContext::PARSER_STRING);
ExpressionPtr expr(new Expression(eval_ctx.expression));
opt_cfg->setExpr(expr);
} catch (const std::exception& ex) {
isc_throw(BadValue, "can't parse supersede expression ["
<< supersede << "] error: " << ex.what());
}
}
ConstElementPtr remove_elem = option->get("remove");
if (remove_elem) {
if (remove_elem->getType() != Element::string) {
isc_throw(BadValue, "'remove' must be a string: "
<< remove_elem->str());
}
string remove = remove_elem->stringValue();
if (remove.empty()) {
isc_throw(BadValue, "'remove' must not be empty");
}
if (opt_cfg->getAction() != NONE) {
isc_throw(BadValue, "multiple actions: " << option->str());
}
opt_cfg->setAction(REMOVE);
opt_cfg->setText(remove);
try {
EvalContext eval_ctx(universe);
eval_ctx.parseString(remove, EvalContext::PARSER_BOOL);
ExpressionPtr expr(new Expression(eval_ctx.expression));
opt_cfg->setExpr(expr);
} catch (const std::exception& ex) {
isc_throw(BadValue, "can't parse remove expression ["
<< remove << "] error: " << ex.what());
}
}
}
} // end of namespace flex_option
} // end of namespace isc

View File

@ -0,0 +1,32 @@
// Copyright (C) 2019 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/.
/**
@page libdhcp_flex_option Kea Flexible Option Hooks Library
@section libdhcp_flex_optionIntro Introduction
Welcome to Kea Flexible Option Hooks Library. This documentation is
addressed to developers who are interested in the internal operation
of the Flexible Option library. This file provides information needed
to understand and perhaps extend this library.
This documentation is stand-alone: you should have read and understood
the <a href="https://jenkins.isc.org/job/Kea_doc/doxygen/">Kea
Developer's Guide</a> and in particular its section about hooks.
@section flex_option Flexible Option Overview
Flexible Option (or flex_option) is a Hook library that can be loaded by
either kea-dhcp4 and kea-dhcp6 servers to extend them with additional
option value setting mechanisms.
@section flex_optionCode Flexible Option Code Overview
Todo
*/

View File

@ -0,0 +1,214 @@
// Copyright (C) 2019 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 FLEX_OPTION_H
#define FLEX_OPTION_H
#include <cc/data.h>
#include <eval/evaluate.h>
#include <eval/token.h>
#include <string>
#include <map>
namespace isc {
namespace flex_option {
/// @brief Flex Option implementation.
class FlexOptionImpl {
public:
/// @brief Action.
///
/// Currently supported actions are:
/// - add (if not already existing)
/// - supersede (as add but also when already existing)
/// - remove
enum Action {
NONE,
ADD,
SUPERSEDE,
REMOVE
};
/// @brief Option configuration.
///
/// Per option configuration.
class OptionConfig {
public:
/// @brief Constructor.
///
/// @param option code.
OptionConfig(uint16_t code);
/// @brief Destructor.
virtual ~OptionConfig();
/// @brief Return option code.
///
/// @return option code.
uint16_t getCode() const {
return (code_);
}
/// @brief Set action.
///
/// @param action the action.
void setAction(Action action) {
action_ = action;
}
/// @brief Return action.
///
/// @return action.
Action getAction() const {
return (action_);
}
/// @brief Set textual expression.
///
/// @param text the textual expression.
void setText(const std::string& text) {
text_ = text;
};
/// @brief Get textual expression.
///
/// @return textual expression.
const std::string& getText() const {
return (text_);
}
/// @brief Set match expression.
///
/// @param expr the match expression.
void setExpr(const isc::dhcp::ExpressionPtr& expr) {
expr_ = expr;
}
/// @brief Get match expression.
///
/// @return the match expression.
const isc::dhcp::ExpressionPtr& getExpr() const {
return (expr_);
}
private:
/// @brief The code.
uint16_t code_;
/// @brief The action.
Action action_;
/// @brief The textual expression.
std::string text_;
/// @brief The match expression.
isc::dhcp::ExpressionPtr expr_;
};
/// @brief The type of shared pointers to option config.
typedef boost::shared_ptr<OptionConfig> OptionConfigPtr;
/// @brief The type of the option config map.
typedef std::map<uint16_t, OptionConfigPtr> OptionConfigMap;
/// @brief Constructor.
FlexOptionImpl();
/// @brief Destructor.
~FlexOptionImpl();
/// @brief Get the option config map.
///
/// @return The option config map.
const OptionConfigMap& getOptionConfigMap() const {
return (option_config_map_);
}
/// @brief Configure the Flex Option implementation.
///
/// @param options The element with option config list.
/// @throw BadValue and similar exceptions on error.
void configure(isc::data::ConstElementPtr options);
/// @brief Process a query / response pair.
///
/// @tparam PktType The type of pointers to packets: Pkt4Ptr or Pkt6Ptr.
/// @param universe The option universe: Option::V4 or Option::V6.
/// @param query The query packet.
/// @param response The response packet.
template <typename PktType>
void process(isc::dhcp::Option::Universe universe,
PktType query, PktType response) {
for (auto pair : getOptionConfigMap()) {
const OptionConfigPtr& opt_cfg = pair.second;
std::string value;
isc::dhcp::OptionBuffer buffer;
isc::dhcp::OptionPtr opt = response->getOption(opt_cfg->getCode());
switch (opt_cfg->getAction()) {
case NONE:
break;
case ADD:
if (opt) {
break;
}
value = isc::dhcp::evaluateString(*opt_cfg->getExpr(), *query);
if (value.empty()) {
break;
}
buffer.assign(value.begin(), value.end());
opt.reset(new isc::dhcp::Option(universe, opt_cfg->getCode(),
buffer));
response->addOption(opt);
break;
case SUPERSEDE:
value = isc::dhcp::evaluateString(*opt_cfg->getExpr(), *query);
if (value.empty()) {
break;
}
while (opt) {
response->delOption(opt_cfg->getCode());
opt = response->getOption(opt_cfg->getCode());
}
buffer.assign(value.begin(), value.end());
opt.reset(new isc::dhcp::Option(universe, opt_cfg->getCode(),
buffer));
response->addOption(opt);
break;
case REMOVE:
if (!opt) {
break;
}
if (!isc::dhcp::evaluateBool(*opt_cfg->getExpr(), *query)) {
break;
}
while (opt) {
response->delOption(opt_cfg->getCode());
opt = response->getOption(opt_cfg->getCode());
}
break;
}
}
}
private:
/// @brief The option config map (code and pointer to option config).
OptionConfigMap option_config_map_;
/// @brief Parse an option config.
///
/// @param option The element with option config.
/// @throw BadValue and similar exceptionson error.
void parseOptionConfig(isc::data::ConstElementPtr option);
};
/// @brief The type of shared pointers to Flex Option implementations.
typedef boost::shared_ptr<FlexOptionImpl> FlexOptionImplPtr;
} // end of namespace flex_option
} // end of namespace isc
#endif

View File

@ -0,0 +1,115 @@
// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the End User License
// Agreement. See COPYING file in the premium/ directory.
#include <config.h>
#include <flex_option.h>
#include <flex_option_log.h>
#include <cc/command_interpreter.h>
#include <hooks/hooks.h>
#include <dhcp/pkt4.h>
#include <dhcp/pkt6.h>
namespace isc {
namespace flex_option {
FlexOptionImplPtr impl;
} // end of namespace flex_option
} // end of namespace isc
using namespace isc;
using namespace isc::data;
using namespace isc::dhcp;
using namespace isc::hooks;
using namespace isc::flex_option;
// Functions accessed by the hooks framework use C linkage to avoid the name
// mangling that accompanies use of the C++ compiler as well as to avoid
// issues related to namespaces.
extern "C" {
/// @brief This callout is called at the "pkt4_send" hook.
///
/// It retrieves v4 query and response packets, and then adds, supersedes
/// or removes option values in the response according to expressions
/// evaluated on the query.
///
/// @param handle CalloutHandle.
///
/// @return 0 upon success, non-zero otherwise
int pkt4_send(CalloutHandle& handle) {
// Get the parameters.
Pkt4Ptr query;
Pkt4Ptr response;
handle.getArgument("query4", query);
handle.getArgument("response4", response);
try {
impl->process<Pkt4Ptr>(Option::V4, query, response);
} catch (const std::exception& ex) {
LOG_ERROR(flex_option_logger, FLEX_OPTION_PROCESS_ERROR)
.arg(query->getLabel())
.arg(ex.what());
}
return (0);
}
/// @brief This callout is called at the "pkt6_send" hook.
///
/// It retrieves v6 query and response packets, and then adds, supersedes
/// or removes option values in the response according to expressions
/// evaluated on the query.
///
/// @param handle CalloutHandle.
///
/// @return 0 upon success, non-zero otherwise
int pkt6_send(CalloutHandle& handle) {
// Get the parameters.
Pkt6Ptr query;
Pkt6Ptr response;
handle.getArgument("query6", query);
handle.getArgument("response6", response);
try {
impl->process<Pkt6Ptr>(Option::V6, query, response);
} catch (const std::exception& ex) {
LOG_ERROR(flex_option_logger, FLEX_OPTION_PROCESS_ERROR)
.arg(query->getLabel())
.arg(ex.what());
}
return (0);
}
/// @brief This function is called when the library is loaded.
///
/// @param handle library handle
/// @return 0 when initialization is successful, 1 otherwise
int load(LibraryHandle& handle) {
try {
impl.reset(new FlexOptionImpl());
ConstElementPtr options = handle.getParameter("options");
impl->configure(options);
} catch (const std::exception& ex) {
LOG_ERROR(flex_option_logger, FLEX_OPTION_LOAD_ERROR)
.arg(ex.what());
return (1);
}
return (0);
}
/// @brief This function is called when the library is unloaded.
///
/// @return always 0.
int unload() {
impl.reset();
LOG_INFO(flex_option_logger, FLEX_OPTION_UNLOAD);
return (0);
}
} // end extern "C"

View File

@ -0,0 +1,17 @@
// Copyright (C) 2019 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/.
#include <config.h>
#include <flex_option_log.h>
namespace isc {
namespace flex_option {
isc::log::Logger flex_option_logger("flex-option-hooks");
} // namespace flex_option
} // namespace isc

View File

@ -0,0 +1,21 @@
// Copyright (C) 2019 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 FLEX_OPTION_LOG_H
#define FLEX_OPTION_LOG_H
#include <log/logger_support.h>
#include <log/macros.h>
#include <flex_option_messages.h>
namespace isc {
namespace flex_option {
extern isc::log::Logger flex_option_logger;
} // end of namespace flex_option
} // end of namespace isc
#endif

View File

@ -0,0 +1,23 @@
// File created from ../../../../src/hooks/dhcp/flex_option/flex_option_messages.mes on Tue Oct 01 2019 14:08
#include <cstddef>
#include <log/message_types.h>
#include <log/message_initializer.h>
extern const isc::log::MessageID FLEX_OPTION_LOAD_ERROR = "FLEX_OPTION_LOAD_ERROR";
extern const isc::log::MessageID FLEX_OPTION_PROCESS_ERROR = "FLEX_OPTION_PROCESS_ERROR";
extern const isc::log::MessageID FLEX_OPTION_UNLOAD = "FLEX_OPTION_UNLOAD";
namespace {
const char* values[] = {
"FLEX_OPTION_LOAD_ERROR", "loading Flex Option hooks library failed: %1",
"FLEX_OPTION_PROCESS_ERROR", "An error occurred processing query %1: %2",
"FLEX_OPTION_UNLOAD", "Flex Option hooks library has been unloaded",
NULL
};
const isc::log::MessageInitializer initializer(values);
} // Anonymous namespace

View File

@ -0,0 +1,12 @@
// File created from ../../../../src/hooks/dhcp/flex_option/flex_option_messages.mes on Tue Oct 01 2019 14:08
#ifndef FLEX_OPTION_MESSAGES_H
#define FLEX_OPTION_MESSAGES_H
#include <log/message_types.h>
extern const isc::log::MessageID FLEX_OPTION_LOAD_ERROR;
extern const isc::log::MessageID FLEX_OPTION_PROCESS_ERROR;
extern const isc::log::MessageID FLEX_OPTION_UNLOAD;
#endif // FLEX_OPTION_MESSAGES_H

View File

@ -0,0 +1,17 @@
# Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC")
% FLEX_OPTION_LOAD_ERROR loading Flex Option hooks library failed: %1
This error message indicates an error during loading the Flex Option
hooks library. The details of the error are provided as argument of
the log message.
% FLEX_OPTION_PROCESS_ERROR An error occurred processing query %1: %2
This error message indicates an error during processing of a query
by the Flex Option hooks library. The client identification information
from the query and the details of the error are provided as arguments
of the log message.
% FLEX_OPTION_UNLOAD Flex Option hooks library has been unloaded
This info message indicates that the Flex Option hooks library has been
unloaded.

View File

@ -0,0 +1 @@
/html

View File

@ -0,0 +1,55 @@
SUBDIRS = .
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
AM_CPPFLAGS += -I$(top_builddir)/src/hooks/dhcp/flex_option -I$(top_srcdir)/src/hooks/dhcp/flex_option
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CPPFLAGS += -DFLEX_OPTION_LIB_SO=\"$(abs_top_builddir)/src/hooks/dhcp/flex_option/.libs/libdhcp_flex_option.so\"
AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
AM_CXXFLAGS = $(KEA_CXXFLAGS)
if USE_STATIC_LINK
AM_LDFLAGS = -static
endif
# Unit test data files need to get installed.
EXTRA_DIST =
CLEANFILES = *.gcno *.gcda
# TESTS_ENVIRONMENT = $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
LOG_COMPILER = $(LIBTOOL)
AM_LOG_FLAGS = --mode=execute
TESTS =
if HAVE_GTEST
TESTS += flex_option_unittests
flex_option_unittests_SOURCES = run_unittests.cc
flex_option_unittests_SOURCES += callout_unittests.cc
flex_option_unittests_SOURCES += load_unload_unittests.cc
flex_option_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
flex_option_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS)
flex_option_unittests_CXXFLAGS = $(AM_CXXFLAGS)
flex_option_unittests_LDADD = $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la
flex_option_unittests_LDADD += $(top_builddir)/src/lib/eval/libkea-eval.la
flex_option_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
flex_option_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
flex_option_unittests_LDADD += $(top_builddir)/src/lib/database/libkea-database.la
flex_option_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
flex_option_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
flex_option_unittests_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la
flex_option_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la
flex_option_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
flex_option_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
flex_option_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
flex_option_unittests_LDADD += $(LOG4CPLUS_LIBS)
flex_option_unittests_LDADD += $(CRYPTO_LIBS)
flex_option_unittests_LDADD += $(BOOST_LIBS)
flex_option_unittests_LDADD += $(GTEST_LDADD)
endif
noinst_PROGRAMS = $(TESTS)

View File

@ -0,0 +1,40 @@
// Copyright (C) 2019 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 This file contains tests which verify flexible option callouts.
#include <config.h>
#include <asiolink/asio_wrapper.h>
#include <exceptions/exceptions.h>
#include <dhcp/dhcp4.h>
#include <dhcp/dhcp6.h>
#include <dhcp/option.h>
#include <dhcp/pkt4.h>
#include <dhcp/pkt6.h>
#include <dhcpsrv/host.h>
#include <dhcp/option.h>
#include <dhcp/option_string.h>
#include <hooks/callout_manager.h>
#include <hooks/hooks.h>
#include <flex_option.h>
#include <flex_option_log.h>
#include <gtest/gtest.h>
using namespace std;
using namespace isc;
using namespace isc::dhcp;
using namespace isc::hooks;
using namespace isc::flex_option;
extern "C" {
extern int pkt4_send(CalloutHandle& handle);
extern int pkt6_send(CalloutHandle& handle);
}
namespace {
} // end of anonymous namespace

View File

@ -0,0 +1,79 @@
// Copyright (C) 2019 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 This file contains tests which exercise the load and unload
/// functions in the flexible option hook library. In order to test the load
/// function, one must be able to pass it hook library parameters. The
/// the only way to populate these parameters is by actually loading the
/// library via HooksManager::loadLibraries().
#include <config.h>
#include <exceptions/exceptions.h>
#include <hooks/hooks_manager.h>
#include <gtest/gtest.h>
#include <cc/data.h>
#include <errno.h>
using namespace std;
using namespace isc;
using namespace hooks;
using namespace isc::data;
namespace {
/// @brief Test fixture for testing loading and unloading the flex-id library
class LibLoadTest : public ::testing::Test {
public:
/// @brief Constructor
LibLoadTest() {
reset();
}
/// @brief Destructor
/// Removes files that may be left over from previous tests
virtual ~LibLoadTest() {
reset();
}
/// @brief Removes files that may be left over from previous tests
virtual void reset() {
HooksManager::unloadLibraries();
}
void addLib(const std::string& lib, ConstElementPtr params) {
libraries_.push_back(make_pair(lib, params));
}
void loadLibs() {
EXPECT_TRUE(HooksManager::loadLibraries(libraries_));
}
void unloadLibs() {
EXPECT_NO_THROW(HooksManager::unloadLibraries());
}
HookLibsCollection libraries_;
};
// Simple test that checks the library can be loaded and unloaded several times.
TEST_F(LibLoadTest, validLoad) {
// Prepare parameters for the callout parameters library.
ElementPtr params = Element::createMap();
ElementPtr options = Element::createList();
params->set("options", options);
addLib(FLEX_OPTION_LIB_SO, params);
loadLibs();
unloadLibs();
loadLibs();
unloadLibs();
}
} // end of anonymous namespace

View File

@ -0,0 +1,19 @@
// Copyright (C) 2019 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/.
#include <config.h>
#include <log/logger_support.h>
#include <gtest/gtest.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
isc::log::initLogger();
int result = RUN_ALL_TESTS();
return (result);
}

View File

@ -0,0 +1,17 @@
// Copyright (C) 2019 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/.
#include <config.h>
#include <hooks/hooks.h>
extern "C" {
/// @brief returns Kea hooks version.
int version() {
return (KEA_HOOKS_VERSION);
}
}