+++ /dev/null
-// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#include <config.h>
-
-#include <asiolink/io_address.h>
-#include <cc/stamped_value.h>
-#include <dhcp/option_string.h>
-#include <dhcpsrv/cb_ctl_dhcp6.h>
-#include <dhcpsrv/cfgmgr.h>
-#include <dhcpsrv/testutils/generic_backend_unittest.h>
-#include <dhcpsrv/testutils/test_config_backend_dhcp6.h>
-#include <boost/date_time/posix_time/posix_time.hpp>
-#include <gtest/gtest.h>
-#include <map>
-#include <string>
-
-using namespace isc::asiolink;
-using namespace isc::db;
-using namespace isc::data;
-using namespace isc::dhcp;
-using namespace isc::dhcp::test;
-using namespace isc::process;
-
-namespace {
-
-/// @brief Base class for testing derivations of the CBControlDHCP.
-class CBControlDHCPTest : public GenericBackendTest {
-public:
-
- /// @brief Constructor.
- CBControlDHCPTest()
- : timestamp_(), object_timestamp_(), audit_entries_() {
- CfgMgr::instance().clear();
- CfgMgr::instance().setFamily(AF_INET6);
- initTimestamps();
- }
-
- /// @brief Destructor.
- virtual ~CBControlDHCPTest() {
- // Unregister the factory to be tidy.
- ConfigBackendDHCPv6Mgr::instance().unregisterBackendFactory("memfile");
- CfgMgr::instance().clear();
- }
-
- /// @brief Creates new CREATE audit entry.
- ///
- /// The audit entry is added to the @c audit_entries_ collection.
- ///
- /// @param object_type Object type to be associated with the audit
- /// entry.
- void addCreateAuditEntry(const std::string& object_type) {
- AuditEntryPtr entry(new AuditEntry(object_type, 1234,
- AuditEntry::ModificationType::CREATE,
- "some log message"));
- audit_entries_.insert(entry);
- }
-
- /// @brief Creates new DELETE audit entry.
- ///
- /// The audit entry is added to the @c audit_entries_ collection.
- ///
- /// @param object_type Object type to be associated with the audit
- /// entry.
- /// @param object_id Identifier of the object to be associated with
- /// the audit entry.
- void addDeleteAuditEntry(const std::string& object_type,
- const uint64_t object_id) {
- AuditEntryPtr entry(new AuditEntry(object_type, object_id,
- AuditEntry::ModificationType::DELETE,
- "some log message"));
- audit_entries_.insert(entry);
- }
-
- /// @brief Initializes timestamps used in tests.
- void initTimestamps() {
- // Get the current timestamp and move it 30 seconds backwards.
- auto now = boost::posix_time::second_clock::local_time() -
- boost::posix_time::seconds(30);
-
- // Initialize multiple timestamps from the base timestamp. The
- // values with indexes [-5, 0] are in the past. The remaining
- // four are in the future.
- for (int i = -5; i < 5; ++i) {
- timestamp_[i] = now + boost::posix_time::minutes(i);
- }
- }
-
- /// @brief Returns timestamp associated with a given index.
- ///
- /// @param timestamp_index Index of the timestamp to be returned.
- boost::posix_time::ptime getTimestamp(const int timestamp_index) {
- return (timestamp_[timestamp_index]);
- }
-
- /// @brief Returns timestamp to be associated with a given object type.
- ///
- /// The object types correspond to the names of the SQL tables holding
- /// them, e.g. dhcp6_global_parameter, dhcp6_subnet etc.
- ///
- /// @param object_type Object type.
- boost::posix_time::ptime getTimestamp(const std::string& object_type) {
- return (object_timestamp_[object_type]);
- }
-
- /// @brief Associates object type with a timestamp.
- ///
- /// When adding objects to the database, each one is associated with
- /// a modification time value. This value is setup by unit tests
- /// via this method.
- void setTimestamp(const std::string& object_type, const int timestamp_index) {
- object_timestamp_[object_type] = timestamp_[timestamp_index];
- }
-
- /// @brief Sets timestamps for various object types to the same value.
- ///
- /// @param timestamp_index Index of the timestamp to be set.
- virtual void setAllTimestamps(const int timestamp_index) = 0;
-
- /// @brief Checks if @c databaseConfigApply should fetch updates for specified
- /// object types.
- ///
- /// @param object_type Object type.
- bool fetchConfigElement(const std::string& object_type) const {
- if (!audit_entries_.empty()) {
- const auto& index = audit_entries_.get<AuditEntryObjectTypeTag>();
- auto range = index.equal_range(object_type);
- for (auto it = range.first; it != range.second; ++it) {
- if (((*it)->getModificationType() != AuditEntry::ModificationType::DELETE)) {
- return (true);
- }
- }
- return (false);
- }
-
- return (true);
- }
-
- /// @brief Check if @c databaseConfigApply should delete a given object from the
- /// local configuration.
- ///
- /// @param object_type Object type.
- /// @param object_id Object identifier.
- bool deleteConfigElement(const std::string& object_type,
- const uint64_t object_id) const {
- if (!audit_entries_.empty()) {
- const auto& index = audit_entries_.get<AuditEntryObjectTypeTag>();
- auto range = index.equal_range(boost::make_tuple(object_type,
- AuditEntry::ModificationType::DELETE));
- for (auto it = range.first; it != range.second; ++it) {
- if ((*it)->getObjectId() == object_id) {
- return (true);
- }
- }
- }
- return (false);
- }
-
- /// @brief Holds test timestamps.
- std::map<int, boost::posix_time::ptime> timestamp_;
-
- /// @brief Holds mapping of the objects types to their timestamps.
- std::map<std::string, boost::posix_time::ptime> object_timestamp_;
-
- /// @brief Collection of audit entries used in the unit tests.
- AuditEntryCollection audit_entries_;
-};
-
-
-/// @brief Naked @c CBControlDHCPv6 class exposing protected methods.
-class TestCBControlDHCPv6 : public CBControlDHCPv6 {
-public:
- using CBControlDHCPv6::getInitialAuditEntryTime;
- using CBControlDHCPv6::databaseConfigApply;
-};
-
-/// @brief Test fixture class for @c CBControlDHCPv6 unit tests.
-class CBControlDHCPv6Test : public CBControlDHCPTest {
-public:
-
- /// @brief Constructor.
- CBControlDHCPv6Test()
- : CBControlDHCPTest(), ctl_() {
- ConfigBackendDHCPv6Mgr::instance().registerBackendFactory("memfile",
- [](const DatabaseConnection::ParameterMap& params)
- -> ConfigBackendDHCPv6Ptr {
- return (TestConfigBackendDHCPv6Ptr(new TestConfigBackendDHCPv6(params)));
- });
- ConfigBackendDHCPv6Mgr::instance().addBackend("type=memfile");
-
- // By default, set timestamps for all object types to -4. That leaves
- // us with the possibility to use index -5 (earlier) to use as lower
- // bound modification time so as all objects are fetched.
- setAllTimestamps(-4);
- }
-
- /// @brief Sets timestamps of all DHCPv6 specific object types.
- ///
- /// @param timestamp_index Index of the timestamp to be set.
- virtual void setAllTimestamps(const int timestamp_index) {
- setTimestamp("dhcp6_global_parameter", timestamp_index);
- setTimestamp("dhcp6_option_def", timestamp_index);
- setTimestamp("dhcp6_options", timestamp_index);
- setTimestamp("dhcp6_shared_network", timestamp_index);
- setTimestamp("dhcp6_subnet", timestamp_index);
- }
-
- /// @brief Creates test server configuration and stores it in a test
- /// configuration backend.
- ///
- /// There are pairs of configuration elements stored in the database.
- /// For example: two global parameters, two option definitions etc.
- /// Having two elements of each type in the database is useful in tests
- /// which verify that an element is deleted from the local configuration
- /// as a result of being deleted from the configuration backend. In that
- /// case the test verifies that one of the elements of the given type
- /// is deleted and one is left.
- void remoteStoreTestConfiguration() {
- auto& mgr = ConfigBackendDHCPv6Mgr::instance();
-
- // Insert global parameters into a database.
- StampedValuePtr global_parameter = StampedValue::create("foo", "bar");
- global_parameter->setModificationTime(getTimestamp("dhcp6_global_parameter"));
- ASSERT_NO_THROW(mgr.getPool()->createUpdateGlobalParameter6(BackendSelector::UNSPEC(),
- ServerSelector::ALL(),
- global_parameter));
-
- global_parameter = StampedValue::create("bar", "teta");
- global_parameter->setModificationTime(getTimestamp("dhcp6_global_parameter"));
- ASSERT_NO_THROW(mgr.getPool()->createUpdateGlobalParameter6(BackendSelector::UNSPEC(),
- ServerSelector::ALL(),
- global_parameter));
-
- // Insert option definitions into the database.
- OptionDefinitionPtr def(new OptionDefinition("one", 101, "uint16"));
- def->setId(1);
- def->setOptionSpaceName("isc");
- def->setModificationTime(getTimestamp("dhcp6_option_def"));
- ASSERT_NO_THROW(mgr.getPool()->createUpdateOptionDef6(BackendSelector::UNSPEC(),
- ServerSelector::ALL(),
- def));
- def.reset(new OptionDefinition("two", 102, "uint16"));
- def->setId(2);
- def->setOptionSpaceName("isc");
- def->setModificationTime(getTimestamp("dhcp6_option_def"));
- ASSERT_NO_THROW(mgr.getPool()->createUpdateOptionDef6(BackendSelector::UNSPEC(),
- ServerSelector::ALL(),
- def));
-
- // Insert global options into the database.
- OptionDescriptorPtr opt(new OptionDescriptor(createOption<OptionString>
- (Option::V6, D6O_BOOTFILE_URL,
- true, false, "some.bootfile")));
- opt->setId(1);
- opt->space_name_ = DHCP6_OPTION_SPACE;
- opt->setModificationTime(getTimestamp("dhcp6_options"));
- mgr.getPool()->createUpdateOption6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
- opt);
-
- opt.reset(new OptionDescriptor(createOption<OptionString>
- (Option::V6, D6O_AFTR_NAME,
- true, true, "some.example.com")));
- opt->setId(2);
- opt->space_name_ = DHCP6_OPTION_SPACE;
- opt->setModificationTime(getTimestamp("dhcp6_options"));
- mgr.getPool()->createUpdateOption6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
- opt);
-
- // Insert shared networks into the database.
- SharedNetwork6Ptr network(new SharedNetwork6("one"));
- network->setId(1);
- network->setModificationTime(getTimestamp("dhcp6_shared_network"));
- mgr.getPool()->createUpdateSharedNetwork6(BackendSelector::UNSPEC(),
- ServerSelector::ALL(),
- network);
-
- network.reset(new SharedNetwork6("two"));
- network->setId(2);
- network->setModificationTime(getTimestamp("dhcp6_shared_network"));
- mgr.getPool()->createUpdateSharedNetwork6(BackendSelector::UNSPEC(),
- ServerSelector::ALL(),
- network);
-
- // Insert subnets into the database.
- Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 64, 1, 2, 3, 4, SubnetID(1)));
- subnet->setModificationTime(getTimestamp("dhcp6_subnet"));
- subnet->setSharedNetworkName("one");
- mgr.getPool()->createUpdateSubnet6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
- subnet);
-
- subnet.reset(new Subnet6(IOAddress("2001:db8:2::"), 64, 1, 2, 3, 4, SubnetID(2)));
- subnet->setModificationTime(getTimestamp("dhcp6_subnet"));
- mgr.getPool()->createUpdateSubnet6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
- subnet);
- }
-
- /// @brief Deletes specified global parameter from the configuration
- /// backend and generates audit entry.
- ///
- /// Note that the current Kea implementation does not track database
- /// identifiers of the global parameters. Therefore, the identifier to
- /// be used to create the audit entry for the deleted parameter must
- /// be explicitly specified.
- ///
- /// @param parameter_name Parameter name.
- /// @param id Parameter id.
- void remoteDeleteGlobalParameter(const std::string& parameter_name,
- const uint64_t id) {
- auto& mgr = ConfigBackendDHCPv6Mgr::instance();
- mgr.getPool()->deleteGlobalParameter6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
- parameter_name);
- addDeleteAuditEntry("dhcp6_global_parameter", id);
- }
-
- /// @brief Deletes specified option definition from the configuration
- /// backend and generates audit entry.
- ///
- /// @param code Option code.
- /// @param space Option space.
- void remoteDeleteOptionDef(const uint16_t code, const std::string& space) {
- auto& mgr = ConfigBackendDHCPv6Mgr::instance();
-
- auto option_def = mgr.getPool()->getOptionDef6(BackendSelector::UNSPEC(),
- ServerSelector::ALL(),
- code, space);
-
- if (option_def) {
- mgr.getPool()->deleteOptionDef6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
- code, space);
- addDeleteAuditEntry("dhcp6_option_def", option_def->getId());
- }
- }
-
- /// @brief Deletes specified global option from the configuration backend
- /// and generates audit entry.
- ///
- /// @param code Option code.
- /// @param space Option space.
- void remoteDeleteOption(const uint16_t code, const std::string& space) {
- auto& mgr = ConfigBackendDHCPv6Mgr::instance();
-
- auto option = mgr.getPool()->getOption6(BackendSelector::UNSPEC(),
- ServerSelector::ALL(),
- code, space);
-
- if (option) {
- mgr.getPool()->deleteOptionDef6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
- code, space);
- addDeleteAuditEntry("dhcp6_option_def", option->getId());
- }
- }
-
- /// @brief Deletes specified shared network from the configuration backend
- /// and generates audit entry.
- ///
- /// @param name Name of the shared network to be deleted.
- void remoteDeleteSharedNetwork(const std::string& name) {
- auto& mgr = ConfigBackendDHCPv6Mgr::instance();
-
- auto network = mgr.getPool()->getSharedNetwork6(BackendSelector::UNSPEC(),
- ServerSelector::ALL(),
- name);
-
- if (network) {
- mgr.getPool()->deleteSharedNetwork6(BackendSelector::UNSPEC(),
- ServerSelector::ALL(),
- name);
- addDeleteAuditEntry("dhcp6_shared_network", network->getId());
- }
- }
-
- /// @brief Deletes specified subnet from the configuration backend and
- /// generates audit entry.
- void remoteDeleteSubnet(const SubnetID& id) {
- auto& mgr = ConfigBackendDHCPv6Mgr::instance();
-
- mgr.getPool()->deleteSubnet6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
- id);
- addDeleteAuditEntry("dhcp6_subnet", id);
- }
-
-
- /// @brief Tests the @c CBControlDHCPv6::databaseConfigApply method.
- ///
- /// This test inserts configuration elements of each type into the
- /// configuration database. Next, it calls the @c databaseConfigApply,
- /// which should merge each object from the database for which the
- /// CREATE or UPDATE audit entry is found. The test then verifies
- /// if the appropriate entries have been merged.
- ///
- /// @param lb_modification_time Lower bound modification time to be
- /// passed to the @c databaseConfigApply.
- void testDatabaseConfigApply(const boost::posix_time::ptime& lb_modification_time) {
- remoteStoreTestConfiguration();
-
- ASSERT_FALSE(audit_entries_.empty())
- << "Require at least one audit entry. The test is broken!";
-
- ctl_.databaseConfigApply(BackendSelector::UNSPEC(), ServerSelector::ALL(),
- lb_modification_time, audit_entries_);
-
- // The updates should have been merged into current configuration.
- auto srv_cfg = CfgMgr::instance().getCurrentCfg();
-
- // If there is an audit entry for global parameter and the parameter
- // modification time is later than last audit entry time it should
- // be merged.
- if (fetchConfigElement("dhcp6_global_parameter") &&
- (getTimestamp("dhcp6_global_parameter") > lb_modification_time)) {
- checkConfiguredGlobal(srv_cfg, "foo", Element::create("bar"));
-
- } else {
- // Otherwise it shouldn't exist.
- EXPECT_FALSE(srv_cfg->getConfiguredGlobals()->get("foo"));
- }
-
- // If there is an audit entry for option definition and the definition
- // modification time is later than last audit entry time it should
- // be merged.
- auto found_def = srv_cfg->getCfgOptionDef()->get("isc", "one");
- if (fetchConfigElement("dhcp6_option_def") &&
- getTimestamp("dhcp6_option_def") > lb_modification_time) {
- ASSERT_TRUE(found_def);
- EXPECT_EQ(101, found_def->getCode());
- EXPECT_EQ(OptionDataType::OPT_UINT16_TYPE, found_def->getType());
-
- } else {
- EXPECT_FALSE(found_def);
- }
-
- // If there is an audit entry for an option and the option
- // modification time is later than last audit entry time it should
- // be merged.
- auto options = srv_cfg->getCfgOption();
- auto found_opt = options->get("dhcp6", D6O_BOOTFILE_URL);
- if (fetchConfigElement("dhcp6_options") &&
- (getTimestamp("dhcp6_options") > lb_modification_time)) {
- ASSERT_TRUE(found_opt.option_);
- EXPECT_EQ("some.bootfile", found_opt.option_->toString());
-
- } else {
- EXPECT_FALSE(found_opt.option_);
- }
-
- // If there is an audit entry for a shared network and the network
- // modification time is later than last audit entry time it should
- // be merged.
- auto networks = srv_cfg->getCfgSharedNetworks6();
- auto found_network = networks->getByName("one");
- if (fetchConfigElement("dhcp6_shared_network") &&
- (getTimestamp("dhcp6_shared_network") > lb_modification_time)) {
- ASSERT_TRUE(found_network);
- EXPECT_TRUE(found_network->hasFetchGlobalsFn());
-
- } else {
- EXPECT_FALSE(found_network);
- }
-
- // If there is an audit entry for a subnet and the subnet modification
- // time is later than last audit entry time it should be merged.
- auto subnets = srv_cfg->getCfgSubnets6();
- auto found_subnet = subnets->getSubnet(1);
- if (fetchConfigElement("dhcp6_subnet") &&
- (getTimestamp("dhcp6_subnet") > lb_modification_time)) {
- ASSERT_TRUE(found_subnet);
- EXPECT_TRUE(found_subnet->hasFetchGlobalsFn());
-
- } else {
- EXPECT_FALSE(found_subnet);
- }
- }
-
- /// @brief Tests deletion of the configuration elements by the
- /// @c CBControlDHCPv6::databaseConfigApply method.
- ///
- /// This test inserts configuration elements of each type into the
- /// configuration database and calls the @c databaseConfigApply
- /// to fetch this configuration and merge into the local server
- /// configuration.
- ///
- /// Next, the test calls the specified callback function, i.e.
- /// @c db_modifications, which deletes selected configuration
- /// elements from the database and generates appropriate audit
- /// entries. Finally, it calls the @c databaseConfigApply again
- /// to process the audit entries and checks if the appropriate
- /// configuration elements are deleted from the local configuration
- ///
- /// @param lb_modification_time Lower bound modification time to be
- /// passed to the @c databaseConfigApply.
- /// @param db_modifications Pointer to the callback function which
- /// applies test specific modifications into the database.
- void testDatabaseConfigApplyDelete(const boost::posix_time::ptime& lb_modification_time,
- std::function<void()> db_modifications) {
- // Store initial configuration into the database.
- remoteStoreTestConfiguration();
-
- // Since we pass an empty audit collection the server treats this
- // as if the server is starting up and fetches the entire
- // configuration from the database.
- ctl_.databaseConfigApply(BackendSelector::UNSPEC(), ServerSelector::ALL(),
- ctl_.getInitialAuditEntryTime(),
- AuditEntryCollection());
- // Commit the configuration so as it is moved from the staging
- // to current.
- CfgMgr::instance().commit();
-
- // Run user defined callback which should delete selected configuration
- // elements from the configuration backend. The appropriate DELETE
- // audit entries should now be stored in the audit_entries_ collection.
- if (db_modifications) {
- db_modifications();
- }
-
- // Process the DELETE audit entries.
- ctl_.databaseConfigApply(BackendSelector::UNSPEC(), ServerSelector::ALL(),
- lb_modification_time, audit_entries_);
-
- // All changes should have been applied in the current configuration.
- auto srv_cfg = CfgMgr::instance().getCurrentCfg();
-
- {
- SCOPED_TRACE("global parameters");
- // One of the global parameters should still be there.
- EXPECT_TRUE(srv_cfg->getConfiguredGlobals()->get("bar"));
- if (deleteConfigElement("dhcp6_global_parameter", 1)) {
- EXPECT_FALSE(srv_cfg->getConfiguredGlobals()->get("foo"));
-
- } else {
- EXPECT_TRUE(srv_cfg->getConfiguredGlobals()->get("foo"));
- }
- }
-
- {
- SCOPED_TRACE("option definitions");
- // One of the option definitions should still be there.
- EXPECT_TRUE(srv_cfg->getCfgOptionDef()->get("isc", "two"));
- auto found_def = srv_cfg->getCfgOptionDef()->get("isc", "one");
- if (deleteConfigElement("dhcp6_option_def", 1)) {
- EXPECT_FALSE(found_def);
-
- } else {
- EXPECT_TRUE(found_def);
- }
- }
-
- {
- SCOPED_TRACE("global options");
- // One of the options should still be there.
- EXPECT_TRUE(srv_cfg->getCfgOption()->get("dhcp6", D6O_AFTR_NAME).option_);
- auto found_opt = srv_cfg->getCfgOption()->get("dhcp6", D6O_AFTR_NAME);
- if (deleteConfigElement("dhcp6_options", 1)) {
- EXPECT_FALSE(found_opt.option_);
-
- } else {
- EXPECT_TRUE(found_opt.option_);
- }
- }
-
- {
- SCOPED_TRACE("shared networks");
- // One of the shared networks should still be there.
- EXPECT_TRUE(srv_cfg->getCfgSharedNetworks6()->getByName("two"));
- auto found_network = srv_cfg->getCfgSharedNetworks6()->getByName("one");
- if (deleteConfigElement("dhcp6_shared_network", 1)) {
- EXPECT_FALSE(found_network);
-
- } else {
- EXPECT_TRUE(found_network);
- }
- }
-
- {
- SCOPED_TRACE("subnets");
- // One of the subnets should still be there.
- EXPECT_TRUE(srv_cfg->getCfgSubnets6()->getSubnet(2));
- auto found_subnet = srv_cfg->getCfgSubnets6()->getSubnet(1);
- if (deleteConfigElement("dhcp6_subnet", 1)) {
- EXPECT_FALSE(found_subnet);
- // If the subnet has been deleted, make sure that
- // it was detached from the shared network it belonged
- // to, if the shared network still exists.
- auto found_network = srv_cfg->getCfgSharedNetworks6()->getByName("one");
- if (found_network) {
- EXPECT_TRUE(found_network->getAllSubnets()->empty());
- }
-
- } else {
- EXPECT_TRUE(found_subnet);
- }
- }
- }
-
- /// @brief Instance of the @c CBControlDHCPv6 used for testing.
- TestCBControlDHCPv6 ctl_;
-};
-
-
-// This test verifies that the configuration updates for all object
-// types are merged into the current configuration.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyAll) {
-
- addCreateAuditEntry("dhcp6_global_parameter");
- addCreateAuditEntry("dhcp6_option_def");
- addCreateAuditEntry("dhcp6_options");
- addCreateAuditEntry("dhcp6_shared_network");
- addCreateAuditEntry("dhcp6_subnet");
-
- testDatabaseConfigApply(getTimestamp(-5));
-}
-
-// This test verifies that multiple configuration elements are
-// deleted from the local configuration as a result of being
-// deleted from the database.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteAll) {
- testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
- remoteDeleteGlobalParameter("foo", 1);
- remoteDeleteOptionDef(101, "isc");
- remoteDeleteOption(D6O_BOOTFILE_URL, DHCP6_OPTION_SPACE);
- remoteDeleteSharedNetwork("one");
- remoteDeleteSubnet(SubnetID(1));
- });
-}
-
-// This test verifies that an attempt to delete non-existing
-// configuration element does not cause an error.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteNonExisting) {
- testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
- // Add several audit entries instructing to delete the
- // non-existing configuration elements. The ids are set
- // to 3, but the only existing elements have ids of 1
- // and 2.
- addDeleteAuditEntry("dhcp6_global_parameter", 3);
- addDeleteAuditEntry("dhcp6_option_def", 3);
- addDeleteAuditEntry("dhcp6_options", 3);
- addDeleteAuditEntry("dhcp6_shared_network", 3);
- addDeleteAuditEntry("dhcp6_subnet", 3);
- });
-}
-
-// This test verifies that only a global parameter is merged into
-// the current configuration.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyGlobal) {
- addCreateAuditEntry("dhcp6_global_parameter");
- testDatabaseConfigApply(getTimestamp(-5));
-}
-
-// This test verifies that the global parameter is deleted from
-// the local configuration as a result of being deleted from the
-// database.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteGlobal) {
- testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
- remoteDeleteGlobalParameter("foo", 1);
- });
-}
-
-// This test verifies that global parameter is not fetched from the
-// database when the modification time is earlier than the last
-// fetched audit entry.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyGlobalNotFetched) {
- addCreateAuditEntry("dhcp6_global_parameter");
- testDatabaseConfigApply(getTimestamp(-3));
-}
-
-// This test verifies that only an option definition is merged into
-// the current configuration.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyOptionDef) {
- addCreateAuditEntry("dhcp6_option_def");
- testDatabaseConfigApply(getTimestamp(-5));
-}
-
-// This test verifies that the option definition is deleted from
-// the local configuration as a result of being deleted from the
-// database.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteOptionDef) {
- addDeleteAuditEntry("dhcp6_option_def", 1);
- testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
- remoteDeleteOptionDef(101, "isc");
- });
-}
-
-// This test verifies that option definition is not fetched from the
-// database when the modification time is earlier than the last
-// fetched audit entry.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyOptionDefNotFetched) {
- addCreateAuditEntry("dhcp6_option_def");
- testDatabaseConfigApply(getTimestamp(-3));
-}
-
-// This test verifies that only a DHCPv6 option is merged into the
-// current configuration.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyOption) {
- addCreateAuditEntry("dhcp6_options");
- testDatabaseConfigApply(getTimestamp(-5));
-}
-
-// This test verifies that the global option is deleted from
-// the local configuration as a result of being deleted from the
-// database.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteOption) {
- testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
- remoteDeleteOption(D6O_BOOTFILE_URL, DHCP6_OPTION_SPACE);
- });
-}
-
-// This test verifies that DHCPv6 option is not fetched from the
-// database when the modification time is earlier than the last
-// fetched audit entry.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyOptionNotFetched) {
- addCreateAuditEntry("dhcp6_options");
- testDatabaseConfigApply(getTimestamp(-3));
-}
-
-// This test verifies that only a shared network is merged into the
-// current configuration.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplySharedNetwork) {
- addCreateAuditEntry("dhcp6_shared_network");
- testDatabaseConfigApply(getTimestamp(-5));
-}
-
-// This test verifies that the shared network is deleted from
-// the local configuration as a result of being deleted from the
-// database.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteSharedNetwork) {
- testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
- remoteDeleteSharedNetwork("one");
- });
-}
-
-// This test verifies that shared network is not fetched from the
-// database when the modification time is earlier than the last
-// fetched audit entry.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplySharedNetworkNotFetched) {
- addCreateAuditEntry("dhcp6_shared_network");
- testDatabaseConfigApply(getTimestamp(-3));
-}
-
-// This test verifies that only a subnet is merged into the current
-// configuration.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplySubnet) {
- addCreateAuditEntry("dhcp6_shared_network");
- addCreateAuditEntry("dhcp6_subnet");
- testDatabaseConfigApply(getTimestamp(-5));
-}
-
-// This test verifies that the subnet is deleted from the local
-// configuration as a result of being deleted from the database.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteSubnet) {
- testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
- remoteDeleteSubnet(SubnetID(1));
- });
-}
-
-// This test verifies that subnet is not fetched from the database
-// when the modification time is earlier than the last fetched audit
-// entry.
-TEST_F(CBControlDHCPv6Test, databaseConfigApplySubnetNotFetched) {
- addCreateAuditEntry("dhcp6_subnet");
- testDatabaseConfigApply(getTimestamp(-3));
-}
-
-}
#include <cc/stamped_value.h>
#include <dhcp/option_string.h>
#include <dhcpsrv/cb_ctl_dhcp4.h>
+#include <dhcpsrv/cb_ctl_dhcp6.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/testutils/generic_backend_unittest.h>
#include <dhcpsrv/testutils/test_config_backend_dhcp4.h>
+#include <dhcpsrv/testutils/test_config_backend_dhcp6.h>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <gtest/gtest.h>
#include <map>
AuditEntryCollection audit_entries_;
};
+// ************************ V4 tests *********************
+
/// @brief Naked @c CBControlDHCPv4 class exposing protected methods.
class TestCBControlDHCPv4 : public CBControlDHCPv4 {
public:
testDatabaseConfigApply(getTimestamp(-3));
}
+// ************************ V6 tests *********************
+
+/// @brief Naked @c CBControlDHCPv6 class exposing protected methods.
+class TestCBControlDHCPv6 : public CBControlDHCPv6 {
+public:
+ /// @brief Constructor.
+ TestCBControlDHCPv6() {
+ CfgMgr::instance().setFamily(AF_INET6);
+ }
+
+ using CBControlDHCPv6::getInitialAuditEntryTime;
+ using CBControlDHCPv6::databaseConfigApply;
+};
+
+/// @brief Test fixture class for @c CBControlDHCPv6 unit tests.
+class CBControlDHCPv6Test : public CBControlDHCPTest {
+public:
+
+ /// @brief Constructor.
+ CBControlDHCPv6Test()
+ : CBControlDHCPTest(), ctl_() {
+ ConfigBackendDHCPv6Mgr::instance().registerBackendFactory("memfile",
+ [](const DatabaseConnection::ParameterMap& params)
+ -> ConfigBackendDHCPv6Ptr {
+ return (TestConfigBackendDHCPv6Ptr(new TestConfigBackendDHCPv6(params)));
+ });
+ ConfigBackendDHCPv6Mgr::instance().addBackend("type=memfile");
+
+ // By default, set timestamps for all object types to -4. That leaves
+ // us with the possibility to use index -5 (earlier) to use as lower
+ // bound modification time so as all objects are fetched.
+ setAllTimestamps(-4);
+ }
+
+ /// @brief Sets timestamps of all DHCPv6 specific object types.
+ ///
+ /// @param timestamp_index Index of the timestamp to be set.
+ virtual void setAllTimestamps(const int timestamp_index) {
+ setTimestamp("dhcp6_global_parameter", timestamp_index);
+ setTimestamp("dhcp6_option_def", timestamp_index);
+ setTimestamp("dhcp6_options", timestamp_index);
+ setTimestamp("dhcp6_shared_network", timestamp_index);
+ setTimestamp("dhcp6_subnet", timestamp_index);
+ }
+
+ /// @brief Creates test server configuration and stores it in a test
+ /// configuration backend.
+ ///
+ /// There are pairs of configuration elements stored in the database.
+ /// For example: two global parameters, two option definitions etc.
+ /// Having two elements of each type in the database is useful in tests
+ /// which verify that an element is deleted from the local configuration
+ /// as a result of being deleted from the configuration backend. In that
+ /// case the test verifies that one of the elements of the given type
+ /// is deleted and one is left.
+ void remoteStoreTestConfiguration() {
+ auto& mgr = ConfigBackendDHCPv6Mgr::instance();
+
+ // Insert global parameters into a database.
+ StampedValuePtr global_parameter = StampedValue::create("foo", "bar");
+ global_parameter->setModificationTime(getTimestamp("dhcp6_global_parameter"));
+ ASSERT_NO_THROW(mgr.getPool()->createUpdateGlobalParameter6(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ global_parameter));
+
+ global_parameter = StampedValue::create("bar", "teta");
+ global_parameter->setModificationTime(getTimestamp("dhcp6_global_parameter"));
+ ASSERT_NO_THROW(mgr.getPool()->createUpdateGlobalParameter6(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ global_parameter));
+
+ // Insert option definitions into the database.
+ OptionDefinitionPtr def(new OptionDefinition("one", 101, "uint16"));
+ def->setId(1);
+ def->setOptionSpaceName("isc");
+ def->setModificationTime(getTimestamp("dhcp6_option_def"));
+ ASSERT_NO_THROW(mgr.getPool()->createUpdateOptionDef6(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ def));
+ def.reset(new OptionDefinition("two", 102, "uint16"));
+ def->setId(2);
+ def->setOptionSpaceName("isc");
+ def->setModificationTime(getTimestamp("dhcp6_option_def"));
+ ASSERT_NO_THROW(mgr.getPool()->createUpdateOptionDef6(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ def));
+
+ // Insert global options into the database.
+ OptionDescriptorPtr opt(new OptionDescriptor(createOption<OptionString>
+ (Option::V6, D6O_BOOTFILE_URL,
+ true, false, "some.bootfile")));
+ opt->setId(1);
+ opt->space_name_ = DHCP6_OPTION_SPACE;
+ opt->setModificationTime(getTimestamp("dhcp6_options"));
+ mgr.getPool()->createUpdateOption6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
+ opt);
+
+ opt.reset(new OptionDescriptor(createOption<OptionString>
+ (Option::V6, D6O_AFTR_NAME,
+ true, true, "some.example.com")));
+ opt->setId(2);
+ opt->space_name_ = DHCP6_OPTION_SPACE;
+ opt->setModificationTime(getTimestamp("dhcp6_options"));
+ mgr.getPool()->createUpdateOption6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
+ opt);
+
+ // Insert shared networks into the database.
+ SharedNetwork6Ptr network(new SharedNetwork6("one"));
+ network->setId(1);
+ network->setModificationTime(getTimestamp("dhcp6_shared_network"));
+ mgr.getPool()->createUpdateSharedNetwork6(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ network);
+
+ network.reset(new SharedNetwork6("two"));
+ network->setId(2);
+ network->setModificationTime(getTimestamp("dhcp6_shared_network"));
+ mgr.getPool()->createUpdateSharedNetwork6(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ network);
+
+ // Insert subnets into the database.
+ Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 64, 1, 2, 3, 4, SubnetID(1)));
+ subnet->setModificationTime(getTimestamp("dhcp6_subnet"));
+ subnet->setSharedNetworkName("one");
+ mgr.getPool()->createUpdateSubnet6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
+ subnet);
+
+ subnet.reset(new Subnet6(IOAddress("2001:db8:2::"), 64, 1, 2, 3, 4, SubnetID(2)));
+ subnet->setModificationTime(getTimestamp("dhcp6_subnet"));
+ mgr.getPool()->createUpdateSubnet6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
+ subnet);
+ }
+
+ /// @brief Deletes specified global parameter from the configuration
+ /// backend and generates audit entry.
+ ///
+ /// Note that the current Kea implementation does not track database
+ /// identifiers of the global parameters. Therefore, the identifier to
+ /// be used to create the audit entry for the deleted parameter must
+ /// be explicitly specified.
+ ///
+ /// @param parameter_name Parameter name.
+ /// @param id Parameter id.
+ void remoteDeleteGlobalParameter(const std::string& parameter_name,
+ const uint64_t id) {
+ auto& mgr = ConfigBackendDHCPv6Mgr::instance();
+ mgr.getPool()->deleteGlobalParameter6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
+ parameter_name);
+ addDeleteAuditEntry("dhcp6_global_parameter", id);
+ }
+
+ /// @brief Deletes specified option definition from the configuration
+ /// backend and generates audit entry.
+ ///
+ /// @param code Option code.
+ /// @param space Option space.
+ void remoteDeleteOptionDef(const uint16_t code, const std::string& space) {
+ auto& mgr = ConfigBackendDHCPv6Mgr::instance();
+
+ auto option_def = mgr.getPool()->getOptionDef6(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ code, space);
+
+ if (option_def) {
+ mgr.getPool()->deleteOptionDef6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
+ code, space);
+ addDeleteAuditEntry("dhcp6_option_def", option_def->getId());
+ }
+ }
+
+ /// @brief Deletes specified global option from the configuration backend
+ /// and generates audit entry.
+ ///
+ /// @param code Option code.
+ /// @param space Option space.
+ void remoteDeleteOption(const uint16_t code, const std::string& space) {
+ auto& mgr = ConfigBackendDHCPv6Mgr::instance();
+
+ auto option = mgr.getPool()->getOption6(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ code, space);
+
+ if (option) {
+ mgr.getPool()->deleteOptionDef6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
+ code, space);
+ addDeleteAuditEntry("dhcp6_option_def", option->getId());
+ }
+ }
+
+ /// @brief Deletes specified shared network from the configuration backend
+ /// and generates audit entry.
+ ///
+ /// @param name Name of the shared network to be deleted.
+ void remoteDeleteSharedNetwork(const std::string& name) {
+ auto& mgr = ConfigBackendDHCPv6Mgr::instance();
+
+ auto network = mgr.getPool()->getSharedNetwork6(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ name);
+
+ if (network) {
+ mgr.getPool()->deleteSharedNetwork6(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ name);
+ addDeleteAuditEntry("dhcp6_shared_network", network->getId());
+ }
+ }
+
+ /// @brief Deletes specified subnet from the configuration backend and
+ /// generates audit entry.
+ void remoteDeleteSubnet(const SubnetID& id) {
+ auto& mgr = ConfigBackendDHCPv6Mgr::instance();
+
+ mgr.getPool()->deleteSubnet6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
+ id);
+ addDeleteAuditEntry("dhcp6_subnet", id);
+ }
+
+
+ /// @brief Tests the @c CBControlDHCPv6::databaseConfigApply method.
+ ///
+ /// This test inserts configuration elements of each type into the
+ /// configuration database. Next, it calls the @c databaseConfigApply,
+ /// which should merge each object from the database for which the
+ /// CREATE or UPDATE audit entry is found. The test then verifies
+ /// if the appropriate entries have been merged.
+ ///
+ /// @param lb_modification_time Lower bound modification time to be
+ /// passed to the @c databaseConfigApply.
+ void testDatabaseConfigApply(const boost::posix_time::ptime& lb_modification_time) {
+ remoteStoreTestConfiguration();
+
+ ASSERT_FALSE(audit_entries_.empty())
+ << "Require at least one audit entry. The test is broken!";
+
+ ctl_.databaseConfigApply(BackendSelector::UNSPEC(), ServerSelector::ALL(),
+ lb_modification_time, audit_entries_);
+
+ // The updates should have been merged into current configuration.
+ auto srv_cfg = CfgMgr::instance().getCurrentCfg();
+
+ // If there is an audit entry for global parameter and the parameter
+ // modification time is later than last audit entry time it should
+ // be merged.
+ if (fetchConfigElement("dhcp6_global_parameter") &&
+ (getTimestamp("dhcp6_global_parameter") > lb_modification_time)) {
+ checkConfiguredGlobal(srv_cfg, "foo", Element::create("bar"));
+
+ } else {
+ // Otherwise it shouldn't exist.
+ EXPECT_FALSE(srv_cfg->getConfiguredGlobals()->get("foo"));
+ }
+
+ // If there is an audit entry for option definition and the definition
+ // modification time is later than last audit entry time it should
+ // be merged.
+ auto found_def = srv_cfg->getCfgOptionDef()->get("isc", "one");
+ if (fetchConfigElement("dhcp6_option_def") &&
+ getTimestamp("dhcp6_option_def") > lb_modification_time) {
+ ASSERT_TRUE(found_def);
+ EXPECT_EQ(101, found_def->getCode());
+ EXPECT_EQ(OptionDataType::OPT_UINT16_TYPE, found_def->getType());
+
+ } else {
+ EXPECT_FALSE(found_def);
+ }
+
+ // If there is an audit entry for an option and the option
+ // modification time is later than last audit entry time it should
+ // be merged.
+ auto options = srv_cfg->getCfgOption();
+ auto found_opt = options->get("dhcp6", D6O_BOOTFILE_URL);
+ if (fetchConfigElement("dhcp6_options") &&
+ (getTimestamp("dhcp6_options") > lb_modification_time)) {
+ ASSERT_TRUE(found_opt.option_);
+ EXPECT_EQ("some.bootfile", found_opt.option_->toString());
+
+ } else {
+ EXPECT_FALSE(found_opt.option_);
+ }
+
+ // If there is an audit entry for a shared network and the network
+ // modification time is later than last audit entry time it should
+ // be merged.
+ auto networks = srv_cfg->getCfgSharedNetworks6();
+ auto found_network = networks->getByName("one");
+ if (fetchConfigElement("dhcp6_shared_network") &&
+ (getTimestamp("dhcp6_shared_network") > lb_modification_time)) {
+ ASSERT_TRUE(found_network);
+ EXPECT_TRUE(found_network->hasFetchGlobalsFn());
+
+ } else {
+ EXPECT_FALSE(found_network);
+ }
+
+ // If there is an audit entry for a subnet and the subnet modification
+ // time is later than last audit entry time it should be merged.
+ auto subnets = srv_cfg->getCfgSubnets6();
+ auto found_subnet = subnets->getSubnet(1);
+ if (fetchConfigElement("dhcp6_subnet") &&
+ (getTimestamp("dhcp6_subnet") > lb_modification_time)) {
+ ASSERT_TRUE(found_subnet);
+ EXPECT_TRUE(found_subnet->hasFetchGlobalsFn());
+
+ } else {
+ EXPECT_FALSE(found_subnet);
+ }
+ }
+
+ /// @brief Tests deletion of the configuration elements by the
+ /// @c CBControlDHCPv6::databaseConfigApply method.
+ ///
+ /// This test inserts configuration elements of each type into the
+ /// configuration database and calls the @c databaseConfigApply
+ /// to fetch this configuration and merge into the local server
+ /// configuration.
+ ///
+ /// Next, the test calls the specified callback function, i.e.
+ /// @c db_modifications, which deletes selected configuration
+ /// elements from the database and generates appropriate audit
+ /// entries. Finally, it calls the @c databaseConfigApply again
+ /// to process the audit entries and checks if the appropriate
+ /// configuration elements are deleted from the local configuration
+ ///
+ /// @param lb_modification_time Lower bound modification time to be
+ /// passed to the @c databaseConfigApply.
+ /// @param db_modifications Pointer to the callback function which
+ /// applies test specific modifications into the database.
+ void testDatabaseConfigApplyDelete(const boost::posix_time::ptime& lb_modification_time,
+ std::function<void()> db_modifications) {
+ // Store initial configuration into the database.
+ remoteStoreTestConfiguration();
+
+ // Since we pass an empty audit collection the server treats this
+ // as if the server is starting up and fetches the entire
+ // configuration from the database.
+ ctl_.databaseConfigApply(BackendSelector::UNSPEC(), ServerSelector::ALL(),
+ ctl_.getInitialAuditEntryTime(),
+ AuditEntryCollection());
+ // Commit the configuration so as it is moved from the staging
+ // to current.
+ CfgMgr::instance().commit();
+
+ // Run user defined callback which should delete selected configuration
+ // elements from the configuration backend. The appropriate DELETE
+ // audit entries should now be stored in the audit_entries_ collection.
+ if (db_modifications) {
+ db_modifications();
+ }
+
+ // Process the DELETE audit entries.
+ ctl_.databaseConfigApply(BackendSelector::UNSPEC(), ServerSelector::ALL(),
+ lb_modification_time, audit_entries_);
+
+ // All changes should have been applied in the current configuration.
+ auto srv_cfg = CfgMgr::instance().getCurrentCfg();
+
+ {
+ SCOPED_TRACE("global parameters");
+ // One of the global parameters should still be there.
+ EXPECT_TRUE(srv_cfg->getConfiguredGlobals()->get("bar"));
+ if (deleteConfigElement("dhcp6_global_parameter", 1)) {
+ EXPECT_FALSE(srv_cfg->getConfiguredGlobals()->get("foo"));
+
+ } else {
+ EXPECT_TRUE(srv_cfg->getConfiguredGlobals()->get("foo"));
+ }
+ }
+
+ {
+ SCOPED_TRACE("option definitions");
+ // One of the option definitions should still be there.
+ EXPECT_TRUE(srv_cfg->getCfgOptionDef()->get("isc", "two"));
+ auto found_def = srv_cfg->getCfgOptionDef()->get("isc", "one");
+ if (deleteConfigElement("dhcp6_option_def", 1)) {
+ EXPECT_FALSE(found_def);
+
+ } else {
+ EXPECT_TRUE(found_def);
+ }
+ }
+
+ {
+ SCOPED_TRACE("global options");
+ // One of the options should still be there.
+ EXPECT_TRUE(srv_cfg->getCfgOption()->get("dhcp6", D6O_AFTR_NAME).option_);
+ auto found_opt = srv_cfg->getCfgOption()->get("dhcp6", D6O_AFTR_NAME);
+ if (deleteConfigElement("dhcp6_options", 1)) {
+ EXPECT_FALSE(found_opt.option_);
+
+ } else {
+ EXPECT_TRUE(found_opt.option_);
+ }
+ }
+
+ {
+ SCOPED_TRACE("shared networks");
+ // One of the shared networks should still be there.
+ EXPECT_TRUE(srv_cfg->getCfgSharedNetworks6()->getByName("two"));
+ auto found_network = srv_cfg->getCfgSharedNetworks6()->getByName("one");
+ if (deleteConfigElement("dhcp6_shared_network", 1)) {
+ EXPECT_FALSE(found_network);
+
+ } else {
+ EXPECT_TRUE(found_network);
+ }
+ }
+
+ {
+ SCOPED_TRACE("subnets");
+ // One of the subnets should still be there.
+ EXPECT_TRUE(srv_cfg->getCfgSubnets6()->getSubnet(2));
+ auto found_subnet = srv_cfg->getCfgSubnets6()->getSubnet(1);
+ if (deleteConfigElement("dhcp6_subnet", 1)) {
+ EXPECT_FALSE(found_subnet);
+ // If the subnet has been deleted, make sure that
+ // it was detached from the shared network it belonged
+ // to, if the shared network still exists.
+ auto found_network = srv_cfg->getCfgSharedNetworks6()->getByName("one");
+ if (found_network) {
+ EXPECT_TRUE(found_network->getAllSubnets()->empty());
+ }
+
+ } else {
+ EXPECT_TRUE(found_subnet);
+ }
+ }
+ }
+
+ /// @brief Instance of the @c CBControlDHCPv6 used for testing.
+ TestCBControlDHCPv6 ctl_;
+};
+
+
+// This test verifies that the configuration updates for all object
+// types are merged into the current configuration.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyAll) {
+
+ addCreateAuditEntry("dhcp6_global_parameter");
+ addCreateAuditEntry("dhcp6_option_def");
+ addCreateAuditEntry("dhcp6_options");
+ addCreateAuditEntry("dhcp6_shared_network");
+ addCreateAuditEntry("dhcp6_subnet");
+
+ testDatabaseConfigApply(getTimestamp(-5));
+}
+
+// This test verifies that multiple configuration elements are
+// deleted from the local configuration as a result of being
+// deleted from the database.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteAll) {
+ testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
+ remoteDeleteGlobalParameter("foo", 1);
+ remoteDeleteOptionDef(101, "isc");
+ remoteDeleteOption(D6O_BOOTFILE_URL, DHCP6_OPTION_SPACE);
+ remoteDeleteSharedNetwork("one");
+ remoteDeleteSubnet(SubnetID(1));
+ });
+}
+
+// This test verifies that an attempt to delete non-existing
+// configuration element does not cause an error.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteNonExisting) {
+ testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
+ // Add several audit entries instructing to delete the
+ // non-existing configuration elements. The ids are set
+ // to 3, but the only existing elements have ids of 1
+ // and 2.
+ addDeleteAuditEntry("dhcp6_global_parameter", 3);
+ addDeleteAuditEntry("dhcp6_option_def", 3);
+ addDeleteAuditEntry("dhcp6_options", 3);
+ addDeleteAuditEntry("dhcp6_shared_network", 3);
+ addDeleteAuditEntry("dhcp6_subnet", 3);
+ });
+}
+
+// This test verifies that only a global parameter is merged into
+// the current configuration.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyGlobal) {
+ addCreateAuditEntry("dhcp6_global_parameter");
+ testDatabaseConfigApply(getTimestamp(-5));
+}
+
+// This test verifies that the global parameter is deleted from
+// the local configuration as a result of being deleted from the
+// database.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteGlobal) {
+ testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
+ remoteDeleteGlobalParameter("foo", 1);
+ });
+}
+
+// This test verifies that global parameter is not fetched from the
+// database when the modification time is earlier than the last
+// fetched audit entry.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyGlobalNotFetched) {
+ addCreateAuditEntry("dhcp6_global_parameter");
+ testDatabaseConfigApply(getTimestamp(-3));
+}
+
+// This test verifies that only an option definition is merged into
+// the current configuration.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyOptionDef) {
+ addCreateAuditEntry("dhcp6_option_def");
+ testDatabaseConfigApply(getTimestamp(-5));
+}
+
+// This test verifies that the option definition is deleted from
+// the local configuration as a result of being deleted from the
+// database.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteOptionDef) {
+ addDeleteAuditEntry("dhcp6_option_def", 1);
+ testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
+ remoteDeleteOptionDef(101, "isc");
+ });
+}
+
+// This test verifies that option definition is not fetched from the
+// database when the modification time is earlier than the last
+// fetched audit entry.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyOptionDefNotFetched) {
+ addCreateAuditEntry("dhcp6_option_def");
+ testDatabaseConfigApply(getTimestamp(-3));
+}
+
+// This test verifies that only a DHCPv6 option is merged into the
+// current configuration.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyOption) {
+ addCreateAuditEntry("dhcp6_options");
+ testDatabaseConfigApply(getTimestamp(-5));
+}
+
+// This test verifies that the global option is deleted from
+// the local configuration as a result of being deleted from the
+// database.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteOption) {
+ testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
+ remoteDeleteOption(D6O_BOOTFILE_URL, DHCP6_OPTION_SPACE);
+ });
+}
+
+// This test verifies that DHCPv6 option is not fetched from the
+// database when the modification time is earlier than the last
+// fetched audit entry.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyOptionNotFetched) {
+ addCreateAuditEntry("dhcp6_options");
+ testDatabaseConfigApply(getTimestamp(-3));
+}
+
+// This test verifies that only a shared network is merged into the
+// current configuration.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplySharedNetwork) {
+ addCreateAuditEntry("dhcp6_shared_network");
+ testDatabaseConfigApply(getTimestamp(-5));
+}
+
+// This test verifies that the shared network is deleted from
+// the local configuration as a result of being deleted from the
+// database.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteSharedNetwork) {
+ testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
+ remoteDeleteSharedNetwork("one");
+ });
+}
+
+// This test verifies that shared network is not fetched from the
+// database when the modification time is earlier than the last
+// fetched audit entry.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplySharedNetworkNotFetched) {
+ addCreateAuditEntry("dhcp6_shared_network");
+ testDatabaseConfigApply(getTimestamp(-3));
+}
+
+// This test verifies that only a subnet is merged into the current
+// configuration.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplySubnet) {
+ addCreateAuditEntry("dhcp6_shared_network");
+ addCreateAuditEntry("dhcp6_subnet");
+ testDatabaseConfigApply(getTimestamp(-5));
+}
+
+// This test verifies that the subnet is deleted from the local
+// configuration as a result of being deleted from the database.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplyDeleteSubnet) {
+ testDatabaseConfigApplyDelete(getTimestamp(-5), [this]() {
+ remoteDeleteSubnet(SubnetID(1));
+ });
+}
+
+// This test verifies that subnet is not fetched from the database
+// when the modification time is earlier than the last fetched audit
+// entry.
+TEST_F(CBControlDHCPv6Test, databaseConfigApplySubnetNotFetched) {
+ addCreateAuditEntry("dhcp6_subnet");
+ testDatabaseConfigApply(getTimestamp(-3));
+}
+
}