DELETE_ALL_SHARED_NETWORKS4,
DELETE_OPTION_DEF4_CODE_NAME,
DELETE_ALL_OPTION_DEFS4,
+ DELETE_ALL_OPTION_DEFS4_UNASSIGNED,
DELETE_OPTION4,
DELETE_ALL_GLOBAL_OPTIONS4_UNASSIGNED,
DELETE_OPTION4_SUBNET_ID,
in_bindings));
}
+ /// @brief Removes unassigned global parameters, global options and
+ /// option definitions.
+ ///
+ /// This function is called when one or more servers are deleted and
+ /// it is likely that there are some orhpaned configuration elements
+ /// left in the database. This method removes those elements.
+ void purgeUnassignedConfig() {
+ multipleUpdateDeleteQueries(DELETE_ALL_GLOBAL_PARAMETERS4_UNASSIGNED,
+ DELETE_ALL_GLOBAL_OPTIONS4_UNASSIGNED,
+ DELETE_ALL_OPTION_DEFS4_UNASSIGNED);
+ }
+
/// @brief Attempts to delete a server having a given tag.
///
/// @param server_tag Tag of the server to be deleted.
in_bindings);
// If we have deleted any servers we have to remove any dangling global
- // parameters.
+ // parameters, options and option definitions.
if (count > 0) {
- conn_.updateDeleteQuery(MySqlConfigBackendDHCPv4Impl::
- DELETE_ALL_GLOBAL_PARAMETERS4_UNASSIGNED,
- MySqlBindingCollection());
-
- conn_.updateDeleteQuery(MySqlConfigBackendDHCPv4Impl::
- DELETE_ALL_GLOBAL_OPTIONS4_UNASSIGNED,
- MySqlBindingCollection());
- /// @todo delete option definitions.
+ purgeUnassignedConfig();
}
transaction.commit();
in_bindings);
// If we have deleted any servers we have to remove any dangling global
- // parameters.
+ // parameters, options and option definitions.
if (count > 0) {
- conn_.updateDeleteQuery(MySqlConfigBackendDHCPv4Impl::
- DELETE_ALL_GLOBAL_PARAMETERS4_UNASSIGNED,
- MySqlBindingCollection());
-
- conn_.updateDeleteQuery(MySqlConfigBackendDHCPv4Impl::
- DELETE_ALL_GLOBAL_OPTIONS4_UNASSIGNED,
- MySqlBindingCollection());
-
- /// @todo delete dangling option definitions.
+ purgeUnassignedConfig();
}
transaction.commit();
MYSQL_DELETE_OPTION_DEF(dhcp4)
},
+ // Delete all option definitions which are assigned to no servers.
+ { MySqlConfigBackendDHCPv4Impl::DELETE_ALL_OPTION_DEFS4_UNASSIGNED,
+ MYSQL_DELETE_OPTION_DEF_UNASSIGNED(dhcp4)
+ },
+
// Delete single global option.
{ MySqlConfigBackendDHCPv4Impl::DELETE_OPTION4,
MYSQL_DELETE_OPTION(dhcp4, AND o.scope_id = 0 AND o.code = ? AND o.space = ?)
DELETE_ALL_SHARED_NETWORKS6,
DELETE_OPTION_DEF6_CODE_NAME,
DELETE_ALL_OPTION_DEFS6,
+ DELETE_ALL_OPTION_DEFS6_UNASSIGNED,
DELETE_OPTION6,
DELETE_ALL_GLOBAL_OPTIONS6_UNASSIGNED,
DELETE_OPTION6_SUBNET_ID,
in_bindings));
}
+ /// @brief Removes unassigned global parameters, global options and
+ /// option definitions.
+ ///
+ /// This function is called when one or more servers are deleted and
+ /// it is likely that there are some orhpaned configuration elements
+ /// left in the database. This method removes those elements.
+ void purgeUnassignedConfig() {
+ multipleUpdateDeleteQueries(DELETE_ALL_GLOBAL_PARAMETERS6_UNASSIGNED,
+ DELETE_ALL_GLOBAL_OPTIONS6_UNASSIGNED,
+ DELETE_ALL_OPTION_DEFS6_UNASSIGNED);
+ }
+
/// @brief Attempts to delete a server having a given tag.
///
/// @param server_tag Tag of the server to be deleted.
in_bindings);
// If we have deleted any servers we have to remove any dangling global
- // parameters.
+ // parameters, options and option definitions.
if (count > 0) {
- conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::
- DELETE_ALL_GLOBAL_PARAMETERS6_UNASSIGNED,
- MySqlBindingCollection());
-
- conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::
- DELETE_ALL_GLOBAL_OPTIONS6_UNASSIGNED,
- MySqlBindingCollection());
- /// @todo delete dangling option definitions.
+ purgeUnassignedConfig();
}
transaction.commit();
in_bindings);
// If we have deleted any servers we have to remove any dangling global
- // parameters.
+ // parameters, options and option definitions.
if (count > 0) {
- conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::
- DELETE_ALL_GLOBAL_PARAMETERS6_UNASSIGNED,
- MySqlBindingCollection());
-
- conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::
- DELETE_ALL_GLOBAL_OPTIONS6_UNASSIGNED,
- MySqlBindingCollection());
- /// @todo delete dangling option definitions.
+ purgeUnassignedConfig();
}
transaction.commit();
MYSQL_DELETE_OPTION_DEF(dhcp6)
},
+ // Delete all option definitions which are assigned to no servers.
+ { MySqlConfigBackendDHCPv6Impl::DELETE_ALL_OPTION_DEFS6_UNASSIGNED,
+ MYSQL_DELETE_OPTION_DEF_UNASSIGNED(dhcp6)
+ },
+
// Delete single global option.
{ MySqlConfigBackendDHCPv6Impl::DELETE_OPTION6,
MYSQL_DELETE_OPTION(dhcp6, AND o.scope_id = 0 AND o.code = ? AND o.space = ?)
uint64_t last_def_id = 0;
+ OptionDefContainer local_option_defs;
+
// Run select query.
conn_.selectQuery(index, in_bindings, out_bindings,
- [&option_defs, &last_def_id]
+ [&local_option_defs, &last_def_id]
(MySqlBindingCollection& out_bindings) {
// Get pointer to last fetched option definition.
OptionDefinitionPtr last_def;
- if (!option_defs.empty()) {
- last_def = *option_defs.rbegin();
+ if (!local_option_defs.empty()) {
+ last_def = *local_option_defs.rbegin();
}
// See if the last fetched definition is the one for which we now got
last_def->setModificationTime(out_bindings[5]->getTimestamp());
// server_tag
- last_def->setServerTag(out_bindings[10]->getString());
+ ServerTag last_def_server_tag(out_bindings[10]->getString());
+ last_def->setServerTag(last_def_server_tag.get());
+
+ // If we're fetching option definitions for a given server
+ // (explicit server tag is provided), it takes precedence over
+ // the same option definition specified for all servers.
+ // Therefore, we check if the given option already exists and
+ // belongs to 'all'.
+ auto& index = local_option_defs.get<1>();
+ auto existing_it_pair = index.equal_range(last_def->getCode());
+ auto existing_it = existing_it_pair.first;
+ bool found = false;
+ for ( ; existing_it != existing_it_pair.second; ++existing_it) {
+ if ((*existing_it)->getOptionSpaceName() == last_def->getOptionSpaceName()) {
+ found = true;
+ // This option definition was already fetched. Let's check
+ // if we should replace it or not.
+ if (!last_def_server_tag.amAll() && (*existing_it)->hasAllServerTag()) {
+ index.replace(existing_it, last_def);
+ return;
+ }
+ break;
+ }
+ }
- // Store created option definition.
- // (option_defs is a multi-index container with no unique
- // indexes so push_back can't fail).
- static_cast<void>(option_defs.push_back(last_def));
+ // If there is no such option definition yet or the existing option
+ // definition belongs to a different server and the inserted option
+ // definition is not for all servers.
+ if (!found ||
+ (!(*existing_it)->hasServerTag(last_def_server_tag) &&
+ !last_def_server_tag.amAll())) {
+ static_cast<void>(local_option_defs.push_back(last_def));
+ }
}
});
+
+ // Append the option definition fetched by this function into the container
+ // supplied by the caller. The container supplied by the caller may already
+ // hold some option definitions fetched for other server tags.
+ option_defs.insert(option_defs.end(), local_option_defs.begin(),
+ local_option_defs.end());
}
void
MySqlBinding::createBool(option_def->getArrayType()),
MySqlBinding::createString(option_def->getEncapsulatedSpace()),
record_types_binding,
- createInputContextBinding(option_def)
+ createInputContextBinding(option_def),
+ MySqlBinding::createString(tag),
+ MySqlBinding::createInteger<uint16_t>(option_def->getCode()),
+ MySqlBinding::createString(option_def->getOptionSpaceName())
};
MySqlTransaction transaction(conn_);
"option definition set",
true);
- if (existing_definition) {
- // Need to add three more bindings for WHERE clause.
- in_bindings.push_back(MySqlBinding::createString(tag));
- in_bindings.push_back(MySqlBinding::createInteger<uint16_t>(existing_definition->getCode()));
- in_bindings.push_back(MySqlBinding::createString(existing_definition->getOptionSpaceName()));
- conn_.updateDeleteQuery(update_option_def, in_bindings);
-
- } else {
- // If the option definition doesn't exist, let's insert it.
+ if (conn_.updateDeleteQuery(update_option_def, in_bindings) == 0) {
+ // Remove the bindings used only during the update.
+ in_bindings.resize(in_bindings.size() - 3);
conn_.insertQuery(insert_option_def, in_bindings);
// Fetch unique identifier of the inserted option definition and use it
#include <set>
#include <sstream>
#include <string>
+#include <vector>
namespace isc {
namespace dhcp {
const int& update_index,
const db::ServerPtr& server);
+ /// @brief Executes multiple update and/or delete queries with no input
+ /// bindings.
+ ///
+ /// This is a convenience function which takes multiple query indexes as
+ /// arguments and for each index executes an update or delete query.
+ /// One of the applications of this function is to remove dangling
+ /// configuration elements after the server associated with these elements
+ /// have been deleted.
+ ///
+ /// @tparam T type of the indexes, e.g. @c MySqlConfigBackendDHCPv4Impl::StatementIndex.
+ /// @tparam R parameter pack holding indexes of type @c T.
+ /// @param first_index first index.
+ /// @param other_indexes remaining indexes.
+ template<typename T, typename... R>
+ void multipleUpdateDeleteQueries(T first_index, R... other_indexes) {
+ std::vector<T> indexes({ first_index, other_indexes... });
+ db::MySqlBindingCollection empty_bindings;
+ for (auto index : indexes) {
+ conn_.updateDeleteQuery(index, empty_bindings);
+ }
+ }
+
/// @brief Returns backend type in the textual format.
///
/// @return "mysql".
"WHERE s.tag = ? " #__VA_ARGS__
#endif
+#ifndef MYSQL_DELETE_OPTION_DEF_UNASSIGNED
+#define MYSQL_DELETE_OPTION_DEF_UNASSIGNED(table_prefix, ...) \
+ "DELETE d FROM " #table_prefix "_option_def AS d " \
+ "LEFT JOIN " #table_prefix "_option_def_server AS a " \
+ " ON d.id = a.option_def_id " \
+ "WHERE a.option_def_id IS NULL " #__VA_ARGS__
+#endif
+
#ifndef MYSQL_DELETE_OPTION
#define MYSQL_DELETE_OPTION(table_prefix, ...) \
"DELETE o FROM " #table_prefix "_options AS o " \
option_def.reset(new OptionDefinition("whale", 236, "string"));
option_def->setOptionSpaceName("xyz");
test_option_defs_.push_back(option_def);
+
+ option_def.reset(new OptionDefinition("foobar", 234, "uint64", true));
+ option_def->setOptionSpaceName("dhcp4");
+ test_option_defs_.push_back(option_def);
}
/// @brief Creates several DHCP options used in tests.
}
}
+// This test verifies that it is possible to differentiate between the
+// option definitions by server tag and that the option definition
+// specified for the particular server overrides the definition for
+// all servers.
+TEST_F(MySqlConfigBackendDHCPv4Test, optionDefs4WithServerTags) {
+ OptionDefinitionPtr option1 = test_option_defs_[0];
+ OptionDefinitionPtr option2 = test_option_defs_[1];
+ OptionDefinitionPtr option3 = test_option_defs_[4];
+
+ // An attempt to create option definition for non-existing server should
+ // fail.
+ EXPECT_THROW(cbptr_->createUpdateOptionDef4(ServerSelector::ONE("server1"),
+ option1),
+ DbOperationError);
+
+ // Create two servers.
+ EXPECT_NO_THROW(cbptr_->createUpdateServer4(test_servers_[1]));
+ {
+ SCOPED_TRACE("server1 is created");
+ testNewAuditEntry("dhcp4_server",
+ AuditEntry::ModificationType::CREATE,
+ "server set");
+ }
+
+ EXPECT_NO_THROW(cbptr_->createUpdateServer4(test_servers_[2]));
+ {
+ SCOPED_TRACE("server2 is created");
+ testNewAuditEntry("dhcp4_server",
+ AuditEntry::ModificationType::CREATE,
+ "server set");
+ }
+
+ // This time creation of the option definition for the server1 should pass.
+ EXPECT_NO_THROW(cbptr_->createUpdateOptionDef4(ServerSelector::ONE("server1"),
+ option1));
+ {
+ SCOPED_TRACE("option definition for server1 is set");
+ // The value of 3 means there should be 3 audit entries available for the
+ // server1, two that indicate creation of the servers and one that we
+ // validate, which sets the option definition.
+ testNewAuditEntry("dhcp4_option_def",
+ AuditEntry::ModificationType::CREATE,
+ "option definition set",
+ ServerSelector::ONE("server1"),
+ 3, 1);
+ }
+
+ // Creation of the option definition for the server2 should also pass.
+ EXPECT_NO_THROW(cbptr_->createUpdateOptionDef4(ServerSelector::ONE("server2"),
+ option2));
+ {
+ SCOPED_TRACE("option definition for server2 is set");
+ // Same as in case of the server1, there should be 3 audit entries and
+ // we validate one of them.
+ testNewAuditEntry("dhcp4_option_def",
+ AuditEntry::ModificationType::CREATE,
+ "option definition set",
+ ServerSelector::ONE("server2"),
+ 3, 1);
+ }
+
+ // Finally, creation of the option definition for all servers should
+ // also pass.
+ EXPECT_NO_THROW(cbptr_->createUpdateOptionDef4(ServerSelector::ALL(),
+ option3));
+ {
+ SCOPED_TRACE("option definition for server2 is set");
+ // There should be one new audit entry for all servers. It logs
+ // the insertion of the option definition.
+ testNewAuditEntry("dhcp4_option_def",
+ AuditEntry::ModificationType::CREATE,
+ "option definition set",
+ ServerSelector::ALL(),
+ 1, 1);
+ }
+
+ OptionDefinitionPtr returned_option_def;
+
+ // Try to fetch the option definition specified for all servers. It should
+ // return the third one.
+ EXPECT_NO_THROW(
+ returned_option_def = cbptr_->getOptionDef4(ServerSelector::ALL(),
+ option3->getCode(),
+ option3->getOptionSpaceName())
+ );
+ ASSERT_TRUE(returned_option_def);
+ EXPECT_TRUE(returned_option_def->equals(*option3));
+
+ // Try to fetch the option definition specified for server1. It should
+ // override the definition for all servers.
+ EXPECT_NO_THROW(
+ returned_option_def = cbptr_->getOptionDef4(ServerSelector::ONE("server1"),
+ option1->getCode(),
+ option1->getOptionSpaceName())
+ );
+ ASSERT_TRUE(returned_option_def);
+ EXPECT_TRUE(returned_option_def->equals(*option1));
+
+ // The same in case of the server2.
+ EXPECT_NO_THROW(
+ returned_option_def = cbptr_->getOptionDef4(ServerSelector::ONE("server2"),
+ option2->getCode(),
+ option2->getOptionSpaceName())
+ );
+ ASSERT_TRUE(returned_option_def);
+ EXPECT_TRUE(returned_option_def->equals(*option2));
+
+ OptionDefContainer returned_option_defs;
+
+ // Try to fetch the collection of the option definitions for server1, server2
+ // and server3. The server3 does not have an explicit option definition, so
+ // for this server we should get the definition associated with "all" servers.
+ EXPECT_NO_THROW(
+ returned_option_defs = cbptr_->getAllOptionDefs4(ServerSelector::
+ MULTIPLE({ "server1", "server2",
+ "server3" }));
+ );
+ ASSERT_EQ(3, returned_option_defs.size());
+
+ // Check that expected option definitions have been returned.
+ auto current_option = returned_option_defs.begin();
+ EXPECT_TRUE((*current_option)->equals(*option1));
+ EXPECT_TRUE((*(++current_option))->equals(*option2));
+ EXPECT_TRUE((*(++current_option))->equals(*option3));
+
+ // Try to fetch the collection of options specified for all servers.
+ // This excludes the options specific to server1 and server2. It returns
+ // only the common ones.
+ EXPECT_NO_THROW(
+ returned_option_defs = cbptr_->getAllOptionDefs4(ServerSelector::ALL());
+
+ );
+ ASSERT_EQ(1, returned_option_defs.size());
+ EXPECT_TRUE((*returned_option_defs.begin())->equals(*option3));
+
+ // Delete the server1. It should remove associations of this server with the
+ // option definitions and the option definition itself.
+ EXPECT_NO_THROW(cbptr_->deleteServer4(ServerTag("server1")));
+ EXPECT_NO_THROW(
+ returned_option_defs = cbptr_->getAllOptionDefs4(ServerSelector::ONE("server1"));
+
+ );
+ ASSERT_EQ(1, returned_option_defs.size());
+ EXPECT_TRUE((*returned_option_defs.begin())->equals(*option3));
+
+ {
+ SCOPED_TRACE("DELETE audit entry for the option definition after server deletion");
+ testNewAuditEntry("dhcp4_option_def",
+ AuditEntry::ModificationType::DELETE,
+ "deleting a server", ServerSelector::ONE("server1"),
+ 2, 1);
+ }
+
+ // Attempt to delete option definition for server1.
+ uint64_t deleted_num = 0;
+ EXPECT_NO_THROW(deleted_num = cbptr_->deleteOptionDef4(ServerSelector::ONE("server1"),
+ option1->getCode(),
+ option1->getOptionSpaceName()));
+ EXPECT_EQ(0, deleted_num);
+
+ // Deleting the existing option definition for server2 should succeed.
+ EXPECT_NO_THROW(deleted_num = cbptr_->deleteOptionDef4(ServerSelector::ONE("server2"),
+ option2->getCode(),
+ option2->getOptionSpaceName()));
+ EXPECT_EQ(1, deleted_num);
+
+ // Create this option definition again to test that deletion of all servers
+ // removes it too.
+ EXPECT_NO_THROW(cbptr_->createUpdateOptionDef4(ServerSelector::ONE("server2"),
+ option2));
+
+ // Delete all servers, except 'all'.
+ EXPECT_NO_THROW(deleted_num = cbptr_->deleteAllServers4());
+ EXPECT_NO_THROW(
+ returned_option_defs = cbptr_->getAllOptionDefs4(ServerSelector::ALL());
+ );
+ EXPECT_EQ(1, deleted_num);
+ EXPECT_EQ(1, returned_option_defs.size());
+ EXPECT_TRUE((*returned_option_defs.begin())->equals(*option3));
+
+ {
+ SCOPED_TRACE("DELETE audit entry for the option definition after deletion of"
+ " all servers");
+ testNewAuditEntry("dhcp4_option_def",
+ AuditEntry::ModificationType::DELETE,
+ "deleting all servers", ServerSelector::ONE("server2"),
+ 4, 1);
+ }
+}
+
// Test that all option definitions can be fetched.
TEST_F(MySqlConfigBackendDHCPv4Test, getAllOptionDefs4) {
// Insert test option definitions into the database. Note that the second
// option definition will overwrite the first option definition as they use
// the same code and space.
+ size_t updates_num = 0;
for (auto option_def : test_option_defs_) {
cbptr_->createUpdateOptionDef4(ServerSelector::ALL(), option_def);
// That option definition overrides the first one so the audit entry should
// indicate an update.
- if (option_def->getName() == "bar") {
- SCOPED_TRACE("UPDATE audit entry for the option definition " +
- option_def->getName());
+ auto name = option_def->getName();
+ if (name.find("bar") != std::string::npos) {
+ SCOPED_TRACE("UPDATE audit entry for the option definition " + name);
testNewAuditEntry("dhcp4_option_def",
AuditEntry::ModificationType::UPDATE,
"option definition set");
+ ++updates_num;
} else {
- SCOPED_TRACE("CREATE audit entry for the option defnition " +
- option_def->getName());
+ SCOPED_TRACE("CREATE audit entry for the option defnition " + name);
testNewAuditEntry("dhcp4_option_def",
AuditEntry::ModificationType::CREATE,
"option definition set");
// Fetch all option_definitions.
OptionDefContainer option_defs = cbptr_->getAllOptionDefs4(ServerSelector::ALL());
- ASSERT_EQ(test_option_defs_.size() - 1, option_defs.size());
+ ASSERT_EQ(test_option_defs_.size() - updates_num, option_defs.size());
// All option definitions should also be returned for explicitly specified
// server tag.
option_defs = cbptr_->getAllOptionDefs4(ServerSelector::ONE("server1"));
- ASSERT_EQ(test_option_defs_.size() - 1, option_defs.size());
+ ASSERT_EQ(test_option_defs_.size() - updates_num, option_defs.size());
// See if option definitions are returned ok.
for (auto def = option_defs.begin(); def != option_defs.end(); ++def) {
EXPECT_EQ(0, cbptr_->deleteOptionDef4(ServerSelector::ALL(),
99, "non-exiting-space"));
// All option definitions should be still there.
- ASSERT_EQ(test_option_defs_.size() - 1, option_defs.size());
+ ASSERT_EQ(test_option_defs_.size() - updates_num, option_defs.size());
// Should not delete option definition for explicit server tag
// because our option definition is for all servers.
option_def.reset(new OptionDefinition("whale", 20236, "string"));
option_def->setOptionSpaceName("xyz");
test_option_defs_.push_back(option_def);
+
+ option_def.reset(new OptionDefinition("bar", 1234, "uint64", true));
+ option_def->setOptionSpaceName("dhcp6");
+ test_option_defs_.push_back(option_def);
}
/// @brief Creates several DHCP options used in tests.
}
}
+// This test verifies that it is possible to differentiate between the
+// option definitions by server tag and that the option definition
+// specified for the particular server overrides the definition for
+// all servers.
+TEST_F(MySqlConfigBackendDHCPv6Test, optionDefs6WithServerTags) {
+ OptionDefinitionPtr option1 = test_option_defs_[0];
+ OptionDefinitionPtr option2 = test_option_defs_[1];
+ OptionDefinitionPtr option3 = test_option_defs_[4];
+
+ // An attempt to create option definition for non-existing server should
+ // fail.
+ EXPECT_THROW(cbptr_->createUpdateOptionDef6(ServerSelector::ONE("server1"),
+ option1),
+ DbOperationError);
+
+ // Create two servers.
+ EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[1]));
+ {
+ SCOPED_TRACE("server1 is created");
+ testNewAuditEntry("dhcp6_server",
+ AuditEntry::ModificationType::CREATE,
+ "server set");
+ }
+
+ EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[2]));
+ {
+ SCOPED_TRACE("server2 is created");
+ testNewAuditEntry("dhcp6_server",
+ AuditEntry::ModificationType::CREATE,
+ "server set");
+ }
+
+ // This time creation of the option definition for the server1 should pass.
+ EXPECT_NO_THROW(cbptr_->createUpdateOptionDef6(ServerSelector::ONE("server1"),
+ option1));
+ {
+ SCOPED_TRACE("option definition for server1 is set");
+ // The value of 3 means there should be 3 audit entries available for the
+ // server1, two that indicate creation of the servers and one that we
+ // validate, which sets the option definition.
+ testNewAuditEntry("dhcp6_option_def",
+ AuditEntry::ModificationType::CREATE,
+ "option definition set",
+ ServerSelector::ONE("server1"),
+ 3, 1);
+ }
+
+ // Creation of the option definition for the server2 should also pass.
+ EXPECT_NO_THROW(cbptr_->createUpdateOptionDef6(ServerSelector::ONE("server2"),
+ option2));
+ {
+ SCOPED_TRACE("option definition for server2 is set");
+ // Same as in case of the server1, there should be 3 audit entries and
+ // we validate one of them.
+ testNewAuditEntry("dhcp6_option_def",
+ AuditEntry::ModificationType::CREATE,
+ "option definition set",
+ ServerSelector::ONE("server2"),
+ 3, 1);
+ }
+
+ // Finally, creation of the option definition for all servers should
+ // also pass.
+ EXPECT_NO_THROW(cbptr_->createUpdateOptionDef6(ServerSelector::ALL(),
+ option3));
+ {
+ SCOPED_TRACE("option definition for server2 is set");
+ // There should be one new audit entry for all servers. It logs
+ // the insertion of the option definition.
+ testNewAuditEntry("dhcp6_option_def",
+ AuditEntry::ModificationType::CREATE,
+ "option definition set",
+ ServerSelector::ALL(),
+ 1, 1);
+ }
+
+ OptionDefinitionPtr returned_option_def;
+
+ // Try to fetch the option definition specified for all servers. It should
+ // return the third one.
+ EXPECT_NO_THROW(
+ returned_option_def = cbptr_->getOptionDef6(ServerSelector::ALL(),
+ option3->getCode(),
+ option3->getOptionSpaceName())
+ );
+ ASSERT_TRUE(returned_option_def);
+ EXPECT_TRUE(returned_option_def->equals(*option3));
+
+ // Try to fetch the option definition specified for server1. It should
+ // override the definition for all servers.
+ EXPECT_NO_THROW(
+ returned_option_def = cbptr_->getOptionDef6(ServerSelector::ONE("server1"),
+ option1->getCode(),
+ option1->getOptionSpaceName())
+ );
+ ASSERT_TRUE(returned_option_def);
+ EXPECT_TRUE(returned_option_def->equals(*option1));
+
+ // The same in case of the server2.
+ EXPECT_NO_THROW(
+ returned_option_def = cbptr_->getOptionDef6(ServerSelector::ONE("server2"),
+ option2->getCode(),
+ option2->getOptionSpaceName())
+ );
+ ASSERT_TRUE(returned_option_def);
+ EXPECT_TRUE(returned_option_def->equals(*option2));
+
+ OptionDefContainer returned_option_defs;
+
+ // Try to fetch the collection of the option definitions for server1, server2
+ // and server3. The server3 does not have an explicit option definition, so
+ // for this server we should get the definition associated with "all" servers.
+ EXPECT_NO_THROW(
+ returned_option_defs = cbptr_->getAllOptionDefs6(ServerSelector::
+ MULTIPLE({ "server1", "server2",
+ "server3" }));
+ );
+ ASSERT_EQ(3, returned_option_defs.size());
+
+ // Check that expected option definitions have been returned.
+ auto current_option = returned_option_defs.begin();
+ EXPECT_TRUE((*current_option)->equals(*option1));
+ EXPECT_TRUE((*(++current_option))->equals(*option2));
+ EXPECT_TRUE((*(++current_option))->equals(*option3));
+
+ // Try to fetch the collection of options specified for all servers.
+ // This excludes the options specific to server1 and server2. It returns
+ // only the common ones.
+ EXPECT_NO_THROW(
+ returned_option_defs = cbptr_->getAllOptionDefs6(ServerSelector::ALL());
+
+ );
+ ASSERT_EQ(1, returned_option_defs.size());
+ EXPECT_TRUE((*returned_option_defs.begin())->equals(*option3));
+
+ // Delete the server1. It should remove associations of this server with the
+ // option definitions and the option definition itself.
+ EXPECT_NO_THROW(cbptr_->deleteServer6(ServerTag("server1")));
+ EXPECT_NO_THROW(
+ returned_option_defs = cbptr_->getAllOptionDefs6(ServerSelector::ONE("server1"));
+
+ );
+ ASSERT_EQ(1, returned_option_defs.size());
+ EXPECT_TRUE((*returned_option_defs.begin())->equals(*option3));
+
+ {
+ SCOPED_TRACE("DELETE audit entry for the option definition after server deletion");
+ testNewAuditEntry("dhcp6_option_def",
+ AuditEntry::ModificationType::DELETE,
+ "deleting a server", ServerSelector::ONE("server1"),
+ 2, 1);
+ }
+
+ // Attempt to delete option definition for server1.
+ uint64_t deleted_num = 0;
+ EXPECT_NO_THROW(deleted_num = cbptr_->deleteOptionDef6(ServerSelector::ONE("server1"),
+ option1->getCode(),
+ option1->getOptionSpaceName()));
+ EXPECT_EQ(0, deleted_num);
+
+ // Deleting the existing option definition for server2 should succeed.
+ EXPECT_NO_THROW(deleted_num = cbptr_->deleteOptionDef6(ServerSelector::ONE("server2"),
+ option2->getCode(),
+ option2->getOptionSpaceName()));
+ EXPECT_EQ(1, deleted_num);
+
+ // Create this option definition again to test that deletion of all servers
+ // removes it too.
+ EXPECT_NO_THROW(cbptr_->createUpdateOptionDef6(ServerSelector::ONE("server2"),
+ option2));
+
+ // Delete all servers, except 'all'.
+ EXPECT_NO_THROW(deleted_num = cbptr_->deleteAllServers6());
+ EXPECT_NO_THROW(
+ returned_option_defs = cbptr_->getAllOptionDefs6(ServerSelector::ALL());
+ );
+ EXPECT_EQ(1, deleted_num);
+ EXPECT_EQ(1, returned_option_defs.size());
+ EXPECT_TRUE((*returned_option_defs.begin())->equals(*option3));
+
+ {
+ SCOPED_TRACE("DELETE audit entry for the option definition after deletion of"
+ " all servers");
+ testNewAuditEntry("dhcp6_option_def",
+ AuditEntry::ModificationType::DELETE,
+ "deleting all servers", ServerSelector::ONE("server2"),
+ 4, 1);
+ }
+}
+
// Test that all option definitions can be fetched.
TEST_F(MySqlConfigBackendDHCPv6Test, getAllOptionDefs6) {
// Insert test option definitions into the database. Note that the second
// option definition will overwrite the first option definition as they use
// the same code and space.
+ size_t updates_num = 0;
for (auto option_def : test_option_defs_) {
cbptr_->createUpdateOptionDef6(ServerSelector::ALL(), option_def);
testNewAuditEntry("dhcp6_option_def",
AuditEntry::ModificationType::UPDATE,
"option definition set");
+ ++updates_num;
} else {
SCOPED_TRACE("CREATE audit entry for the option definition " +
// Fetch all option_definitions.
OptionDefContainer option_defs = cbptr_->getAllOptionDefs6(ServerSelector::ALL());
- ASSERT_EQ(test_option_defs_.size() - 1, option_defs.size());
+ ASSERT_EQ(test_option_defs_.size() - updates_num, option_defs.size());
// All option definitions should also be returned for explicitly specified
// server tag.
option_defs = cbptr_->getAllOptionDefs6(ServerSelector::ONE("server1"));
- ASSERT_EQ(test_option_defs_.size() - 1, option_defs.size());
+ ASSERT_EQ(test_option_defs_.size() - updates_num, option_defs.size());
// See if option definitions are returned ok.
for (auto def = option_defs.begin(); def != option_defs.end(); ++def) {
EXPECT_EQ(0, cbptr_->deleteOptionDef6(ServerSelector::ALL(),
99, "non-exiting-space"));
// All option definitions should be still there.
- ASSERT_EQ(test_option_defs_.size() - 1, option_defs.size());
+ ASSERT_EQ(test_option_defs_.size() - updates_num, option_defs.size());
// Should not delete option definition for explicit server tag
// because our option definition is for all servers.