2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-22 09:57:41 +00:00

[#3315] use internal IOService for hooks

This commit is contained in:
Razvan Becheriu 2024-03-27 23:38:26 +02:00
parent 4f935d2b86
commit 4398fb6c4a
58 changed files with 1079 additions and 343 deletions

View File

@ -89,6 +89,7 @@ CtrlAgentProcess::run() {
size_t
CtrlAgentProcess::runIO() {
getIOService()->pollExternalIOServices();
size_t cnt = getIOService()->poll();
if (!cnt) {
cnt = getIOService()->runOne();
@ -192,6 +193,17 @@ CtrlAgentProcess::configure(isc::data::ConstElementPtr config_set,
int rcode = 0;
config::parseAnswer(rcode, answer);
/// Let postponed hook initializations to run.
try {
getIOService()->pollExternalIOServices();
} catch (const std::exception& ex) {
std::ostringstream err;
err << "Error initializing hooks: "
<< ex.what();
return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
}
return (answer);
}

View File

@ -29,10 +29,10 @@ const int CheckExistsAddTransaction::FQDN_NOT_IN_USE_EVT;
CheckExistsAddTransaction::
CheckExistsAddTransaction(asiolink::IOServicePtr& io_service,
dhcp_ddns::NameChangeRequestPtr& ncr,
DdnsDomainPtr& forward_domain,
DdnsDomainPtr& reverse_domain,
D2CfgMgrPtr& cfg_mgr)
dhcp_ddns::NameChangeRequestPtr& ncr,
DdnsDomainPtr& forward_domain,
DdnsDomainPtr& reverse_domain,
D2CfgMgrPtr& cfg_mgr)
: NameChangeTransaction(io_service, ncr, forward_domain, reverse_domain,
cfg_mgr) {
if (ncr->getChangeType() != isc::dhcp_ddns::CHG_ADD) {

View File

@ -26,10 +26,10 @@ const int CheckExistsRemoveTransaction::REMOVING_REV_PTRS_ST;
CheckExistsRemoveTransaction::
CheckExistsRemoveTransaction(asiolink::IOServicePtr& io_service,
dhcp_ddns::NameChangeRequestPtr& ncr,
DdnsDomainPtr& forward_domain,
DdnsDomainPtr& reverse_domain,
D2CfgMgrPtr& cfg_mgr)
dhcp_ddns::NameChangeRequestPtr& ncr,
DdnsDomainPtr& forward_domain,
DdnsDomainPtr& reverse_domain,
D2CfgMgrPtr& cfg_mgr)
: NameChangeTransaction(io_service, ncr, forward_domain, reverse_domain,
cfg_mgr) {
if (ncr->getChangeType() != isc::dhcp_ddns::CHG_REMOVE) {

View File

@ -55,12 +55,15 @@ to the end of this list.
- @b Description: this callout is executed when the server has completed
its (re)configuration. The server provides received and parsed configuration
structures to the hook library. It also provides a pointer to the IOService
object which is used by the server to run asynchronous operations. The hooks
libraries can use this IOService object to schedule asynchronous tasks which
are triggered by the Kea DHCP DDNS's main loop. The hook library should hold
the provided pointer until the library is unloaded. The D2CfgContext
object provides access to the D2 running configuration.
structures to the hook library.
If the library uses any IO operations, it should create a local IOService
object and register it to the main IOService which is also provided. This way
the local IOService is used by the server to run asynchronous operations. The
hooks library can use the local IOService object to schedule asynchronous
tasks which are triggered by the D2 server's main loop. The hook library
should hold the provided pointer until the library is unloaded at which stage
it must unregister the local IOService.
The D2CfgContext object provides access to the D2 running configuration.
- <b>Next step status</b>: If any callout sets the status to DROP, the server
considers the configuration is incorrect and rejects it using the error

View File

@ -129,6 +129,7 @@ D2Process::run() {
size_t
D2Process::runIO() {
getIOService()->pollExternalIOServices();
// We want to block until at least one handler is called. We'll use
// boost::asio::io_service directly for two reasons. First off
// asiolink::IOService::runOne is a void and boost::asio::io_service::stopped
@ -148,7 +149,6 @@ D2Process::runIO() {
// service is stopped it will return immediately with a cnt of zero.
cnt = getIOService()->runOne();
}
return (cnt);
}
@ -301,6 +301,16 @@ D2Process::configure(isc::data::ConstElementPtr config_set, bool check_only) {
}
}
/// Let postponed hook initializations to run.
try {
getIOService()->pollExternalIOServices();
} catch (const std::exception& ex) {
std::ostringstream err;
err << "Error initializing hooks: "
<< ex.what();
return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
}
// If we are here, configuration was valid, at least it parsed correctly
// and therefore contained no invalid values.
// Return the success answer from above.

View File

@ -26,10 +26,10 @@ const int NameRemoveTransaction::REMOVING_REV_PTRS_ST;
NameRemoveTransaction::
NameRemoveTransaction(asiolink::IOServicePtr& io_service,
dhcp_ddns::NameChangeRequestPtr& ncr,
DdnsDomainPtr& forward_domain,
DdnsDomainPtr& reverse_domain,
D2CfgMgrPtr& cfg_mgr)
dhcp_ddns::NameChangeRequestPtr& ncr,
DdnsDomainPtr& forward_domain,
DdnsDomainPtr& reverse_domain,
D2CfgMgrPtr& cfg_mgr)
: NameChangeTransaction(io_service, ncr, forward_domain, reverse_domain,
cfg_mgr) {
if (ncr->getChangeType() != isc::dhcp_ddns::CHG_REMOVE) {

View File

@ -28,10 +28,10 @@ const int SimpleAddTransaction::FQDN_NOT_IN_USE_EVT;
SimpleAddTransaction::
SimpleAddTransaction(asiolink::IOServicePtr& io_service,
dhcp_ddns::NameChangeRequestPtr& ncr,
DdnsDomainPtr& forward_domain,
DdnsDomainPtr& reverse_domain,
D2CfgMgrPtr& cfg_mgr)
dhcp_ddns::NameChangeRequestPtr& ncr,
DdnsDomainPtr& forward_domain,
DdnsDomainPtr& reverse_domain,
D2CfgMgrPtr& cfg_mgr)
: NameChangeTransaction(io_service, ncr, forward_domain, reverse_domain,
cfg_mgr) {
if (ncr->getChangeType() != isc::dhcp_ddns::CHG_ADD) {

View File

@ -28,10 +28,10 @@ const int SimpleAddWithoutDHCIDTransaction::FQDN_NOT_IN_USE_EVT;
SimpleAddWithoutDHCIDTransaction::
SimpleAddWithoutDHCIDTransaction(asiolink::IOServicePtr& io_service,
dhcp_ddns::NameChangeRequestPtr& ncr,
DdnsDomainPtr& forward_domain,
DdnsDomainPtr& reverse_domain,
D2CfgMgrPtr& cfg_mgr)
dhcp_ddns::NameChangeRequestPtr& ncr,
DdnsDomainPtr& forward_domain,
DdnsDomainPtr& reverse_domain,
D2CfgMgrPtr& cfg_mgr)
: NameChangeTransaction(io_service, ncr, forward_domain, reverse_domain,
cfg_mgr) {
if (ncr->getChangeType() != isc::dhcp_ddns::CHG_ADD) {

View File

@ -25,10 +25,10 @@ const int SimpleRemoveTransaction::REMOVING_REV_PTRS_ST;
SimpleRemoveTransaction::
SimpleRemoveTransaction(asiolink::IOServicePtr& io_service,
dhcp_ddns::NameChangeRequestPtr& ncr,
DdnsDomainPtr& forward_domain,
DdnsDomainPtr& reverse_domain,
D2CfgMgrPtr& cfg_mgr)
dhcp_ddns::NameChangeRequestPtr& ncr,
DdnsDomainPtr& forward_domain,
DdnsDomainPtr& reverse_domain,
D2CfgMgrPtr& cfg_mgr)
: NameChangeTransaction(io_service, ncr, forward_domain, reverse_domain,
cfg_mgr) {
if (ncr->getChangeType() != isc::dhcp_ddns::CHG_REMOVE) {

View File

@ -25,10 +25,10 @@ const int SimpleRemoveWithoutDHCIDTransaction::REMOVING_REV_PTRS_ST;
SimpleRemoveWithoutDHCIDTransaction::
SimpleRemoveWithoutDHCIDTransaction(asiolink::IOServicePtr& io_service,
dhcp_ddns::NameChangeRequestPtr& ncr,
DdnsDomainPtr& forward_domain,
DdnsDomainPtr& reverse_domain,
D2CfgMgrPtr& cfg_mgr)
dhcp_ddns::NameChangeRequestPtr& ncr,
DdnsDomainPtr& forward_domain,
DdnsDomainPtr& reverse_domain,
D2CfgMgrPtr& cfg_mgr)
: NameChangeTransaction(io_service, ncr, forward_domain, reverse_domain,
cfg_mgr) {
if (ncr->getChangeType() != isc::dhcp_ddns::CHG_REMOVE) {

View File

@ -1159,7 +1159,7 @@ TEST_F(CheckExistsRemoveTransactionTest, selectingRevServerHandler) {
// Post a server IO error event. This simulates an IO error occurring
// and a need to select the new server.
ASSERT_NO_THROW(name_remove->postNextEvent(NameChangeTransaction::
SERVER_IO_ERROR_EVT))
SERVER_IO_ERROR_EVT))
<< " num_servers: " << num_servers
<< " selections: " << i;
}

View File

@ -378,7 +378,7 @@ public:
/// @return the number of default items added to the tree
size_t setDefaults(data::ElementPtr config) {
return (SimpleParser::setListDefaults(config, D2SimpleParser::
TSIG_KEY_DEFAULTS));
TSIG_KEY_DEFAULTS));
}
/// @brief Attempts to parse the given element into a list of TSIGKeyInfos
@ -422,7 +422,7 @@ public:
/// @return the number of default items added to the tree
virtual size_t setDefaults(data::ElementPtr config) {
return (SimpleParser::setDefaults(config, D2SimpleParser::
DNS_SERVER_DEFAULTS));
DNS_SERVER_DEFAULTS));
}
/// @brief Attempts to parse the given element into a DnsServerInfo
@ -468,7 +468,7 @@ public:
/// @return the number of default items added to the tree
virtual size_t setDefaults(data::ElementPtr config) {
return (SimpleParser::setListDefaults(config, D2SimpleParser::
DNS_SERVER_DEFAULTS));
DNS_SERVER_DEFAULTS));
}
/// @brief Attempts to parse the given element into a list of DnsServerInfos
@ -579,9 +579,8 @@ public:
// We don't use SimpleParser::setListDefaults() as this does
// not handle sub-lists or sub-maps
for (auto const& domain : config->listValue()) {
cnt += D2SimpleParser::
setDdnsDomainDefaults(domain, D2SimpleParser::
DDNS_DOMAIN_DEFAULTS);
cnt += D2SimpleParser::setDdnsDomainDefaults(domain, D2SimpleParser::
DDNS_DOMAIN_DEFAULTS);
}
return (cnt);

View File

@ -1159,7 +1159,7 @@ TEST_F(NameRemoveTransactionTest, selectingRevServerHandler) {
// Post a server IO error event. This simulates an IO error occurring
// and a need to select the new server.
ASSERT_NO_THROW(name_remove->postNextEvent(NameChangeTransaction::
SERVER_IO_ERROR_EVT))
SERVER_IO_ERROR_EVT))
<< " num_servers: " << num_servers
<< " selections: " << i;
}

View File

@ -717,7 +717,7 @@ TEST_F(SimpleRemoveTransactionTest, selectingRevServerHandler) {
// Post a server IO error event. This simulates an IO error occurring
// and a need to select the new server.
ASSERT_NO_THROW(name_remove->postNextEvent(NameChangeTransaction::
SERVER_IO_ERROR_EVT));
SERVER_IO_ERROR_EVT));
}
// We should have exhausted the list of servers. Processing another

View File

@ -717,7 +717,7 @@ TEST_F(SimpleRemoveWithoutDHCIDTransactionTest, selectingRevServerHandler) {
// Post a server IO error event. This simulates an IO error occurring
// and a need to select the new server.
ASSERT_NO_THROW(name_remove->postNextEvent(NameChangeTransaction::
SERVER_IO_ERROR_EVT));
SERVER_IO_ERROR_EVT));
}
// We should have exhausted the list of servers. Processing another

View File

@ -241,6 +241,7 @@ ControlledDhcpv4Srv::commandLibReloadHandler(const string&, ConstElementPtr) {
HookLibsCollection loaded = HooksManager::getLibraryInfo();
HooksManager::prepareUnloadLibraries();
static_cast<void>(HooksManager::unloadLibraries());
getIOService()->clearExternalIOServices();
bool multi_threading_enabled = true;
uint32_t thread_count = 0;
uint32_t queue_size = 0;
@ -452,7 +453,7 @@ ControlledDhcpv4Srv::commandConfigSetHandler(const string&,
/// Let postponed hook initializations to run.
try {
getIOService()->poll();
getIOService()->pollExternalIOServices();
} catch (const std::exception& ex) {
std::ostringstream err;
err << "Error initializing hooks: "

View File

@ -55,13 +55,17 @@ to the end of this list.
- @b Description: this callout is executed when the server has completed
its (re)configuration. The server provides received and parsed configuration
structures to the hook library. It also provides a pointer to the IOService
object which is used by the server to run asynchronous operations. The hooks
libraries can use this IOService object to schedule asynchronous tasks which
are triggered by the DHCP server's main loop. The hook library should hold the
provided pointer until the library is unloaded. The NetworkState object
provides access to the DHCP service state of the server and allows for
enabling and disabling the DHCP service from the hooks libraries.
structures to the hook library.
If the library uses any IO operations, it should create a local IOService
object and register it to the main IOService which is also provided. This way
the local IOService is used by the server to run asynchronous operations. The
hooks library can use the local IOService object to schedule asynchronous
tasks which are triggered by the DHCP server's main loop. The hook library
should hold the provided pointer until the library is unloaded at which stage
it must unregister the local IOService.
The NetworkState object provides access to the DHCP service state of the
server and allows for enabling and disabling the DHCP service from the hooks
libraries.
- <b>Next step status</b>: If any callout sets the status to DROP, the server
will interrupt the reconfiguration process. The hook callout is expected to

View File

@ -706,6 +706,7 @@ Dhcpv4Srv::~Dhcpv4Srv() {
}
LOG_ERROR(dhcp4_logger, DHCP4_SRV_UNLOAD_LIBRARIES_ERROR).arg(msg);
}
getIOService()->clearExternalIOServices();
io_service_->stop();
io_service_->restart();
try {
@ -1132,6 +1133,7 @@ Dhcpv4Srv::run() {
#endif // ENABLE_AFL
try {
runOne();
getIOService()->pollExternalIOServices();
getIOService()->poll();
} catch (const std::exception& e) {
// General catch-all exception that are not caught by more specific

View File

@ -31,6 +31,9 @@ void start_service(void) {
isc_throw(isc::Unexpected, "start service failed");
};
IOServicePtr io_service;
IOServicePtr main_io_service;
} // end anonymous
// Functions accessed by the hooks framework use C linkage to avoid the name
@ -40,6 +43,7 @@ extern "C" {
int
do_load_impl(LibraryHandle& handle) {
io_service.reset(new IOService());
// Determine if this callout is configured to fail.
isc::dhcp::SrvConfigPtr config;
isc::data::ConstElementPtr const& parameters(handle.getParameters());
@ -51,9 +55,17 @@ do_load_impl(LibraryHandle& handle) {
return (0);
}
int
do_unload_impl() {
if (main_io_service) {
main_io_service->unregisterExternalIOService(io_service);
}
return (0);
}
int (*do_load)(LibraryHandle& handle) = do_load_impl;
int (*do_unload)();
int (*do_unload)() = do_unload_impl;
/// @brief Callout which appends library number and provided arguments to
/// the marker file for dhcp4_srv_configured callout.
@ -79,13 +91,13 @@ dhcp4_srv_configured(CalloutHandle& handle) {
// Get the IO context to post start_service on it.
std::string error("");
IOServicePtr io_context;
try {
handle.getArgument("io_context", io_context);
if (!io_context) {
handle.getArgument("io_context", main_io_service);
if (!main_io_service) {
error = "null io_context";
}
io_context->post(start_service);
main_io_service->registerExternalIOService(io_service);
io_service->post(start_service);
} catch (const std::exception& ex) {
error = "no io_context in arguments";
}

View File

@ -244,6 +244,7 @@ ControlledDhcpv6Srv::commandLibReloadHandler(const string&, ConstElementPtr) {
HookLibsCollection loaded = HooksManager::getLibraryInfo();
HooksManager::prepareUnloadLibraries();
static_cast<void>(HooksManager::unloadLibraries());
getIOService()->clearExternalIOServices();
bool multi_threading_enabled = true;
uint32_t thread_count = 0;
uint32_t queue_size = 0;
@ -454,7 +455,7 @@ ControlledDhcpv6Srv::commandConfigSetHandler(const string&,
/// Let postponed hook initializations to run.
try {
getIOService()->poll();
getIOService()->pollExternalIOServices();
} catch (const std::exception& ex) {
std::ostringstream err;
err << "Error initializing hooks: "

View File

@ -55,14 +55,17 @@ to the end of this list.
- @b Description: this callout is executed when the server has completed
its (re)configuration. The server provides received and parsed configuration
structures to the hook library. It also provides a pointer to the IOService
object which is used by the server to run asynchronous operations. The hooks
libraries can use this IOService object to schedule asynchronous tasks which
are triggered by the DHCP server's main loop. The hook library should hold the
provided pointer until the library is unloaded. The NetworkState object
provides access to the DHCP service state of the server and allows for
enabling and disabling the DHCP service from the hooks libraries.
structures to the hook library.
If the library uses any IO operations, it should create a local IOService
object and register it to the main IOService which is also provided. This way
the local IOService is used by the server to run asynchronous operations. The
hooks library can use the local IOService object to schedule asynchronous
tasks which are triggered by the DHCP server's main loop. The hook library
should hold the provided pointer until the library is unloaded at which stage
it must unregister the local IOService.
The NetworkState object provides access to the DHCP service state of the
server and allows for enabling and disabling the DHCP service from the hooks
libraries.
- <b>Next step status</b>: If any callout sets the status to DROP, the server
will interrupt the reconfiguration process. The hook callout is expected to

View File

@ -302,6 +302,7 @@ Dhcpv6Srv::~Dhcpv6Srv() {
}
LOG_ERROR(dhcp6_logger, DHCP6_SRV_UNLOAD_LIBRARIES_ERROR).arg(msg);
}
getIOService()->clearExternalIOServices();
io_service_->stop();
io_service_->restart();
try {
@ -613,6 +614,7 @@ Dhcpv6Srv::run() {
#endif // ENABLE_AFL
try {
runOne();
getIOService()->pollExternalIOServices();
getIOService()->poll();
} catch (const std::exception& e) {
// General catch-all standard exceptions that are not caught by more

View File

@ -31,6 +31,9 @@ void start_service(void) {
isc_throw(isc::Unexpected, "start service failed");
};
IOServicePtr io_service;
IOServicePtr main_io_service;
} // end anonymous
// Functions accessed by the hooks framework use C linkage to avoid the name
@ -40,6 +43,7 @@ extern "C" {
int
do_load_impl(LibraryHandle& handle) {
io_service.reset(new IOService());
// Determine if this callout is configured to fail.
isc::dhcp::SrvConfigPtr config;
isc::data::ConstElementPtr const& parameters(handle.getParameters());
@ -51,9 +55,17 @@ do_load_impl(LibraryHandle& handle) {
return (0);
}
int
do_unload_impl() {
if (main_io_service) {
main_io_service->unregisterExternalIOService(io_service);
}
return (0);
}
int (*do_load)(LibraryHandle& handle) = do_load_impl;
int (*do_unload)();
int (*do_unload)() = do_unload_impl;
/// @brief Callout which appends library number and provided arguments to
/// the marker file for dhcp6_srv_configured callout.
@ -79,13 +91,13 @@ dhcp6_srv_configured(CalloutHandle& handle) {
// Get the IO context to post start_service on it.
std::string error("");
IOServicePtr io_context;
try {
handle.getArgument("io_context", io_context);
if (!io_context) {
handle.getArgument("io_context", main_io_service);
if (!main_io_service) {
error = "null io_context";
}
io_context->post(start_service);
main_io_service->registerExternalIOService(io_service);
io_service->post(start_service);
} catch (const std::exception& ex) {
error = "no io_context in arguments";
}

View File

@ -65,6 +65,7 @@ NetconfProcess::run() {
size_t
NetconfProcess::runIO() {
getIOService()->pollExternalIOServices();
size_t cnt = getIOService()->poll();
if (!cnt) {
cnt = getIOService()->runOne();
@ -85,6 +86,17 @@ NetconfProcess::configure(isc::data::ConstElementPtr config_set,
getCfgMgr()->simpleParseConfig(config_set, check_only);
int rcode = 0;
config::parseAnswer(rcode, answer);
/// Let postponed hook initializations to run.
try {
getIOService()->pollExternalIOServices();
} catch (const std::exception& ex) {
std::ostringstream err;
err << "Error initializing hooks: "
<< ex.what();
return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
}
return (answer);
}

View File

@ -12,12 +12,8 @@
#include <config.h>
#include <cc/data.h>
#include <dhcpsrv/cfgmgr.h>
#include <hooks/hooks_manager.h>
#include <process/daemon.h>
#include <dhcpsrv/testutils/lib_load_test_fixture.h>
#include <testutils/gtest_utils.h>
#include <testutils/lib_load_test_fixture.h>
#include <gtest/gtest.h>
#include <errno.h>

View File

@ -12,11 +12,8 @@
#include <config.h>
#include <flex_option.h>
#include <dhcpsrv/cfgmgr.h>
#include <hooks/hooks_manager.h>
#include <process/daemon.h>
#include <testutils/lib_load_test_fixture.h>
#include <dhcpsrv/testutils/lib_load_test_fixture.h>
#include <testutils/gtest_utils.h>
#include <gtest/gtest.h>
#include <errno.h>

View File

@ -142,6 +142,9 @@ have been introduced. These hook points are used by the DHCPv4 and the
DHCPv6 servers respectively, to pass the instance of the IOService
(via "io_context" argument) to the hooks libraries which require to
schedule asynchronous tasks.
The hook's IOService object must be registered on the server's main IOService by
calling registerExternalIOService and must unregister it on "unload" hook point
by calling unregisterExternalIOService.
It is also worth to note that the blocking reception of the DHCP packets
may cause up to 1 second delays in the asynchronous operations. This is

View File

@ -46,9 +46,8 @@ extern "C" {
/// @param handle callout handle.
int dhcp4_srv_configured(CalloutHandle& handle) {
try {
isc::asiolink::IOServicePtr io_service;
handle.getArgument("io_context", io_service);
if (!io_service) {
handle.getArgument("io_context", impl->getMainIOService());
if (!impl->getMainIOService()) {
// Should not happen!
handle.setStatus(isc::hooks::CalloutHandle::NEXT_STEP_DROP);
const string error("Error: io_context is null");
@ -57,7 +56,8 @@ int dhcp4_srv_configured(CalloutHandle& handle) {
}
isc::dhcp::NetworkStatePtr network_state;
handle.getArgument("network_state", network_state);
impl->startServices(io_service, network_state, HAServerType::DHCPv4);
impl->startServices(network_state, HAServerType::DHCPv4);
impl->getMainIOService()->registerExternalIOService(impl->getIOService());
} catch (const std::exception& ex) {
LOG_ERROR(ha_logger, HA_DHCP4_START_SERVICE_FAILED)
@ -164,9 +164,8 @@ int lease4_server_decline(CalloutHandle& handle) {
/// @param handle callout handle.
int dhcp6_srv_configured(CalloutHandle& handle) {
try {
isc::asiolink::IOServicePtr io_service;
handle.getArgument("io_context", io_service);
if (!io_service) {
handle.getArgument("io_context", impl->getMainIOService());
if (!impl->getMainIOService()) {
// Should not happen!
handle.setStatus(isc::hooks::CalloutHandle::NEXT_STEP_DROP);
const string error("Error: io_context is null");
@ -175,7 +174,8 @@ int dhcp6_srv_configured(CalloutHandle& handle) {
}
isc::dhcp::NetworkStatePtr network_state;
handle.getArgument("network_state", network_state);
impl->startServices(io_service, network_state, HAServerType::DHCPv6);
impl->startServices(network_state, HAServerType::DHCPv6);
impl->getMainIOService()->registerExternalIOService(impl->getIOService());
} catch (const std::exception& ex) {
LOG_ERROR(ha_logger, HA_DHCP6_START_SERVICE_FAILED)
@ -442,6 +442,17 @@ int load(LibraryHandle& handle) {
///
/// @return 0 if deregistration was successful, 1 otherwise
int unload() {
if (impl) {
if (impl->getMainIOService()) {
impl->getMainIOService()->unregisterExternalIOService(impl->getIOService());
}
impl->getIOService()->stop();
impl->getIOService()->restart();
try {
impl->getIOService()->poll();
} catch (...) {
}
}
impl.reset();
LOG_INFO(ha_logger, HA_DEINIT_OK);
return (0);

View File

@ -31,7 +31,7 @@ namespace isc {
namespace ha {
HAImpl::HAImpl()
: config_(), services_(new HAServiceMapper()) {
: io_service_(new IOService()), config_(), services_(new HAServiceMapper()) {
}
void
@ -40,13 +40,12 @@ HAImpl::configure(const ConstElementPtr& input_config) {
}
void
HAImpl::startServices(const IOServicePtr& io_service,
const NetworkStatePtr& network_state,
HAImpl::startServices(const NetworkStatePtr& network_state,
const HAServerType& server_type) {
auto configs = config_->getAll();
for (auto id = 0; id < configs.size(); ++id) {
// Create the HA service and crank up the state machine.
auto service = boost::make_shared<HAService>(id, io_service, network_state,
auto service = boost::make_shared<HAService>(id, io_service_, network_state,
configs[id], server_type);
for (auto const& peer_config : configs[id]->getAllServersConfig()) {
services_->map(peer_config.first, service);
@ -54,7 +53,7 @@ HAImpl::startServices(const IOServicePtr& io_service,
}
// Schedule a start of the services. This ensures we begin after
// the dust has settled and Kea MT mode has been firmly established.
io_service->post([&]() {
io_service_->post([&]() {
for (auto const& service : services_->getAll()) {
service->startClientAndListener();
}

View File

@ -45,13 +45,11 @@ public:
/// The caller must ensure that the HA configuration is valid before
/// calling this function.
///
/// @param io_service IO service object provided by the DHCP server.
/// @param network_state pointer to the object holding a state of the
/// DHCP service (enabled/disabled).
/// @param server_type DHCP server type for which the HA service should
/// be created.
void startServices(const asiolink::IOServicePtr& io_service,
const dhcp::NetworkStatePtr& network_state,
void startServices(const dhcp::NetworkStatePtr& network_state,
const HAServerType& server_type);
/// @brief Destructor.
@ -223,8 +221,42 @@ public:
HAServicePtr getHAServiceByServerName(const std::string& command_name,
data::ConstElementPtr args) const;
/// @brief Get the hook I/O service.
///
/// @return the hook I/O service.
isc::asiolink::IOServicePtr& getIOService() {
return (io_service_);
}
/// @brief Set the hook I/O service.
///
/// @param io_service the hook I/O service.
void setIOService(isc::asiolink::IOServicePtr io_service) {
io_service_ = io_service;
}
/// @brief Get the main I/O service.
///
/// @return the main I/O service.
isc::asiolink::IOServicePtr& getMainIOService() {
return (main_io_service_);
}
/// @brief Set the main I/O service.
///
/// @param io_service the main I/O service.
void setMainIOService(isc::asiolink::IOServicePtr io_service) {
main_io_service_ = io_service;
}
protected:
/// @brief The hook I/O service.
isc::asiolink::IOServicePtr io_service_;
/// @brief The main I/O service.
isc::asiolink::IOServicePtr main_io_service_;
/// @brief Holds parsed configuration.
HAConfigMapperPtr config_;

View File

@ -12,11 +12,8 @@
#include <config.h>
#include <cc/data.h>
#include <dhcpsrv/cfgmgr.h>
#include <hooks/hooks_manager.h>
#include <process/daemon.h>
#include <testutils/lib_load_test_fixture.h>
#include <dhcpsrv/testutils/lib_load_test_fixture.h>
#include <testutils/gtest_utils.h>
#include <gtest/gtest.h>
#include <errno.h>

View File

@ -127,11 +127,12 @@ public:
const std::string& expected_response) {
io_service_.reset(new IOService());
ha_impl_.reset(new HAImpl());
ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(ha_impl_->configure(createValidJsonConfiguration()));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
ConstElementPtr command = Element::fromJSON(ha_sync_command);
@ -174,13 +175,14 @@ public:
TEST_F(HAImplTest, startServices) {
// Valid configuration must be provided prior to starting the service.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(createValidJsonConfiguration()));
// Network state is also required.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
// Start the service for DHCPv4 server.
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
// Make sure that the HA service has been created for the requested
@ -193,13 +195,14 @@ TEST_F(HAImplTest, startServices) {
TEST_F(HAImplTest, startServices6) {
// Valid configuration must be provided prior to starting the service.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(createValidJsonConfiguration()));
// Network state is also required.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv6));
// Start the service for DHCPv4 server.
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv6));
// Make sure that the HA service has been created for the requested
@ -216,11 +219,12 @@ TEST_F(HAImplTest, buffer4Receive) {
// Create implementation object and configure it.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(ha_config));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
// Initially the HA service is in the waiting state and serves no scopes.
@ -329,11 +333,12 @@ TEST_F(HAImplTest, subnet4Select) {
// Create implementation object and configure it.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(ha_config));
// Starting the service is required before any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
// The hub is a standby server and by default serves no scopes. Explicitly
@ -386,11 +391,12 @@ TEST_F(HAImplTest, subnet4SelectSharedNetwork) {
// Create implementation object and configure it.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(ha_config));
// Starting the service is required before any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
// The hub is a standby server and by default serves no scopes. Explicitly
@ -446,11 +452,12 @@ TEST_F(HAImplTest, subnet4SelectSingleRelationship) {
// Create implementation object and configure it.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(ha_config));
// Starting the service is required before any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
// Create callout handle to be used for passing arguments to the
@ -487,11 +494,12 @@ TEST_F(HAImplTest, subnet4SelectDropNoServerName) {
// Create implementation object and configure it.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(ha_config));
// Starting the service is required before any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
// The hub is a standby server and by default serves no scopes. Explicitly
@ -532,11 +540,12 @@ TEST_F(HAImplTest, subnet4SelectDropInvalidServerNameType) {
// Create implementation object and configure it.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(ha_config));
// Starting the service is required before any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
// The hub is a standby server and by default serves no scopes. Explicitly
@ -581,11 +590,12 @@ TEST_F(HAImplTest, subnet4SelectDropNotInScope) {
// Create implementation object and configure it.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(ha_config));
// Starting the service is required before any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
// This server serves server1/server2 scopes but not server3/server4 scopes.
@ -633,11 +643,12 @@ TEST_F(HAImplTest, subnet4SelectNoSubnet) {
// Create implementation object and configure it.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(ha_config));
// Starting the service is required before any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
test_ha_impl_->services_->get("server2")->serveFailoverScopes();
@ -679,11 +690,12 @@ TEST_F(HAImplTest, buffer6Receive) {
// Create implementation object and configure it.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(ha_config));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv6));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv6));
// Initially the HA service is in the waiting state and serves no scopes.
@ -769,11 +781,12 @@ TEST_F(HAImplTest, subnet6Select) {
// Create implementation object and configure it.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(ha_config));
// Starting the service is required before any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv6));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv6));
// The hub is a standby server and by default serves no scopes. Explicitly
@ -823,11 +836,12 @@ TEST_F(HAImplTest, subnet6SelectSharedNetwork) {
// Create implementation object and configure it.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(ha_config));
// Starting the service is required before any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv6));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv6));
// The hub is a standby server and by default serves no scopes. Explicitly
@ -883,11 +897,12 @@ TEST_F(HAImplTest, subnet6SelectSingleRelationship) {
// Create implementation object and configure it.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(ha_config));
// Starting the service is required before any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv6));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv6));
// Create callout handle to be used for passing arguments to the
@ -924,11 +939,12 @@ TEST_F(HAImplTest, subnet6SelectDropNoServerName) {
// Create implementation object and configure it.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(ha_config));
// Starting the service is required before any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv6));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv6));
// The hub is a standby server and by default serves no scopes. Explicitly
@ -969,11 +985,12 @@ TEST_F(HAImplTest, subnet6SelectDropInvalidServerNameType) {
// Create implementation object and configure it.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(ha_config));
// Starting the service is required before any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv6));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv6));
// The hub is a standby server and by default serves no scopes. Explicitly
@ -1018,11 +1035,12 @@ TEST_F(HAImplTest, subnet6SelectDropNotInScope) {
// Create implementation object and configure it.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(ha_config));
// Starting the service is required before any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv6));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv6));
// This server serves server1/server2 scopes but not server3/server4 scopes.
@ -1070,11 +1088,12 @@ TEST_F(HAImplTest, subnet6SelectNoSubnet) {
// Create implementation object and configure it.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(ha_config));
// Starting the service is required before any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv6));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv6));
test_ha_impl_->services_->get("server2")->serveFailoverScopes();
@ -1112,11 +1131,12 @@ TEST_F(HAImplTest, subnet6SelectNoSubnet) {
TEST_F(HAImplTest, leases4Committed) {
// Create implementation object and configure it.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(createValidJsonConfiguration()));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
// Make sure we wait for the acks from the backup server to be able to
@ -1198,11 +1218,12 @@ TEST_F(HAImplTest, leases4Committed) {
TEST_F(HAImplTest, leases4CommittedMultipleRelationships) {
// Create implementation object and configure it.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(createValidHubJsonConfiguration()));
// Starting the service is required before running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
// By enabling this setting we ensure that the lease updates are always
@ -1254,11 +1275,12 @@ TEST_F(HAImplTest, leases4CommittedMultipleRelationships) {
TEST_F(HAImplTest, leases4CommittedMultipleRelationshipsNoServerName) {
// Create implementation object and configure it.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(createValidHubJsonConfiguration()));
// Starting the service is required before running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
// By enabling this setting we ensure that the lease updates are always
@ -1306,11 +1328,12 @@ TEST_F(HAImplTest, leases4CommittedMultipleRelationshipsNoServerName) {
TEST_F(HAImplTest, leases4CommittedMultipleRelationshipsInvalidServerName) {
// Create implementation object and configure it.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(createValidHubJsonConfiguration()));
// Starting the service is required before running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
// By enabling this setting we ensure that the lease updates are always
@ -1360,11 +1383,12 @@ TEST_F(HAImplTest, leases4CommittedMultipleRelationshipsInvalidServerName) {
TEST_F(HAImplTest, leases6Committed) {
// Create implementation object and configure it.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(createValidJsonConfiguration()));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv6));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv6));
// Make sure we wait for the acks from the backup server to be able to
@ -1445,11 +1469,12 @@ TEST_F(HAImplTest, leases6Committed) {
TEST_F(HAImplTest, leases6CommittedMultipleRelationships) {
// Create implementation object and configure it.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(createValidHubJsonConfiguration()));
// Starting the service is required before running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv6));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv6));
// By enabling this setting we ensure that the lease updates are always
@ -1500,11 +1525,12 @@ TEST_F(HAImplTest, leases6CommittedMultipleRelationships) {
TEST_F(HAImplTest, leases6CommittedMultipleRelationshipsNoServerName) {
// Create implementation object and configure it.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(createValidHubJsonConfiguration()));
// Starting the service is required before running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv6));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv6));
// By enabling this setting we ensure that the lease updates are always
@ -1551,11 +1577,12 @@ TEST_F(HAImplTest, leases6CommittedMultipleRelationshipsNoServerName) {
TEST_F(HAImplTest, leases6CommittedMultipleRelationshipsInvalidServerName) {
// Create implementation object and configure it.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(createValidHubJsonConfiguration()));
// Starting the service is required before running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv6));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv6));
// By enabling this setting we ensure that the lease updates are always
@ -1682,11 +1709,12 @@ TEST_F(HAImplTest, synchronizeHandler) {
// Tests ha-continue command handler with a specified server name.
TEST_F(HAImplTest, continueHandler) {
ha_impl_.reset(new HAImpl());
ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(ha_impl_->configure(createValidJsonConfiguration()));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
ConstElementPtr command = Element::fromJSON("{"
@ -1711,11 +1739,12 @@ TEST_F(HAImplTest, continueHandler) {
// Tests ha-continue command handler without a server name.
TEST_F(HAImplTest, continueHandlerWithNoServerName) {
ha_impl_.reset(new HAImpl());
ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(ha_impl_->configure(createValidJsonConfiguration()));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
ConstElementPtr command = Element::fromJSON("{ \"command\": \"ha-continue\" }");
@ -1735,11 +1764,12 @@ TEST_F(HAImplTest, continueHandlerWithNoServerName) {
// Tests ha-continue command handler with wrong server name.
TEST_F(HAImplTest, continueHandlerWithWrongServerName) {
ha_impl_.reset(new HAImpl());
ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(ha_impl_->configure(createValidJsonConfiguration()));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
ConstElementPtr command = Element::fromJSON("{"
@ -1764,11 +1794,12 @@ TEST_F(HAImplTest, continueHandlerWithWrongServerName) {
// Tests status-get command processed handler.
TEST_F(HAImplTest, statusGet) {
ha_impl_.reset(new HAImpl());
ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(ha_impl_->configure(createValidJsonConfiguration()));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
std::string name = "status-get";
@ -1825,12 +1856,13 @@ TEST_F(HAImplTest, statusGet) {
// Tests status-get command processed handler for backup server.
TEST_F(HAImplTest, statusGetBackupServer) {
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(createValidJsonConfiguration()));
test_ha_impl_->config_->get()->setThisServerName("server3");
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
std::string name = "status-get";
@ -1875,11 +1907,12 @@ TEST_F(HAImplTest, statusGetBackupServer) {
// passive-backup state.
TEST_F(HAImplTest, statusGetPassiveBackup) {
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(createValidPassiveBackupJsonConfiguration()));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
std::string name = "status-get";
@ -1924,11 +1957,12 @@ TEST_F(HAImplTest, statusGetPassiveBackup) {
// hub-and-spoke mode.
TEST_F(HAImplTest, statusGetHubAndSpoke) {
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(createValidHubJsonConfiguration()));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
std::string name = "status-get";
@ -2009,11 +2043,12 @@ TEST_F(HAImplTest, statusGetHubAndSpoke) {
// Test ha-maintenance-notify command handler with server name.
TEST_F(HAImplTest, maintenanceNotify) {
ha_impl_.reset(new HAImpl());
ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(ha_impl_->configure(createValidJsonConfiguration()));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
ConstElementPtr command = Element::fromJSON(
@ -2041,11 +2076,12 @@ TEST_F(HAImplTest, maintenanceNotify) {
// Test ha-maintenance-notify command handler without server name.
TEST_F(HAImplTest, maintenanceNotifyNoServerName) {
ha_impl_.reset(new HAImpl());
ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(ha_impl_->configure(createValidJsonConfiguration()));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
ConstElementPtr command = Element::fromJSON(
@ -2072,11 +2108,12 @@ TEST_F(HAImplTest, maintenanceNotifyNoServerName) {
// Test ha-maintenance-notify command handler without server name.
TEST_F(HAImplTest, maintenanceNotifyBadServerName) {
ha_impl_.reset(new HAImpl());
ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(ha_impl_->configure(createValidJsonConfiguration()));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
ConstElementPtr command = Element::fromJSON(
@ -2104,11 +2141,12 @@ TEST_F(HAImplTest, maintenanceNotifyBadServerName) {
// Test ha-reset command handler with a specified server name.
TEST_F(HAImplTest, haReset) {
ha_impl_.reset(new HAImpl());
ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(ha_impl_->configure(createValidJsonConfiguration()));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
ConstElementPtr command = Element::fromJSON(
@ -2135,11 +2173,12 @@ TEST_F(HAImplTest, haReset) {
// Test ha-reset command handler without a specified server name.
TEST_F(HAImplTest, haResetNoServerName) {
ha_impl_.reset(new HAImpl());
ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(ha_impl_->configure(createValidJsonConfiguration()));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
ConstElementPtr command = Element::fromJSON(
@ -2163,11 +2202,12 @@ TEST_F(HAImplTest, haResetNoServerName) {
// Test ha-reset command handler with a wrong server name.
TEST_F(HAImplTest, haResetBadServerName) {
ha_impl_.reset(new HAImpl());
ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(ha_impl_->configure(createValidJsonConfiguration()));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
ConstElementPtr command = Element::fromJSON(
@ -2194,11 +2234,12 @@ TEST_F(HAImplTest, haResetBadServerName) {
// Test ha-heartbeat command handler with a specified server name.
TEST_F(HAImplTest, haHeartbeat) {
ha_impl_.reset(new HAImpl());
ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(ha_impl_->configure(createValidJsonConfiguration()));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
ConstElementPtr command = Element::fromJSON(
@ -2225,11 +2266,12 @@ TEST_F(HAImplTest, haHeartbeat) {
// Test ha-heartbeat command handler without a specified server name.
TEST_F(HAImplTest, haHeartbeatNoServerName) {
ha_impl_.reset(new HAImpl());
ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(ha_impl_->configure(createValidJsonConfiguration()));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
ConstElementPtr command = Element::fromJSON(
@ -2253,11 +2295,12 @@ TEST_F(HAImplTest, haHeartbeatNoServerName) {
// Test ha-heartbeat command handler with a wrong server name.
TEST_F(HAImplTest, haHeartbeatBadServerName) {
ha_impl_.reset(new HAImpl());
ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(ha_impl_->configure(createValidJsonConfiguration()));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
ConstElementPtr command = Element::fromJSON(
@ -2284,11 +2327,12 @@ TEST_F(HAImplTest, haHeartbeatBadServerName) {
// Test ha-sync-complete-notify command handler with a specified server name.
TEST_F(HAImplTest, haSyncCompleteNotify) {
ha_impl_.reset(new HAImpl());
ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(ha_impl_->configure(createValidJsonConfiguration()));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
ConstElementPtr command = Element::fromJSON(
@ -2316,11 +2360,12 @@ TEST_F(HAImplTest, haSyncCompleteNotify) {
// Test ha-sync-complete-notify command handler without a specified server name.
TEST_F(HAImplTest, haSyncCompleteNotifyNoServerName) {
ha_impl_.reset(new HAImpl());
ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(ha_impl_->configure(createValidJsonConfiguration()));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
ConstElementPtr command = Element::fromJSON(
@ -2345,11 +2390,12 @@ TEST_F(HAImplTest, haSyncCompleteNotifyNoServerName) {
// Test ha-sync-complete-notify command handler with a wrong server name.
TEST_F(HAImplTest, haSyncCompleteNotifyBadServerName) {
ha_impl_.reset(new HAImpl());
ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(ha_impl_->configure(createValidJsonConfiguration()));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
ConstElementPtr command = Element::fromJSON(
@ -2376,11 +2422,12 @@ TEST_F(HAImplTest, haSyncCompleteNotifyBadServerName) {
// Test ha-scopes command handler with a specified server name.
TEST_F(HAImplTest, haScopes) {
ha_impl_.reset(new HAImpl());
ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(ha_impl_->configure(createValidJsonConfiguration()));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
ConstElementPtr command = Element::fromJSON(
@ -2408,11 +2455,12 @@ TEST_F(HAImplTest, haScopes) {
// Test ha-scopes command handler without a specified server name.
TEST_F(HAImplTest, haScopesNoServerName) {
ha_impl_.reset(new HAImpl());
ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(ha_impl_->configure(createValidJsonConfiguration()));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
ConstElementPtr command = Element::fromJSON(
@ -2439,11 +2487,12 @@ TEST_F(HAImplTest, haScopesNoServerName) {
// Test ha-scopes command handler with a wrong server name.
TEST_F(HAImplTest, haScopesBadServerName) {
ha_impl_.reset(new HAImpl());
ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(ha_impl_->configure(createValidJsonConfiguration()));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
ConstElementPtr command = Element::fromJSON(
@ -2472,11 +2521,12 @@ TEST_F(HAImplTest, haScopesBadServerName) {
TEST_F(HAImplTest, lease4ServerDecline) {
// Create implementation object and configure it.
test_ha_impl_.reset(new TestHAImpl());
test_ha_impl_->setIOService(io_service_);
ASSERT_NO_THROW(test_ha_impl_->configure(createValidJsonConfiguration()));
// Starting the service is required prior to running any callouts.
NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
ASSERT_NO_THROW(test_ha_impl_->startServices(io_service_, network_state,
ASSERT_NO_THROW(test_ha_impl_->startServices(network_state,
HAServerType::DHCPv4));
// Make sure we wait for the acks from the backup server to be able to

View File

@ -12,12 +12,8 @@
#include <config.h>
#include <cc/data.h>
#include <dhcpsrv/cfgmgr.h>
#include <hooks/hooks_manager.h>
#include <process/daemon.h>
#include <dhcpsrv/testutils/lib_load_test_fixture.h>
#include <testutils/gtest_utils.h>
#include <testutils/lib_load_test_fixture.h>
#include <gtest/gtest.h>
#include <errno.h>

View File

@ -12,12 +12,8 @@
#include <config.h>
#include <cc/data.h>
#include <dhcpsrv/cfgmgr.h>
#include <hooks/hooks_manager.h>
#include <process/daemon.h>
#include <dhcpsrv/testutils/lib_load_test_fixture.h>
#include <testutils/gtest_utils.h>
#include <testutils/lib_load_test_fixture.h>
#include <gtest/gtest.h>
#include <errno.h>

View File

@ -22,6 +22,7 @@
#include <sstream>
#include <string>
using namespace isc::asiolink;
using namespace isc::cb;
using namespace isc::dhcp;
using namespace isc::hooks;
@ -67,15 +68,15 @@ int load(LibraryHandle& /* handle */) {
/// @param handle callout handle passed to the callout.
/// @return 0 on success, 1 otherwise.
int dhcp4_srv_configured(CalloutHandle& handle) {
isc::asiolink::IOServicePtr io_service;
handle.getArgument("io_context", io_service);
if (!io_service) {
handle.getArgument("io_context", isc::dhcp::MySqlConfigBackendImpl::getMainIOService());
if (!isc::dhcp::MySqlConfigBackendImpl::getMainIOService()) {
const string error("Error: io_context is null");
handle.setArgument("error", error);
handle.setStatus(isc::hooks::CalloutHandle::NEXT_STEP_DROP);
return (1);
}
isc::dhcp::MySqlConfigBackendImpl::setIOService(io_service);
isc::dhcp::MySqlConfigBackendImpl::getIOService().reset(new IOService());
isc::dhcp::MySqlConfigBackendImpl::getMainIOService()->registerExternalIOService(isc::dhcp::MySqlConfigBackendImpl::getIOService());
return (0);
}
@ -86,15 +87,15 @@ int dhcp4_srv_configured(CalloutHandle& handle) {
/// @param handle callout handle passed to the callout.
/// @return 0 on success, 1 otherwise.
int dhcp6_srv_configured(CalloutHandle& handle) {
isc::asiolink::IOServicePtr io_service;
handle.getArgument("io_context", io_service);
if (!io_service) {
handle.getArgument("io_context", isc::dhcp::MySqlConfigBackendImpl::getMainIOService());
if (!isc::dhcp::MySqlConfigBackendImpl::getMainIOService()) {
const string error("Error: io_context is null");
handle.setArgument("error", error);
handle.setStatus(isc::hooks::CalloutHandle::NEXT_STEP_DROP);
return (1);
}
isc::dhcp::MySqlConfigBackendImpl::setIOService(io_service);
isc::dhcp::MySqlConfigBackendImpl::getIOService().reset(new IOService());
isc::dhcp::MySqlConfigBackendImpl::getMainIOService()->registerExternalIOService(isc::dhcp::MySqlConfigBackendImpl::getIOService());
return (0);
}
@ -106,6 +107,17 @@ int unload() {
// Unregister the factories and remove MySQL backends
isc::dhcp::MySqlConfigBackendDHCPv4::unregisterBackendType();
isc::dhcp::MySqlConfigBackendDHCPv6::unregisterBackendType();
if (isc::dhcp::MySqlConfigBackendImpl::getMainIOService()) {
isc::dhcp::MySqlConfigBackendImpl::getMainIOService()->unregisterExternalIOService(isc::dhcp::MySqlConfigBackendImpl::getIOService());
}
if (isc::dhcp::MySqlConfigBackendImpl::getIOService()) {
isc::dhcp::MySqlConfigBackendImpl::getIOService()->stop();
isc::dhcp::MySqlConfigBackendImpl::getIOService()->restart();
try {
isc::dhcp::MySqlConfigBackendImpl::getIOService()->poll();
} catch (...) {
}
}
return (0);
}

View File

@ -29,7 +29,8 @@ using namespace isc::util;
namespace isc {
namespace dhcp {
isc::asiolink::IOServicePtr MySqlConfigBackendImpl::io_service_ = isc::asiolink::IOServicePtr();
isc::asiolink::IOServicePtr MySqlConfigBackendImpl::io_service_;
isc::asiolink::IOServicePtr MySqlConfigBackendImpl::main_io_service_;
MySqlConfigBackendImpl::
ScopedAuditRevision::ScopedAuditRevision(MySqlConfigBackendImpl* impl,
@ -86,6 +87,11 @@ MySqlConfigBackendImpl(const std::string& space,
}
}
MySqlConfigBackendImpl::~MySqlConfigBackendImpl() {
/// nothing to do there. The conn_ connection will be deleted and its dtor
/// will take care of releasing the compiled statements and similar.
}
MySqlBindingPtr
MySqlConfigBackendImpl::createBinding(const Triplet<uint32_t>& triplet) {
if (triplet.unspecified()) {

View File

@ -115,7 +115,7 @@ public:
const db::DbCallback db_reconnect_callback);
/// @brief Destructor.
virtual ~MySqlConfigBackendImpl() {};
virtual ~MySqlConfigBackendImpl();
/// @brief Creates MySQL binding from an @c Optional of integer type.
///
@ -836,16 +836,32 @@ public:
return (parameters_);
}
/// @brief Sets IO service to be used by the MySQL config backend.
/// @brief Get the hook I/O service.
///
/// @param IOService object, used for all ASIO operations.
static void setIOService(const isc::asiolink::IOServicePtr& io_service) {
/// @return the hook I/O service.
static isc::asiolink::IOServicePtr& getIOService() {
return (io_service_);
}
/// @brief Set the hook I/O service.
///
/// @param io_service the hook I/O service.
static void setIOService(isc::asiolink::IOServicePtr io_service) {
io_service_ = io_service;
}
/// @brief Returns pointer to the IO service.
static isc::asiolink::IOServicePtr& getIOService() {
return (io_service_);
/// @brief Get the main I/O service.
///
/// @return the main I/O service.
static isc::asiolink::IOServicePtr& getMainIOService() {
return (main_io_service_);
}
/// @brief Set the main I/O service.
///
/// @param io_service the main I/O service.
static void setMainIOService(isc::asiolink::IOServicePtr io_service) {
main_io_service_ = io_service;
}
/// @brief Represents connection to the MySQL database.
@ -864,8 +880,11 @@ private:
/// @brief Connection parameters
isc::db::DatabaseConnection::ParameterMap parameters_;
/// @brief The IOService object, used for all ASIO operations.
/// @brief The hook I/O service.
static isc::asiolink::IOServicePtr io_service_;
/// @brief The main I/O service.
static isc::asiolink::IOServicePtr main_io_service_;
};
} // end of namespace isc::dhcp

View File

@ -12,12 +12,8 @@
#include <config.h>
#include <cc/data.h>
#include <dhcpsrv/cfgmgr.h>
#include <hooks/hooks_manager.h>
#include <process/daemon.h>
#include <dhcpsrv/testutils/lib_load_test_fixture.h>
#include <testutils/gtest_utils.h>
#include <testutils/lib_load_test_fixture.h>
#include <gtest/gtest.h>
#include <errno.h>

View File

@ -12,12 +12,8 @@
#include <config.h>
#include <cc/data.h>
#include <dhcpsrv/cfgmgr.h>
#include <hooks/hooks_manager.h>
#include <process/daemon.h>
#include <dhcpsrv/testutils/lib_load_test_fixture.h>
#include <testutils/gtest_utils.h>
#include <testutils/lib_load_test_fixture.h>
#include <gtest/gtest.h>
#include <errno.h>

View File

@ -22,6 +22,7 @@
#include <sstream>
#include <string>
using namespace isc::asiolink;
using namespace isc::cb;
using namespace isc::dhcp;
using namespace isc::hooks;
@ -67,15 +68,15 @@ int load(LibraryHandle& /* handle */) {
/// @param handle callout handle passed to the callout.
/// @return 0 on success, 1 otherwise.
int dhcp4_srv_configured(CalloutHandle& handle) {
isc::asiolink::IOServicePtr io_service;
handle.getArgument("io_context", io_service);
if (!io_service) {
handle.getArgument("io_context", isc::dhcp::PgSqlConfigBackendImpl::getMainIOService());
if (!isc::dhcp::PgSqlConfigBackendImpl::getMainIOService()) {
const string error("Error: io_context is null");
handle.setArgument("error", error);
handle.setStatus(isc::hooks::CalloutHandle::NEXT_STEP_DROP);
return (1);
}
isc::dhcp::PgSqlConfigBackendImpl::setIOService(io_service);
isc::dhcp::PgSqlConfigBackendImpl::getIOService().reset(new IOService());
isc::dhcp::PgSqlConfigBackendImpl::getMainIOService()->registerExternalIOService(isc::dhcp::PgSqlConfigBackendImpl::getIOService());
return (0);
}
@ -86,15 +87,15 @@ int dhcp4_srv_configured(CalloutHandle& handle) {
/// @param handle callout handle passed to the callout.
/// @return 0 on success, 1 otherwise.
int dhcp6_srv_configured(CalloutHandle& handle) {
isc::asiolink::IOServicePtr io_service;
handle.getArgument("io_context", io_service);
if (!io_service) {
handle.getArgument("io_context", isc::dhcp::PgSqlConfigBackendImpl::getMainIOService());
if (!isc::dhcp::PgSqlConfigBackendImpl::getMainIOService()) {
const string error("Error: io_context is null");
handle.setArgument("error", error);
handle.setStatus(isc::hooks::CalloutHandle::NEXT_STEP_DROP);
return (1);
}
isc::dhcp::PgSqlConfigBackendImpl::setIOService(io_service);
isc::dhcp::PgSqlConfigBackendImpl::getIOService().reset(new IOService());
isc::dhcp::PgSqlConfigBackendImpl::getMainIOService()->registerExternalIOService(isc::dhcp::PgSqlConfigBackendImpl::getIOService());
return (0);
}
@ -106,6 +107,17 @@ int unload() {
// Unregister the factories and remove PostgreSQL backends
isc::dhcp::PgSqlConfigBackendDHCPv4::unregisterBackendType();
isc::dhcp::PgSqlConfigBackendDHCPv6::unregisterBackendType();
if (isc::dhcp::PgSqlConfigBackendImpl::getMainIOService()) {
isc::dhcp::PgSqlConfigBackendImpl::getMainIOService()->unregisterExternalIOService(isc::dhcp::PgSqlConfigBackendImpl::getIOService());
}
if (isc::dhcp::PgSqlConfigBackendImpl::getIOService()) {
isc::dhcp::PgSqlConfigBackendImpl::getIOService()->stop();
isc::dhcp::PgSqlConfigBackendImpl::getIOService()->restart();
try {
isc::dhcp::PgSqlConfigBackendImpl::getIOService()->poll();
} catch (...) {
}
}
return (0);
}

View File

@ -28,7 +28,8 @@ using namespace isc::util;
namespace isc {
namespace dhcp {
isc::asiolink::IOServicePtr PgSqlConfigBackendImpl::io_service_ = isc::asiolink::IOServicePtr();
isc::asiolink::IOServicePtr PgSqlConfigBackendImpl::io_service_;
isc::asiolink::IOServicePtr PgSqlConfigBackendImpl::main_io_service_;
PgSqlTaggedStatement&
PgSqlConfigBackendImpl::getStatement(size_t /* index */) const {

View File

@ -800,18 +800,6 @@ public:
return (parameters_);
}
/// @brief Sets IO service to be used by the PostgreSQL config backend.
///
/// @param IOService object, used for all ASIO operations.
static void setIOService(const isc::asiolink::IOServicePtr& io_service) {
io_service_ = io_service;
}
/// @brief Returns pointer to the IO service.
static isc::asiolink::IOServicePtr& getIOService() {
return (io_service_);
}
/// @brief Fetches the SQL statement for a given statement index.
///
/// Derivations must override the implementation. The reference
@ -872,6 +860,34 @@ public:
/// @return Number of affected rows.
uint64_t updateDeleteQuery(size_t index, const db::PsqlBindArray& in_bindings);
/// @brief Get the hook I/O service.
///
/// @return the hook I/O service.
static isc::asiolink::IOServicePtr& getIOService() {
return (io_service_);
}
/// @brief Set the hook I/O service.
///
/// @param io_service the hook I/O service.
static void setIOService(isc::asiolink::IOServicePtr io_service) {
io_service_ = io_service;
}
/// @brief Get the main I/O service.
///
/// @return the main I/O service.
static isc::asiolink::IOServicePtr& getMainIOService() {
return (main_io_service_);
}
/// @brief Set the main I/O service.
///
/// @param io_service the main I/O service.
static void setMainIOService(isc::asiolink::IOServicePtr io_service) {
main_io_service_ = io_service;
}
/// @brief Represents connection to the PostgreSQL database.
db::PgSqlConnection conn_;
@ -888,9 +904,12 @@ private:
/// @brief Connection parameters
isc::db::DatabaseConnection::ParameterMap parameters_;
/// @brief The IOService object, used for all ASIO operations.
/// @brief The hook I/O service.
static isc::asiolink::IOServicePtr io_service_;
/// @brief The main I/O service.
static isc::asiolink::IOServicePtr main_io_service_;
/// @brief Statement index of the SQL statement to use for fetching
/// last inserted id in a given table.
size_t last_insert_id_index_;

View File

@ -12,11 +12,8 @@
#include <config.h>
#include <cc/data.h>
#include <dhcpsrv/cfgmgr.h>
#include <hooks/hooks_manager.h>
#include <process/daemon.h>
#include <testutils/lib_load_test_fixture.h>
#include <dhcpsrv/testutils/lib_load_test_fixture.h>
#include <testutils/gtest_utils.h>
#include <gtest/gtest.h>
#include <errno.h>

View File

@ -19,8 +19,9 @@ namespace isc {
namespace run_script {
IOServicePtr RunScriptImpl::io_service_;
IOServicePtr RunScriptImpl::main_io_service_;
RunScriptImpl::RunScriptImpl() : name_(), sync_(false) {
RunScriptImpl::RunScriptImpl() : io_context_(new IOService()), name_(), sync_(false) {
}
void

View File

@ -30,20 +30,6 @@ public:
/// @brief Destructor.
~RunScriptImpl() = default;
/// @brief Sets IO service to be used by the @ref ProcessSpawn instance.
///
/// @param io_service The IOService object, used for all ASIO operations.
static void setIOService(const isc::asiolink::IOServicePtr& io_service) {
io_service_ = io_service;
}
/// @brief Gets IO service to be used by the @ref ProcessSpawn instance.
///
/// @return The IOService object, used for all ASIO operations.
static isc::asiolink::IOServicePtr getIOService() {
return (io_service_);
}
/// @brief Extract boolean data and append to environment.
///
/// @param value The value to be exported to target script environment.
@ -256,7 +242,53 @@ public:
/// @brief This function parses and applies configuration parameters.
void configure(isc::hooks::LibraryHandle& handle);
/// @brief Get the hook I/O service.
///
/// @return the hook I/O service.
isc::asiolink::IOServicePtr& getIOContext() {
return (io_context_);
}
/// @brief Set the hook I/O service.
///
/// @param io_service the hook I/O service.
void setIOContext(isc::asiolink::IOServicePtr io_service) {
io_context_ = io_service;
}
/// @brief Get the hook I/O service.
///
/// @return the hook I/O service.
static isc::asiolink::IOServicePtr& getIOService() {
return (io_service_);
}
/// @brief Set the hook I/O service.
///
/// @param io_service the hook I/O service.
static void setIOService(isc::asiolink::IOServicePtr io_service) {
io_service_ = io_service;
}
/// @brief Get the main I/O service.
///
/// @return the main I/O service.
static isc::asiolink::IOServicePtr& getMainIOService() {
return (main_io_service_);
}
/// @brief Set the main I/O service.
///
/// @param io_service the main I/O service.
static void setMainIOService(isc::asiolink::IOServicePtr io_service) {
main_io_service_ = io_service;
}
private:
/// @brief The IOService object, used for all ASIO operations.
isc::asiolink::IOServicePtr io_context_;
/// @brief Script name.
std::string name_;
@ -267,8 +299,11 @@ private:
/// started.
bool sync_;
/// @brief The IOService object, used for all ASIO operations.
/// @brief The hook I/O service.
static isc::asiolink::IOServicePtr io_service_;
/// @brief The main I/O service.
static isc::asiolink::IOServicePtr main_io_service_;
};
/// @brief The type of shared pointers to Run Script implementations.

View File

@ -80,7 +80,18 @@ int load(LibraryHandle& handle) {
///
/// @return always 0.
int unload() {
if (RunScriptImpl::getMainIOService()) {
RunScriptImpl::getMainIOService()->unregisterExternalIOService(impl->getIOContext());
}
impl.reset();
if (RunScriptImpl::getIOService()) {
RunScriptImpl::getIOService()->stop();
RunScriptImpl::getIOService()->restart();
try {
RunScriptImpl::getIOService()->poll();
} catch (...) {
}
}
RunScriptImpl::setIOService(IOServicePtr());
LOG_INFO(run_script_logger, RUN_SCRIPT_UNLOAD);
return (0);
@ -91,16 +102,16 @@ int unload() {
/// @param handle callout handle.
int dhcp4_srv_configured(CalloutHandle& handle) {
try {
isc::asiolink::IOServicePtr io_service;
handle.getArgument("io_context", io_service);
if (!io_service) {
handle.getArgument("io_context", RunScriptImpl::getMainIOService());
if (!RunScriptImpl::getMainIOService()) {
// Should not happen!
handle.setStatus(isc::hooks::CalloutHandle::NEXT_STEP_DROP);
const string error("Error: io_context is null");
handle.setArgument("error", error);
return (1);
}
RunScriptImpl::setIOService(io_service);
RunScriptImpl::setIOService(impl->getIOContext());
RunScriptImpl::getMainIOService()->registerExternalIOService(impl->getIOContext());
} catch (const exception& ex) {
LOG_ERROR(run_script_logger, RUN_SCRIPT_LOAD_ERROR)
@ -116,16 +127,16 @@ int dhcp4_srv_configured(CalloutHandle& handle) {
/// @param handle callout handle.
int dhcp6_srv_configured(CalloutHandle& handle) {
try {
isc::asiolink::IOServicePtr io_service;
handle.getArgument("io_context", io_service);
if (!io_service) {
handle.getArgument("io_context", RunScriptImpl::getMainIOService());
if (!RunScriptImpl::getMainIOService()) {
// Should not happen!
handle.setStatus(isc::hooks::CalloutHandle::NEXT_STEP_DROP);
const string error("Error: io_context is null");
handle.setArgument("error", error);
return (1);
}
RunScriptImpl::setIOService(io_service);
RunScriptImpl::setIOService(impl->getIOContext());
RunScriptImpl::getMainIOService()->registerExternalIOService(impl->getIOContext());
} catch (const exception& ex) {
LOG_ERROR(run_script_logger, RUN_SCRIPT_LOAD_ERROR)

View File

@ -12,12 +12,8 @@
#include <config.h>
#include <cc/data.h>
#include <dhcpsrv/cfgmgr.h>
#include <hooks/hooks_manager.h>
#include <process/daemon.h>
#include <dhcpsrv/testutils/lib_load_test_fixture.h>
#include <testutils/gtest_utils.h>
#include <testutils/lib_load_test_fixture.h>
#include <gtest/gtest.h>
#include <errno.h>

View File

@ -18,98 +18,102 @@ namespace isc {
namespace asiolink {
class IOServiceImpl {
/// @brief Constructors and Destructor.
///
/// @note The copy constructor and the assignment operator are
/// intentionally defined as private, making this class non-copyable.
//@{
private:
IOServiceImpl(const IOService& source);
IOServiceImpl& operator=(const IOService& source);
public:
/// \brief The constructor
/// @brief The constructor.
IOServiceImpl() :
io_service_(),
work_(new boost::asio::io_service::work(io_service_)) {
};
/// \brief The destructor.
~IOServiceImpl() {
};
/// @brief The destructor.
~IOServiceImpl() = default;
//@}
/// \brief Start the underlying event loop.
/// @brief Start the underlying event loop.
///
/// This method does not return control to the caller until
/// the \c stop() method is called via some handler.
/// the @ref stop() method is called via some handler.
void run() {
io_service_.run();
};
/// \brief Run the underlying event loop for a single event.
/// @brief Run the underlying event loop for a single event.
///
/// This method return control to the caller as soon as the
/// first handler has completed. (If no handlers are ready when
/// it is run, it will block until one is.)
///
/// \return The number of handlers that were executed.
/// @return The number of handlers that were executed.
size_t runOne() {
return (static_cast<size_t>(io_service_.run_one()));
};
/// \brief Run the underlying event loop for a ready events.
/// @brief Run the underlying event loop for a ready events.
///
/// This method executes handlers for all ready events and returns.
/// It will return immediately if there are no ready events.
///
/// \return The number of handlers that were executed.
/// @return The number of handlers that were executed.
size_t poll() {
return (static_cast<size_t>(io_service_.poll()));
};
/// \brief Run the underlying event loop for a ready events.
/// @brief Run the underlying event loop for a ready events.
///
/// This method executes handlers for all ready events and returns.
/// It will return immediately if there are no ready events.
///
/// \return The number of handlers that were executed.
/// @return The number of handlers that were executed.
size_t pollOne() {
return (static_cast<size_t>(io_service_.poll_one()));
};
/// \brief Stop the underlying event loop.
/// @brief Stop the underlying event loop.
///
/// This will return the control to the caller of the \c run() method.
/// This will return the control to the caller of the @ref run() method.
void stop() {
io_service_.stop();
}
/// \brief Indicates if the IOService has been stopped.
/// @brief Indicates if the IOService has been stopped.
///
/// \return true if the IOService has been stopped, false otherwise.
/// @return true if the IOService has been stopped, false otherwise.
bool stopped() const {
return (io_service_.stopped());
}
/// \brief Restarts the IOService in preparation for a subsequent \c run() invocation.
/// @brief Restarts the IOService in preparation for a subsequent @ref run() invocation.
void restart() {
io_service_.reset();
}
/// \brief Removes IO service work object to let it finish running
/// @brief Removes IO service work object to let it finish running
/// when all handlers have been invoked.
void stopWork() {
work_.reset();
}
/// \brief Return the native \c io_service object used in this wrapper.
/// @brief Return the native @ref io_service object used in this wrapper.
///
/// This is a short term work around to support other Kea modules
/// that share the same \c io_service with the authoritative server.
/// that share the same @ref io_service with the authoritative server.
/// It will eventually be removed once the wrapper interface is
/// generalized.
boost::asio::io_service& getInternalIOService() {
return (io_service_);
}
/// \brief Post a callback on the IO service
/// @brief Post a callback on the IO service.
///
/// \param callback The callback to be run on the IO service.
/// @param callback The callback to be run on the IO service.
void post(const std::function<void ()>& callback) {
io_service_.post(callback);
}
@ -175,5 +179,25 @@ IOService::post(const std::function<void ()>& callback) {
return (io_impl_->post(callback));
}
void
IOService::registerExternalIOService(IOServicePtr io_service) {
external_io_services_.push_back(io_service);
}
void
IOService::unregisterExternalIOService(IOServicePtr io_service) {
auto it = std::find(external_io_services_.begin(), external_io_services_.end(), io_service);
if (it != external_io_services_.end()) {
external_io_services_.erase(it);
}
}
void
IOService::pollExternalIOServices() {
for (auto& io_service : external_io_services_) {
io_service->poll();
}
}
} // namespace asiolink
} // namespace isc

View File

@ -10,6 +10,7 @@
#include <boost/version.hpp>
#include <boost/shared_ptr.hpp>
#include <functional>
#include <list>
namespace boost {
namespace asio {
@ -26,84 +27,89 @@ namespace isc {
namespace asiolink {
class IOServiceImpl;
class IOService;
/// \brief The \c IOService class is a wrapper for the ASIO \c io_service
/// @brief Defines a smart pointer to an IOService instance.
typedef boost::shared_ptr<IOService> IOServicePtr;
/// @brief The @ref IOService class is a wrapper for the ASIO @ref io_service
/// class.
///
class IOService {
/// @brief Constructors and Destructor.
///
/// \name Constructors and Destructor
///
/// Note: The copy constructor and the assignment operator are
/// @note The copy constructor and the assignment operator are
/// intentionally defined as private, making this class non-copyable.
//@{
private:
IOService(const IOService& source);
IOService& operator=(const IOService& source);
public:
/// \brief The constructor
/// @brief The constructor.
IOService();
/// \brief The destructor.
/// @brief The destructor.
~IOService();
//@}
/// \brief Start the underlying event loop.
/// @brief Start the underlying event loop.
///
/// This method does not return control to the caller until
/// the \c stop() method is called via some handler.
/// the @ref stop() method is called via some handler.
void run();
/// \brief Run the underlying event loop for a single event.
/// @brief Run the underlying event loop for a single event.
///
/// This method return control to the caller as soon as the
/// first handler has completed. (If no handlers are ready when
/// it is run, it will block until one is.)
///
/// \return The number of handlers that were executed.
/// @return The number of handlers that were executed.
size_t runOne();
/// \brief Run the underlying event loop for a ready events.
/// @brief Run the underlying event loop for a ready events.
///
/// This method executes handlers for all ready events and returns.
/// It will return immediately if there are no ready events.
///
/// \return The number of handlers that were executed.
/// @return The number of handlers that were executed.
size_t poll();
/// \brief Run the underlying event loop for a ready events.
/// @brief Run the underlying event loop for a ready events.
///
/// This method executes handlers for all ready events and returns.
/// It will return immediately if there are no ready events.
///
/// \return The number of handlers that were executed.
/// @return The number of handlers that were executed.
size_t pollOne();
/// \brief Stop the underlying event loop.
/// @brief Stop the underlying event loop.
///
/// This will return the control to the caller of the \c run() method.
/// This will return the control to the caller of the @ref run() method.
void stop();
/// \brief Indicates if the IOService has been stopped.
/// @brief Indicates if the IOService has been stopped.
///
/// \return true if the IOService has been stopped, false otherwise.
/// @return true if the IOService has been stopped, false otherwise.
bool stopped() const;
/// \brief Restarts the IOService in preparation for a subsequent \c run() invocation.
/// @brief Restarts the IOService in preparation for a subsequent @ref run() invocation.
void restart();
/// \brief Removes IO service work object to let it finish running
/// @brief Removes IO service work object to let it finish running
/// when all handlers have been invoked.
void stopWork();
/// \brief Return the native \c io_service object used in this wrapper.
/// @brief Return the native @ref io_service object used in this wrapper.
///
/// This is a short term work around to support other Kea modules
/// that share the same \c io_service with the authoritative server.
/// that share the same @ref io_service with the authoritative server.
/// It will eventually be removed once the wrapper interface is
/// generalized.
///
/// @return The internal io_service object.
boost::asio::io_service& getInternalIOService();
/// \brief Post a callback to the end of the queue.
/// @brief Post a callback to the end of the queue.
///
/// Requests the callback be called sometime later. It is not guaranteed
/// by the underlying asio, but it can reasonably be expected the callback
@ -114,12 +120,39 @@ public:
/// by small bits that are called from time to time).
void post(const std::function<void ()>& callback);
private:
boost::shared_ptr<IOServiceImpl> io_impl_;
};
/// @brief Register external IOService.
///
/// @param io_service The external IOService to be registered.
void registerExternalIOService(IOServicePtr io_service);
/// @brief Defines a smart pointer to an IOService instance.
typedef boost::shared_ptr<IOService> IOServicePtr;
/// @brief Unregister external IOService.
///
/// @param io_service The external IOService to be unregistered.
void unregisterExternalIOService(IOServicePtr io_service);
/// @brief Clear the list of external IOService objects.
void clearExternalIOServices() {
external_io_services_.clear();
}
/// @brief The count of external IOService objects.
///
// @return The count of external IOService objects.
size_t externalIOServiceCount() {
return (external_io_services_.size());
}
/// @brief Poll external IOService objects.
void pollExternalIOServices();
private:
/// @brief The implementation.
boost::shared_ptr<IOServiceImpl> io_impl_;
/// @brief The list of external IOService objects.
std::list<IOServicePtr> external_io_services_;
};
} // namespace asiolink
} // namespace isc

View File

@ -45,4 +45,105 @@ TEST(IOService, post) {
EXPECT_EQ(3, called[2]);
}
TEST(IOService, externalIOService) {
IOServicePtr main_io_service(new IOService());
EXPECT_EQ(main_io_service->externalIOServiceCount(), 0);
int one_io_callback_count = 0;
auto one_f = [&one_io_callback_count] () {
one_io_callback_count++;
};
int two_io_callback_count = 0;
auto two_f = [&two_io_callback_count] () {
two_io_callback_count++;
};
{
IOServicePtr one_io_service(new IOService());
one_io_service->post(one_f);
IOServicePtr two_io_service(new IOService());
two_io_service->post(two_f);
main_io_service->pollExternalIOServices();
EXPECT_EQ(one_io_callback_count, 0);
EXPECT_EQ(two_io_callback_count, 0);
main_io_service->registerExternalIOService(one_io_service);
EXPECT_EQ(main_io_service->externalIOServiceCount(), 1);
main_io_service->registerExternalIOService(two_io_service);
EXPECT_EQ(main_io_service->externalIOServiceCount(), 2);
main_io_service->pollExternalIOServices();
EXPECT_EQ(one_io_callback_count, 1);
EXPECT_EQ(two_io_callback_count, 1);
one_io_service->post(one_f);
two_io_service->post(two_f);
}
EXPECT_EQ(main_io_service->externalIOServiceCount(), 2);
main_io_service->pollExternalIOServices();
EXPECT_EQ(one_io_callback_count, 2);
EXPECT_EQ(two_io_callback_count, 2);
main_io_service->clearExternalIOServices();
EXPECT_EQ(main_io_service->externalIOServiceCount(), 0);
IOServicePtr one_io_service(new IOService());
one_io_service->post(one_f);
IOServicePtr two_io_service(new IOService());
two_io_service->post(two_f);
main_io_service->pollExternalIOServices();
EXPECT_EQ(one_io_callback_count, 2);
EXPECT_EQ(two_io_callback_count, 2);
main_io_service->registerExternalIOService(one_io_service);
EXPECT_EQ(main_io_service->externalIOServiceCount(), 1);
main_io_service->pollExternalIOServices();
EXPECT_EQ(one_io_callback_count, 3);
EXPECT_EQ(two_io_callback_count, 2);
one_io_service->post(one_f);
two_io_service->post(two_f);
main_io_service->registerExternalIOService(two_io_service);
EXPECT_EQ(main_io_service->externalIOServiceCount(), 2);
main_io_service->pollExternalIOServices();
EXPECT_EQ(one_io_callback_count, 4);
EXPECT_EQ(two_io_callback_count, 4);
one_io_service->post(one_f);
two_io_service->post(two_f);
main_io_service->unregisterExternalIOService(one_io_service);
EXPECT_EQ(main_io_service->externalIOServiceCount(), 1);
main_io_service->pollExternalIOServices();
EXPECT_EQ(one_io_callback_count, 4);
EXPECT_EQ(two_io_callback_count, 5);
one_io_service->post(one_f);
two_io_service->post(two_f);
main_io_service->unregisterExternalIOService(two_io_service);
EXPECT_EQ(main_io_service->externalIOServiceCount(), 0);
main_io_service->pollExternalIOServices();
EXPECT_EQ(one_io_callback_count, 4);
EXPECT_EQ(two_io_callback_count, 5);
EXPECT_NO_THROW(main_io_service->registerExternalIOService(main_io_service));
EXPECT_EQ(main_io_service->externalIOServiceCount(), 1);
main_io_service->post(one_f);
main_io_service->pollExternalIOServices();
EXPECT_EQ(one_io_callback_count, 5);
EXPECT_EQ(two_io_callback_count, 5);
EXPECT_NO_THROW(main_io_service->unregisterExternalIOService(main_io_service));
EXPECT_EQ(main_io_service->externalIOServiceCount(), 0);
main_io_service->pollExternalIOServices();
EXPECT_EQ(one_io_callback_count, 5);
EXPECT_EQ(two_io_callback_count, 5);
}
}

View File

@ -579,7 +579,7 @@ TEST_F(NameChangeTransactionTest, responseString) {
EXPECT_EQ("IO_STOPPED", name_change_->responseString());
ASSERT_NO_THROW(name_change_->setDnsUpdateStatus(DNSClient::
INVALID_RESPONSE));
INVALID_RESPONSE));
EXPECT_EQ("INVALID_RESPONSE", name_change_->responseString());
ASSERT_NO_THROW(name_change_->setDnsUpdateStatus(DNSClient::OTHER));

View File

@ -540,7 +540,7 @@ checkAddFwdAddressRequest(NameChangeTransaction& tran) {
dns::RRsetPtr rrset;
checkRRCount(request, D2UpdateMessage::SECTION_PREREQUISITE, 1);
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_PREREQUISITE, 0));
SECTION_PREREQUISITE, 0));
checkRR(rrset, exp_fqdn, dns::RRClass::NONE(), dns::RRType::ANY(), 0, ncr);
// Verify the UPDATE SECTION
@ -552,12 +552,12 @@ checkAddFwdAddressRequest(NameChangeTransaction& tran) {
// First, Verify the FQDN/IP add RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 0));
SECTION_UPDATE, 0));
checkRR(rrset, exp_fqdn, dns::RRClass::IN(), exp_ip_rr_type, ttl, ncr);
// Now, verify the DHCID add RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 1));
SECTION_UPDATE, 1));
checkRR(rrset, exp_fqdn, dns::RRClass::IN(), dns::RRType::DHCID(),
ttl, ncr);
@ -595,12 +595,12 @@ checkReplaceFwdAddressRequest(NameChangeTransaction& tran) {
// Verify the FQDN test RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_PREREQUISITE, 0));
SECTION_PREREQUISITE, 0));
checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), dns::RRType::ANY(), 0, ncr);
// Verify the DHCID test RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_PREREQUISITE, 1));
SECTION_PREREQUISITE, 1));
checkRR(rrset, exp_fqdn, dns::RRClass::IN(), dns::RRType::DHCID(), 0, ncr);
// Verify the UPDATE SECTION
@ -613,12 +613,12 @@ checkReplaceFwdAddressRequest(NameChangeTransaction& tran) {
// Verify the FQDN delete RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 0));
SECTION_UPDATE, 0));
checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), exp_ip_rr_type, 0, ncr);
// Verify the FQDN/IP add RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 1));
SECTION_UPDATE, 1));
checkRR(rrset, exp_fqdn, dns::RRClass::IN(), exp_ip_rr_type, ttl, ncr);
// Verify there are no RRs in the ADDITIONAL Section.
@ -663,25 +663,25 @@ checkReplaceRevPtrsRequest(NameChangeTransaction& tran) {
// Verify the PTR delete RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 0));
SECTION_UPDATE, 0));
checkRR(rrset, exp_rev_addr, dns::RRClass::ANY(), dns::RRType::PTR(),
0, ncr);
// Verify the DHCID delete RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 1));
SECTION_UPDATE, 1));
checkRR(rrset, exp_rev_addr, dns::RRClass::ANY(), dns::RRType::DHCID(),
0, ncr);
// Verify the PTR add RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 2));
SECTION_UPDATE, 2));
checkRR(rrset, exp_rev_addr, dns::RRClass::IN(), dns::RRType::PTR(),
ttl, ncr);
// Verify the DHCID add RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 3));
SECTION_UPDATE, 3));
checkRR(rrset, exp_rev_addr, dns::RRClass::IN(), dns::RRType::DHCID(),
ttl, ncr);
@ -714,7 +714,7 @@ checkRemoveFwdAddressRequest(NameChangeTransaction& tran) {
// Verify the DHCID matching assertion RR.
dns::RRsetPtr rrset;
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_PREREQUISITE, 0));
SECTION_PREREQUISITE, 0));
checkRR(rrset, exp_fqdn, dns::RRClass::IN(), dns::RRType::DHCID(),
0, ncr);
@ -724,7 +724,7 @@ checkRemoveFwdAddressRequest(NameChangeTransaction& tran) {
// Verify the FQDN/IP delete RR.
const dns::RRType& exp_ip_rr_type = tran.getAddressRRType();
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 0));
SECTION_UPDATE, 0));
checkRR(rrset, exp_fqdn, dns::RRClass::NONE(), exp_ip_rr_type,
0, ncr);
@ -754,19 +754,19 @@ checkRemoveFwdRRsRequest(NameChangeTransaction& tran) {
// Verify the DHCID matches assertion.
dns::RRsetPtr rrset;
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_PREREQUISITE, 0));
SECTION_PREREQUISITE, 0));
checkRR(rrset, exp_fqdn, dns::RRClass::IN(), dns::RRType::DHCID(),
0, ncr);
// Verify the NO A RRs assertion.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_PREREQUISITE, 1));
SECTION_PREREQUISITE, 1));
checkRR(rrset, exp_fqdn, dns::RRClass::NONE(), dns::RRType::A(),
0, ncr, NO_RDATA);
// Verify the NO AAAA RRs assertion.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_PREREQUISITE, 2));
SECTION_PREREQUISITE, 2));
checkRR(rrset, exp_fqdn, dns::RRClass::NONE(), dns::RRType::AAAA(),
0, ncr, NO_RDATA);
@ -775,7 +775,7 @@ checkRemoveFwdRRsRequest(NameChangeTransaction& tran) {
// Verify the delete all for the FQDN RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 0));
SECTION_UPDATE, 0));
checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), dns::RRType::ANY(),
0, ncr);
@ -805,7 +805,7 @@ checkRemoveRevPtrsRequest(NameChangeTransaction& tran) {
// Verify the FQDN-PTRNAME assertion RR.
dns::RRsetPtr rrset;
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_PREREQUISITE, 0));
SECTION_PREREQUISITE, 0));
checkRR(rrset, exp_rev_addr, dns::RRClass::IN(), dns::RRType::PTR(),
0, ncr);
@ -814,7 +814,7 @@ checkRemoveRevPtrsRequest(NameChangeTransaction& tran) {
// Verify the delete all for the FQDN RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 0));
SECTION_UPDATE, 0));
checkRR(rrset, exp_rev_addr, dns::RRClass::ANY(), dns::RRType::ANY(),
0, ncr);
@ -876,23 +876,23 @@ checkSimpleReplaceFwdAddressRequest(NameChangeTransaction& tran) {
// Verify the FQDN delete RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 0));
SECTION_UPDATE, 0));
checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), exp_ip_rr_type, 0, ncr);
// Verify the DHCID delete RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 1));
SECTION_UPDATE, 1));
checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), dns::RRType::DHCID(),
0, ncr);
// Verify the FQDN/IP add RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 2));
SECTION_UPDATE, 2));
checkRR(rrset, exp_fqdn, dns::RRClass::IN(), exp_ip_rr_type, ttl, ncr);
// Now, verify the DHCID add RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 3));
SECTION_UPDATE, 3));
checkRR(rrset, exp_fqdn, dns::RRClass::IN(), dns::RRType::DHCID(),
ttl, ncr);
@ -929,12 +929,12 @@ checkSimpleRemoveFwdRRsRequest(NameChangeTransaction& tran) {
// Verify the FQDN delete RR.
dns::RRsetPtr rrset;
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 0));
SECTION_UPDATE, 0));
checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), exp_ip_rr_type, 0, ncr);
// Verify the DHCID delete RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 1));
SECTION_UPDATE, 1));
checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), dns::RRType::DHCID(),
0, ncr);
@ -970,12 +970,12 @@ checkSimpleRemoveRevPtrsRequest(NameChangeTransaction& tran) {
// Verify the FQDN delete RR.
dns::RRsetPtr rrset;
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 0));
SECTION_UPDATE, 0));
checkRR(rrset, exp_rev_addr, dns::RRClass::ANY(), dns::RRType::PTR(), 0, ncr);
// Verify the DHCID delete RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 1));
SECTION_UPDATE, 1));
checkRR(rrset, exp_rev_addr, dns::RRClass::ANY(), dns::RRType::DHCID(), 0, ncr);
// Verify that it will render toWire without throwing.
@ -1021,7 +1021,7 @@ checkExistsReplaceFwdAddressRequest(NameChangeTransaction& tran) {
// Verify the DHCID test RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_PREREQUISITE, 0));
SECTION_PREREQUISITE, 0));
checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), dns::RRType::DHCID(), 0, ncr);
// Verify the UPDATE SECTION
@ -1037,22 +1037,22 @@ checkExistsReplaceFwdAddressRequest(NameChangeTransaction& tran) {
// Verify the FQDN delete RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 0));
SECTION_UPDATE, 0));
checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), exp_ip_rr_type, 0, ncr);
// Verify the DHCID delete RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 1));
SECTION_UPDATE, 1));
checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), dns::RRType::DHCID(), 0, ncr);
// Verify the FQDN/IP add RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 2));
SECTION_UPDATE, 2));
checkRR(rrset, exp_fqdn, dns::RRClass::IN(), exp_ip_rr_type, ttl, ncr);
// Verify the DHCID add RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 3));
SECTION_UPDATE, 3));
checkRR(rrset, exp_fqdn, dns::RRClass::IN(), dns::RRType::DHCID(),
ttl, ncr);
@ -1088,7 +1088,7 @@ checkExistsRemoveFwdAddressRequest(NameChangeTransaction& tran) {
// Verify the DHCID exists assertion RR.
dns::RRsetPtr rrset;
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_PREREQUISITE, 0));
SECTION_PREREQUISITE, 0));
checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), dns::RRType::DHCID(),
0, ncr);
@ -1098,7 +1098,7 @@ checkExistsRemoveFwdAddressRequest(NameChangeTransaction& tran) {
// Verify the FQDN/IP delete RR.
const dns::RRType& exp_ip_rr_type = tran.getAddressRRType();
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 0));
SECTION_UPDATE, 0));
checkRR(rrset, exp_fqdn, dns::RRClass::NONE(), exp_ip_rr_type,
0, ncr);
@ -1131,13 +1131,13 @@ checkExistsRemoveFwdRRsRequest(NameChangeTransaction& tran) {
// Verify the NO A RRs assertion.
dns::RRsetPtr rrset;
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_PREREQUISITE, 0));
SECTION_PREREQUISITE, 0));
checkRR(rrset, exp_fqdn, dns::RRClass::NONE(), dns::RRType::A(),
0, ncr, NO_RDATA);
// Verify the NO AAAA RRs assertion.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_PREREQUISITE, 1));
SECTION_PREREQUISITE, 1));
checkRR(rrset, exp_fqdn, dns::RRClass::NONE(), dns::RRType::AAAA(),
0, ncr, NO_RDATA);
@ -1146,7 +1146,7 @@ checkExistsRemoveFwdRRsRequest(NameChangeTransaction& tran) {
// Verify the DHCID delete RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 0));
SECTION_UPDATE, 0));
checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), dns::RRType::DHCID(),
0, ncr);
@ -1190,12 +1190,12 @@ checkSimpleReplaceFwdAddressWithoutDHCIDRequest(NameChangeTransaction& tran) {
// Verify the FQDN delete RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 0));
SECTION_UPDATE, 0));
checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), exp_ip_rr_type, 0, ncr);
// Verify the FQDN/IP add RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 1));
SECTION_UPDATE, 1));
checkRR(rrset, exp_fqdn, dns::RRClass::IN(), exp_ip_rr_type, ttl, ncr);
// Verify there are no RRs in the ADDITIONAL Section.
@ -1234,7 +1234,7 @@ checkSimpleRemoveFwdRRsWithoutDHCIDRequest(NameChangeTransaction& tran) {
// Verify the FQDN delete RR.
dns::RRsetPtr rrset;
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 0));
SECTION_UPDATE, 0));
checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), exp_ip_rr_type, 0, ncr);
// Verify that it will render toWire without throwing.
@ -1275,13 +1275,13 @@ checkSimpleReplaceRevPtrsWithoutDHCIDRequest(NameChangeTransaction& tran) {
// Verify the PTR delete RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 0));
SECTION_UPDATE, 0));
checkRR(rrset, exp_rev_addr, dns::RRClass::ANY(), dns::RRType::PTR(),
0, ncr);
// Verify the PTR add RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 1));
SECTION_UPDATE, 1));
checkRR(rrset, exp_rev_addr, dns::RRClass::IN(), dns::RRType::PTR(),
ttl, ncr);
@ -1320,7 +1320,7 @@ checkSimpleRemoveRevPtrsWithoutDHCIDRequest(NameChangeTransaction& tran) {
// Verify the FQDN delete RR.
dns::RRsetPtr rrset;
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 0));
SECTION_UPDATE, 0));
checkRR(rrset, exp_rev_addr, dns::RRClass::ANY(), dns::RRType::PTR(), 0, ncr);
// Verify that it will render toWire without throwing.

View File

@ -16,6 +16,7 @@ libdhcpsrvtest_la_SOURCES = concrete_lease_mgr.cc concrete_lease_mgr.h
libdhcpsrvtest_la_SOURCES += config_result_check.cc config_result_check.h
libdhcpsrvtest_la_SOURCES += dhcp4o6_test_ipc.cc dhcp4o6_test_ipc.h
libdhcpsrvtest_la_SOURCES += host_data_source_utils.cc host_data_source_utils.h
libdhcpsrvtest_la_SOURCES += lib_load_test_fixture.h
libdhcpsrvtest_la_SOURCES += memory_host_data_source.cc memory_host_data_source.h
libdhcpsrvtest_la_SOURCES += test_utils.cc test_utils.h
libdhcpsrvtest_la_SOURCES += generic_backend_unittest.cc generic_backend_unittest.h

View File

@ -9,7 +9,9 @@
#include <cc/data.h>
#include <dhcpsrv/cfgmgr.h>
#include <hooks/hooks_manager.h>
#include <process/daemon.h>
#include <testutils/gtest_utils.h>
namespace isc {

View File

@ -1620,7 +1620,7 @@ cb4_updated and cb6_updated have no thread safety requirements.
Other hook library entry points are called by the main thread:
- io service (io context is recent boost versions) is polled by the main
thread
thread
- external socket callbacks are executed by the main thread
- commands including command_process

View File

@ -415,6 +415,8 @@ DControllerBase::configFromFile() {
// case of problems.
storage->applyLoggingCfg();
getIOService()->clearExternalIOServices();
answer = updateConfig(module_config);
// In all cases the right logging configuration is in the context.
process_->getCfgMgr()->getContext()->applyLoggingCfg();
@ -655,6 +657,8 @@ DControllerBase::configSetHandler(const std::string&, ConstElementPtr args) {
// case of problems.
storage->applyLoggingCfg();
getIOService()->clearExternalIOServices();
ConstElementPtr answer = updateConfig(module_config);
int rcode = 0;
parseAnswer(rcode, answer);
@ -844,6 +848,8 @@ DControllerBase::~DControllerBase() {
LOG_ERROR(dctl_logger, DCTL_UNLOAD_LIBRARIES_ERROR).arg(msg);
}
getIOService()->clearExternalIOServices();
io_signal_set_.reset();
try {
getIOService()->poll();

View File

@ -16,7 +16,6 @@ libkea_testutils_la_SOURCES += unix_control_client.cc unix_control_client.h
libkea_testutils_la_SOURCES += user_context_utils.cc user_context_utils.h
libkea_testutils_la_SOURCES += gtest_utils.h
libkea_testutils_la_SOURCES += multi_threading_utils.h
libkea_testutils_la_SOURCES += lib_load_test_fixture.h
libkea_testutils_la_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
libkea_testutils_la_LIBADD = $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
libkea_testutils_la_LIBADD += $(top_builddir)/src/lib/dns/libkea-dns++.la

319
tools/check-lib-dependencies.sh Executable file
View File

@ -0,0 +1,319 @@
#!/bin/bash
# extract folder name containing file
#
# param ${1} file name
# return folder name
extract_folder_name() {
# return name of the file until last '/'
echo "$(echo "${1}" | rev | cut -d '/' -f 2- | rev)"
}
# extract all includes found in source files found in the same folder as specified Makefile.am
#
# param ${1} path to a Makefile.am
# return all dependencies libs in the order of compilation
extract_includes() {
# extract folder name from current library Makefile.am
CURRENT_FOLDER=$(extract_folder_name "${1}")"/"
# select only files in current folder
SEARCH_FILES=$(echo "${FILE_LIST}" | grep "${CURRENT_FOLDER}")
# select all lines containing '#include ' directive
RAW_INCLUDES_LIST=$(echo "${SEARCH_FILES}" | xargs grep "^#include " 2>/dev/null)
# filter only included dependencies found in other libraries by using the form 'other_lib_name/header_file.h'
# to do this it is required to select the string between '<' and '>', searching for '/' character and returning the name until last '/'
RAW_INCLUDES_LIST=$(echo "${RAW_INCLUDES_LIST}" | cut -d "#" -f 2 | tr "\"" " " | cut -d "<" -f 2 | cut -d ">" -f 1 | grep "\/" | rev | cut -d "/" -f 2 | rev | sort | uniq)
# filter includes that are not compiled by the project's Makefiles
INCLUDES_LIST=
for i in ${LIBRARIES_LIST}; do
for j in ${RAW_INCLUDES_LIST}; do
if test "${j}" = "${i}"; then
INCLUDES_LIST="${i} ${INCLUDES_LIST}"
break
fi
done
done
# remove empty spaces
INCLUDES_LIST=$(echo ${INCLUDES_LIST} | tr -s " ")
# order dependencies in the order of compilation
FILTERED_INCLUDES_LIST=
for i in ${LIBRARIES_LIST}; do
if test $(echo "${INCLUDES_LIST}" | grep "\b${i}\b" | wc -l) -ne 0; then
FILTERED_INCLUDES_LIST="${i} ${FILTERED_INCLUDES_LIST}"
fi
done
echo "${FILTERED_INCLUDES_LIST}"
}
# extract all header only files and headers and source files found in the external library required by specified library
# param ${1} name of the current library
# param ${2} name of the external dependency library required by current library
# return the list of header only files as 'HEADERS: heaser1.h header2.h' and header and source files as 'HEADERS_AND_SOURCES: source1.h source1.cc source2.h source2.cpp'
extract_non_include_files() {
# extract folder name for current library Makefile.am
CURRENT_FOLDER=$(extract_folder_name "src/lib/${1}/Makefile.am")"/"
# extract folder name for external dependency library Makefile.am
EXTERNAL_FOLDER=$(extract_folder_name "src/lib/${2}/Makefile.am")"/"
# select only files in current folder
SEARCH_FILES=$(echo "${FILE_LIST}" | grep "${CURRENT_FOLDER}")
HEADERS_LIST=
NON_HEADERS_LIST=
# select all lines containing '#include ' directive
RAW_INCLUDES_LIST=$(echo "${SEARCH_FILES}" | xargs grep "^#include " 2>/dev/null)
# filter only included headers found in other libraries by using the form 'other_lib_name/header_file.h'
# to do this it is required to select the string between '<' and '>', searching for '/' character, search for the extension marker '.' and returning the name after last '/'
RAW_INCLUDES_LIST=$(echo "${RAW_INCLUDES_LIST}" | cut -d "#" -f 2 | tr "\"" " " | cut -d "<" -f 2 | cut -d ">" -f 1 | grep "\/" | grep "\b${2}\b" | cut -d "/" -f 2 | grep "\." | sort | uniq)
# select only files in dependency library folder and strip full path
RELATIVE_SEARCH_FILES=$(echo "${FILE_LIST}" | grep "${EXTERNAL_FOLDER}" | sed -e "s#${REPO_FOLDER}${EXTERNAL_FOLDER}##g")
# search for the header file but also for source files
for i in ${RAW_INCLUDES_LIST}; do
# filter by name only (no extension)
FILTER=$(echo "${i}" | cut -d "." -f 1)
# filter non header files with exact name of the header file without the extension
NON_HEADER=$(echo "${RELATIVE_SEARCH_FILES}" | grep "\b${FILTER}\." | grep -v "${i}")
if test $(echo "${NON_HEADER}" | wc -w) -ne 0; then
# append header and source file names
NON_HEADERS_LIST="${i} ${NON_HEADER} ${NON_HEADERS_LIST}"
else
# append header only file name
HEADERS_LIST="${i} ${HEADERS_LIST}"
fi
done
# sort header only files
HEADERS_LIST=$(echo ${HEADERS_LIST} | tr -s " " | sort | uniq)
# sort header and source files
NON_HEADERS_LIST=$(echo ${NON_HEADERS_LIST} | tr -s " " | sort | uniq)
echo "HEADERS_AND_SOURCES:${NON_HEADERS_LIST}"
echo "HEADERS:${HEADERS_LIST}"
}
# extract all valid dependencies of a specified library
#
# param ${1} list of all libraries in the reverse compilation order
# param ${2} library name for which the dependency list is computed
# return the list of dependencies for specified library in the reverse compilation order
extract_dependencies() {
echo "${1}" | grep -Eo "\b${2}\b.*$"
}
# extract computed dependency for specified library
#
# param ${1} library name for which the dependency list is retrieved
# param ${2} library path for which the dependency list is retrieved
# return stored value of computed dependencies or 'NONE' if dependencies have not been computed yet
extract_computed_dependencies() {
PATH_TO_NAME=$(echo "${2}" | tr -s "/" "_")
NAME="COMPUTED_DEPENDENCIES_${PATH_TO_NAME}_${1}"
if test -n "${!NAME+x}"; then
echo "${!NAME}"
else
echo "NONE"
fi
}
# extract library directive
#
# param ${1} artifact path
extract_library_directive() {
ARTIFACT_PATH="${1}"
echo `cat ${ARTIFACT_PATH}/Makefile.am | grep "LIBADD\|LDADD" | sort | tr -s ' ' | cut -d " " -f 1 | sort -u`
}
# extract library name
#
# param ${1} artifact path
extract_library_name() {
ARTIFACT_PATH="${1}"
echo `cat ${ARTIFACT_PATH}/Makefile.am | grep "LIBRARIES" | tr -s ' ' | cut -d " " -f 3`
}
# compute artifact dependencies
#
# param ${1} artifact name
# param ${2} artifact path
compute_dependencies() {
ARTIFACT="${1}"
ARTIFACT_PATH="${2}"
echo ""
echo "########################################"
echo "### ${ARTIFACT_PATH}/${ARTIFACT}"
echo "########################################"
echo ""
# all valid dependencies that can be added by each dependency library
echo "${ARTIFACT_PATH}/${ARTIFACT} valid dependencies:"
echo "${VALID_LIST}"
# detect dependencies errors by searching for dependencies that are compiled after the current library and can generate missing symbols
NON_RECURSIVE_BASE_DEPENDENCIES=
for j in ${BASE_DEPENDENCIES}; do
# only add the dependency if it is in the valid dependencies list to prevent infinite recursion and log the error otherwise
if test $(echo "${VALID_LIST}" | grep "\b${j}\b" | wc -l) -eq 0; then
# search for external header and source files
INVALID_EXTERNAL_DEPENDENCIES=$(extract_non_include_files "${ARTIFACT}" "${j}")
# filter header only external files
EXTERNAL_HEADERS=$(echo "${INVALID_EXTERNAL_DEPENDENCIES}" | grep "HEADERS:" | cut -d ":" -f 2)
# filter header and source external files
EXTERNAL_ALL=$(echo "${INVALID_EXTERNAL_DEPENDENCIES}" | grep "HEADERS_AND_SOURCES:" | cut -d ":" -f 2)
echo "### ERROR ### dependencies ERROR for ${ARTIFACT_PATH}/${ARTIFACT} on ${j} with:"
# if there are any header only external files
if test $(echo "${EXTERNAL_ALL}" | wc -w) -ne 0; then
echo "non header only files: ${EXTERNAL_ALL}"
fi
# if there are any header and source external files
if test $(echo "${EXTERNAL_HEADERS}" | wc -w) -ne 0; then
echo "header only files: ${EXTERNAL_HEADERS}"
fi
else
# don't add current library to it's dependencies list
if test ${j} != ${ARTIFACT}; then
NON_RECURSIVE_BASE_DEPENDENCIES="${NON_RECURSIVE_BASE_DEPENDENCIES} ${j}"
fi
fi
done
# all found dependencies in the reverse compilation order
BASE_DEPENDENCIES=$(echo "${BASE_DEPENDENCIES}" | xargs)
# all found and valid dependencies in the reverse compilation order
NON_RECURSIVE_BASE_DEPENDENCIES=$(echo "${NON_RECURSIVE_BASE_DEPENDENCIES}" | xargs)
echo "${ARTIFACT_PATH}/${ARTIFACT} base dependencies:"
echo "${BASE_DEPENDENCIES}"
echo "${ARTIFACT_PATH}/${ARTIFACT} non recursive dependencies:"
echo "${NON_RECURSIVE_BASE_DEPENDENCIES}"
# minimum set of dependencies for current library
DEPENDENCIES=
for j in ${NON_RECURSIVE_BASE_DEPENDENCIES}; do
NEW_DEPENDENCIES=$(extract_computed_dependencies "${j}" "src/lib")
if test "${NEW_DEPENDENCIES}" == "NONE"; then
echo "### ERROR ### computed dependency not found for ${j}"
else
DEPENDENCIES="${NEW_DEPENDENCIES} ${DEPENDENCIES}"
fi
done
DEPENDENCIES=$(echo "${DEPENDENCIES} ${NON_RECURSIVE_BASE_DEPENDENCIES}" | tr -s " " "\n" | sort | uniq | xargs)
# order dependencies in the order of compilation
SORTED_DEPENDENCIES=
for j in ${LIBRARIES_LIST}; do
if test $(echo "${DEPENDENCIES}" | grep "\b${j}\b" | wc -l) -ne 0; then
SORTED_DEPENDENCIES="${j} ${SORTED_DEPENDENCIES}"
fi
done
echo "${ARTIFACT_PATH}/${ARTIFACT} minimum dependencies:"
echo "${SORTED_DEPENDENCIES}"
echo ""
echo "++++++++++++++++++++++++++++++++++++++++"
ARTIFACT_DIRECTIVE=$(extract_library_directive ${ARTIFACT_PATH}/${ARTIFACT})
for j in ${SORTED_DEPENDENCIES}; do
DEPENDENCY_LIBRARY_NAME=$(extract_library_name "src/lib/${j}")
echo "${ARTIFACT_DIRECTIVE} += \$(top_builddir)/src/lib/${j}/${DEPENDENCY_LIBRARY_NAME}"
done
echo "++++++++++++++++++++++++++++++++++++++++"
echo "########################################"
echo ""
}
# if wrong number of parameters print usage
if test ${#} -ne 1; then
echo "Usage: ${0} path/to/kea/repo"
exit
fi
# folder containing full repo
REPO_FOLDER=${1}
if test $(echo -n ${REPO_FOLDER} | tail -c 1) != "/"; then
REPO_FOLDER="${REPO_FOLDER}/"
fi
# filter all Makefile.am files
MAKEFILES_LIST=$(find ${REPO_FOLDER} | grep "Makefile\.am" | sed -e "s#${REPO_FOLDER}##g" | grep "src\/" | sort)
# if no Makefile.am found exit
if test -z "${MAKEFILES_LIST}"; then
echo "invalid repo path: no Makefile.am file found"
exit
fi
echo "list of Makefile.am:"
echo "${MAKEFILES_LIST}"
# base Makefile.am for all sources is in src/lib/Makefile.am
BASE_MAKEFILE=$(echo "${MAKEFILES_LIST}" | grep "src\/lib\/Makefile.am")
# if no src/lib/Makefile.am found exit
if test -z ${BASE_MAKEFILE}; then
echo "invalid repo path: no src/lib/Makefile.am file found"
exit
fi
echo "base Makefile.am:"
echo "${BASE_MAKEFILE}"
# generate the list of libraries in the compilation order
LIBRARIES_LIST=
RAW_LIBRARIES_LIST=$(cat "${REPO_FOLDER}${BASE_MAKEFILE}" | grep "SUBDIRS")
for i in ${RAW_LIBRARIES_LIST}; do
LIBRARIES_LIST="${LIBRARIES_LIST} $(echo ${i} | grep -v "SUBDIRS" | grep -v '=')"
done
# remove empty spaces
LIBRARIES_LIST=$(echo "${LIBRARIES_LIST}" | tr -s ' ' | xargs)
# generate the list of libraries in the reverse compilation order
REVERSE_LIBRARIES_LIST=
for i in ${LIBRARIES_LIST}; do
REVERSE_LIBRARIES_LIST="${i} ${REVERSE_LIBRARIES_LIST}"
done
echo "list of libraries:"
echo "${LIBRARIES_LIST}"
echo "reverse list of libraries:"
echo "${REVERSE_LIBRARIES_LIST}"
# filter all files of interest ignoring irrelevant ones
# ignore .git, .libs, .deps doc folders and .o .lo .Plo .Po .gcno .gcda .m4 .dox .json .mes files
FILE_LIST=$(find "${REPO_FOLDER}" 2>/dev/null | grep -v "\.git" | grep -v "\/\.libs\/" | grep -v "\.o$" | grep -v "\/\.deps\/" | grep -v "\.lo$" | grep -v "\.Plo$" | grep -v "\.Po$" | grep -v "\.gcno$" | grep -v "gcda" | grep -v "\.m4$" | grep -v "\.dox$" | grep -v "\.json$" | grep -v "\/doc\/" | grep -v "\.mes$" | sort)
#echo "files:"
#echo "${FILE_LIST}"
BASE_LIBRARIES_MAKEFILES=
# generate the list of dependencies for all libraries in src/lib
for i in ${LIBRARIES_LIST}; do
# generate current library Makefile.am path
BASE_LIBRARIES_MAKEFILES="${BASE_LIBRARIES_MAKEFILES} src/lib/${i}/Makefile.am"
# extract dependencies found in the library folder
BASE_DEPENDENCIES=$(extract_includes "src/lib/${i}/Makefile.am")
# generate the list of valid dependencies for the current library (take compilation order into account)
VALID_LIST=$(extract_dependencies "${REVERSE_LIBRARIES_LIST}" "${i}")
compute_dependencies "${i}" "src/lib"
PATH_TO_NAME=$(echo "src/lib" | tr -s "/" "_")
declare COMPUTED_DEPENDENCIES_${PATH_TO_NAME}_${i}="${SORTED_DEPENDENCIES}"
done
# remove empty spaces
BASE_LIBRARIES_MAKEFILES=$(echo "${BASE_LIBRARIES_MAKEFILES}" | xargs | tr -s " " "\n")
echo "base Makefiles.am files:"
echo "${BASE_LIBRARIES_MAKEFILES}"
OTHER_MAKEFILES=$(echo "${MAKEFILES_LIST}" | tr -s " " "\n" | grep -v "src/lib/" | grep -v "src/share/" | grep -v "src/Makefile.am")
# remove empty spaces
OTHER_MAKEFILES=$(echo "${OTHER_MAKEFILES}" | xargs | tr -s " " "\n")
echo "remaining Makefile.am files:"
echo "${OTHER_MAKEFILES}"
for i in ${OTHER_MAKEFILES}; do
# extract dependencies found in the artifact folder
BASE_DEPENDENCIES=$(extract_includes "${i}")
# generate the list of valid dependencies for the current artifact (take compilation order into account)
VALID_LIST="${REVERSE_LIBRARIES_LIST}"
ARTIFACT=$(echo "${i}" | rev | cut -d "/" -f 2 | rev)
ARTIFACT_PATH=$(echo "${i}" | rev | cut -d "/" -f 3- | rev)
compute_dependencies "${ARTIFACT}" "${ARTIFACT_PATH}"
PATH_TO_NAME=$(echo "${ARTIFACT_PATH}" | tr -s "/" "_")
declare COMPUTED_DEPENDENCIES_${PATH_TO_NAME}_${ARTIFACT}="${SORTED_DEPENDENCIES}"
done
exit