Commit ee02fde1 authored by Thomas Markwalder's avatar Thomas Markwalder

[#566,!304] kea-dhcp6 now removes objects deleted from config backend

src/lib/dhcpsrv/cb_ctl_dhcp6.cc
    CBControlDHCPv6::databaseConfigApply() - revamped to
    delete objects based on DELETE audit entries

src/lib/dhcpsrv/unittests
    Renamed cb_ctl_dhcp_unittest.cc to cb_ctl_dhcp4_unittest.cc
    cb_ctl_dhcp6_unittest.cc - new file

Added proposed ChangeLog
parent 8c3acffe
1560. [func] tmark
kea-dhcp6 now automatically deletes configuration elements
that have been deleted from configuration backends.
(Gitlab #566,!304, git TBD)
1559. [func] fdupont
Added DHCPv6 support to the MySQL Config Backend hook.
(Gitlab #397,!244, git 980091ecd717e41a61f0d7f6808213e450647d8e)
......
......@@ -9,6 +9,7 @@
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/dhcpsrv_log.h>
using namespace isc::db;
using namespace isc::data;
using namespace isc::process;
......@@ -20,11 +21,99 @@ CBControlDHCPv6::databaseConfigApply(const db::BackendSelector& backend_selector
const db::ServerSelector& server_selector,
const boost::posix_time::ptime& lb_modification_time,
const db::AuditEntryCollection& audit_entries) {
bool globals_fetched = false;
// Let's first delete all the configuration elements for which DELETE audit
// entries are found. Although, this may break chronology of the audit in
// some cases it should not affect the end result of the data fetch. If the
// object was created and then subsequently deleted, we will first try to
// delete this object from the local configuration (which will fail because
// the object does not exist) and then we will try to fetch it from the
// database which will return no result.
if (!audit_entries.empty()) {
auto cfg = CfgMgr::instance().getCurrentCfg();
auto external_cfg = CfgMgr::instance().createExternalCfg();
// Get audit entries for deleted global parameters.
const auto& index = audit_entries.get<AuditEntryObjectTypeTag>();
auto range = index.equal_range(boost::make_tuple("dhcp6_global_parameter",
AuditEntry::ModificationType::DELETE));
if (range.first != range.second) {
// Some globals have been deleted. Since we currently don't track database
// identifiers of the global parameters we have to fetch all global
// parameters for this server. Next, we simply replace existing
// global parameters with the new parameters. This is slightly
// inefficient but only slightly. Note that this is a single
// database query and the number of global parameters is small.
data::StampedValueCollection globals;
globals = getMgr().getPool()->getAllGlobalParameters6(backend_selector, server_selector);
addGlobalsToConfig(external_cfg, globals);
// Now that we successfully fetched the new global parameters, let's
// remove existing ones and merge them into the current configuration.
cfg->clearConfiguredGlobals();
CfgMgr::instance().mergeIntoCurrentCfg(external_cfg->getSequence());
globals_fetched = true;
}
try {
// Get audit entries for deleted option definitions and delete each
// option definition from the current configuration for which the
// audit entry is found.
range = index.equal_range(boost::make_tuple("dhcp6_option_def",
AuditEntry::ModificationType::DELETE));
for (auto entry = range.first; entry != range.second; ++entry) {
cfg->getCfgOptionDef()->del((*entry)->getObjectId());
}
// Repeat the same for other configuration elements.
range = index.equal_range(boost::make_tuple("dhcp6_options",
AuditEntry::ModificationType::DELETE));
for (auto entry = range.first; entry != range.second; ++entry) {
cfg->getCfgOption()->del((*entry)->getObjectId());
}
range = index.equal_range(boost::make_tuple("dhcp6_shared_network",
AuditEntry::ModificationType::DELETE));
for (auto entry = range.first; entry != range.second; ++entry) {
cfg->getCfgSharedNetworks6()->del((*entry)->getObjectId());
}
range = index.equal_range(boost::make_tuple("dhcp6_subnet",
AuditEntry::ModificationType::DELETE));
for (auto entry = range.first; entry != range.second; ++entry) {
// If the deleted subnet belongs to a shared network and the
// shared network is not being removed, we need to detach the
// subnet from the shared network.
auto subnet = cfg->getCfgSubnets6()->getBySubnetId((*entry)->getObjectId());
if (subnet) {
// Check if the subnet belongs to a shared network.
SharedNetwork6Ptr network;
subnet->getSharedNetwork(network);
if (network) {
// Detach the subnet from the shared network.
network->del(subnet->getID());
}
// Actually delete the subnet from the configuration.
cfg->getCfgSubnets6()->del((*entry)->getObjectId());
}
}
} catch (...) {
// Ignore errors thrown when attempting to delete a non-existing
// configuration entry. There is no guarantee that the deleted
// entry is actually there as we're not processing the audit
// chronologically.
}
}
// Create the external config into which we'll fetch backend config data.
SrvConfigPtr external_cfg = CfgMgr::instance().createExternalCfg();
// First let's fetch the globals and add them to external config.
if (fetchConfigElement(audit_entries, "dhcp6_global_parameter")) {
if (!globals_fetched && fetchConfigElement(audit_entries, "dhcp6_global_parameter")) {
data::StampedValueCollection globals;
globals = getMgr().getPool()->getModifiedGlobalParameters6(backend_selector, server_selector,
lb_modification_time);
......@@ -57,6 +146,12 @@ CBControlDHCPv6::databaseConfigApply(const db::BackendSelector& backend_selector
getMgr().getPool()->getModifiedSharedNetworks6(backend_selector, server_selector,
lb_modification_time);
for (auto network = networks.begin(); network != networks.end(); ++network) {
// In order to take advantage of the dynamic inheritance of global
// parameters to a shared network we need to set a callback function
// for each network to allow for fetching global parameters.
(*network)->setFetchGlobalsFn([] () -> ConstElementPtr {
return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals());
});
external_cfg->getCfgSharedNetworks6()->add((*network));
}
}
......@@ -67,6 +162,12 @@ CBControlDHCPv6::databaseConfigApply(const db::BackendSelector& backend_selector
server_selector,
lb_modification_time);
for (auto subnet = subnets.begin(); subnet != subnets.end(); ++subnet) {
// In order to take advantage of the dynamic inheritance of global
// parameters to a subnet we need to set a callback function for each
// subnet to allow for fetching global parameters.
(*subnet)->setFetchGlobalsFn([] () -> ConstElementPtr {
return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals());
});
external_cfg->getCfgSubnets6()->add((*subnet));
}
}
......
......@@ -63,7 +63,8 @@ libdhcpsrv_unittests_SOURCES += alloc_engine_hooks_unittest.cc
libdhcpsrv_unittests_SOURCES += alloc_engine4_unittest.cc
libdhcpsrv_unittests_SOURCES += alloc_engine6_unittest.cc
libdhcpsrv_unittests_SOURCES += callout_handle_store_unittest.cc
libdhcpsrv_unittests_SOURCES += cb_ctl_dhcp_unittest.cc
libdhcpsrv_unittests_SOURCES += cb_ctl_dhcp4_unittest.cc
libdhcpsrv_unittests_SOURCES += cb_ctl_dhcp6_unittest.cc
libdhcpsrv_unittests_SOURCES += cfg_db_access_unittest.cc
libdhcpsrv_unittests_SOURCES += cfg_duid_unittest.cc
libdhcpsrv_unittests_SOURCES += cfg_expiration_unittest.cc
......
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment