mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-30 05:27:55 +00:00
[#714,!409] Associate global options with the server tags.
This commit is contained in:
parent
711c1dca9d
commit
ed7befb616
@ -115,6 +115,7 @@ public:
|
||||
DELETE_OPTION_DEF4_CODE_NAME,
|
||||
DELETE_ALL_OPTION_DEFS4,
|
||||
DELETE_OPTION4,
|
||||
DELETE_ALL_OPTIONS4_UNASSIGNED,
|
||||
DELETE_OPTION4_SUBNET_ID,
|
||||
DELETE_OPTION4_POOL_RANGE,
|
||||
DELETE_OPTION4_SHARED_NETWORK,
|
||||
@ -1441,7 +1442,10 @@ public:
|
||||
createInputContextBinding(option),
|
||||
MySqlBinding::createNull(),
|
||||
MySqlBinding::createNull(),
|
||||
MySqlBinding::createTimestamp(option->getModificationTime())
|
||||
MySqlBinding::createTimestamp(option->getModificationTime()),
|
||||
MySqlBinding::createString(tag),
|
||||
MySqlBinding::createInteger<uint8_t>(option->option_->getType()),
|
||||
MySqlBinding::condCreateString(option->space_name_)
|
||||
};
|
||||
|
||||
MySqlTransaction transaction(conn_);
|
||||
@ -1456,16 +1460,11 @@ public:
|
||||
MySqlConfigBackendDHCPv4Impl::CREATE_AUDIT_REVISION,
|
||||
server_selector, "global option set", false);
|
||||
|
||||
if (existing_option) {
|
||||
in_bindings.push_back(MySqlBinding::createString(tag));
|
||||
in_bindings.push_back(MySqlBinding::createInteger<uint8_t>(option->option_->getType()));
|
||||
in_bindings.push_back(MySqlBinding::condCreateString(option->space_name_));
|
||||
conn_.updateDeleteQuery(MySqlConfigBackendDHCPv4Impl::UPDATE_OPTION4,
|
||||
in_bindings);
|
||||
|
||||
} else {
|
||||
if (conn_.updateDeleteQuery(MySqlConfigBackendDHCPv4Impl::UPDATE_OPTION4,
|
||||
in_bindings) == 0) {
|
||||
// Remove the 3 bindings used only in case of update.
|
||||
in_bindings.resize(in_bindings.size() - 3);
|
||||
insertOption4(server_selector, in_bindings);
|
||||
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
@ -1931,7 +1930,11 @@ public:
|
||||
conn_.updateDeleteQuery(MySqlConfigBackendDHCPv4Impl::
|
||||
DELETE_ALL_GLOBAL_PARAMETERS4_UNASSIGNED,
|
||||
MySqlBindingCollection());
|
||||
/// @todo delete dangling options and option definitions.
|
||||
|
||||
conn_.updateDeleteQuery(MySqlConfigBackendDHCPv4Impl::
|
||||
DELETE_ALL_OPTIONS4_UNASSIGNED,
|
||||
MySqlBindingCollection());
|
||||
/// @todo delete option definitions.
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
@ -1967,7 +1970,12 @@ public:
|
||||
conn_.updateDeleteQuery(MySqlConfigBackendDHCPv4Impl::
|
||||
DELETE_ALL_GLOBAL_PARAMETERS4_UNASSIGNED,
|
||||
MySqlBindingCollection());
|
||||
/// @todo delete dangling options and option definitions.
|
||||
|
||||
conn_.updateDeleteQuery(MySqlConfigBackendDHCPv4Impl::
|
||||
DELETE_ALL_OPTIONS4_UNASSIGNED,
|
||||
MySqlBindingCollection());
|
||||
|
||||
/// @todo delete dangling option definitions.
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
@ -2396,6 +2404,11 @@ TaggedStatementArray tagged_statements = { {
|
||||
MYSQL_DELETE_OPTION(dhcp4, AND o.scope_id = 0 AND o.code = ? AND o.space = ?)
|
||||
},
|
||||
|
||||
// Delete all options which are unassigned to any servers.
|
||||
{ MySqlConfigBackendDHCPv4Impl::DELETE_ALL_OPTIONS4_UNASSIGNED,
|
||||
MYSQL_DELETE_OPTION_UNASSIGNED(dhcp4)
|
||||
},
|
||||
|
||||
// Delete single option from a subnet.
|
||||
{ MySqlConfigBackendDHCPv4Impl::DELETE_OPTION4_SUBNET_ID,
|
||||
MYSQL_DELETE_OPTION(dhcp4,
|
||||
|
@ -121,6 +121,7 @@ public:
|
||||
DELETE_OPTION_DEF6_CODE_NAME,
|
||||
DELETE_ALL_OPTION_DEFS6,
|
||||
DELETE_OPTION6,
|
||||
DELETE_ALL_OPTIONS6_UNASSIGNED,
|
||||
DELETE_OPTION6_SUBNET_ID,
|
||||
DELETE_OPTION6_POOL_RANGE,
|
||||
DELETE_OPTION6_PD_POOL,
|
||||
@ -1653,7 +1654,10 @@ public:
|
||||
MySqlBinding::createNull(),
|
||||
MySqlBinding::createNull(),
|
||||
MySqlBinding::createTimestamp(option->getModificationTime()),
|
||||
MySqlBinding::createNull()
|
||||
MySqlBinding::createNull(),
|
||||
MySqlBinding::createString(tag),
|
||||
MySqlBinding::createInteger<uint8_t>(option->option_->getType()),
|
||||
MySqlBinding::condCreateString(option->space_name_)
|
||||
};
|
||||
|
||||
MySqlTransaction transaction(conn_);
|
||||
@ -1668,16 +1672,11 @@ public:
|
||||
MySqlConfigBackendDHCPv6Impl::CREATE_AUDIT_REVISION,
|
||||
server_selector, "global option set", false);
|
||||
|
||||
if (existing_option) {
|
||||
in_bindings.push_back(MySqlBinding::createString(tag));
|
||||
in_bindings.push_back(MySqlBinding::createInteger<uint16_t>(option->option_->getType()));
|
||||
in_bindings.push_back(MySqlBinding::condCreateString(option->space_name_));
|
||||
conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::UPDATE_OPTION6,
|
||||
in_bindings);
|
||||
|
||||
} else {
|
||||
if (conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::UPDATE_OPTION6,
|
||||
in_bindings) == 0) {
|
||||
// Remove the 3 bindings used only in case of update.
|
||||
in_bindings.resize(in_bindings.size() - 3);
|
||||
insertOption6(server_selector, in_bindings);
|
||||
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
@ -2245,7 +2244,11 @@ public:
|
||||
conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::
|
||||
DELETE_ALL_GLOBAL_PARAMETERS6_UNASSIGNED,
|
||||
MySqlBindingCollection());
|
||||
/// @todo delete dangling options and option definitions.
|
||||
|
||||
conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::
|
||||
DELETE_ALL_OPTIONS6_UNASSIGNED,
|
||||
MySqlBindingCollection());
|
||||
/// @todo delete dangling option definitions.
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
@ -2281,7 +2284,11 @@ public:
|
||||
conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::
|
||||
DELETE_ALL_GLOBAL_PARAMETERS6_UNASSIGNED,
|
||||
MySqlBindingCollection());
|
||||
/// @todo delete dangling options and option definitions.
|
||||
|
||||
conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::
|
||||
DELETE_ALL_OPTIONS6_UNASSIGNED,
|
||||
MySqlBindingCollection());
|
||||
/// @todo delete dangling option definitions.
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
@ -2753,6 +2760,11 @@ TaggedStatementArray tagged_statements = { {
|
||||
MYSQL_DELETE_OPTION(dhcp6, AND o.scope_id = 0 AND o.code = ? AND o.space = ?)
|
||||
},
|
||||
|
||||
// Delete all options which are unassigned to any servers.
|
||||
{ MySqlConfigBackendDHCPv6Impl::DELETE_ALL_OPTIONS6_UNASSIGNED,
|
||||
MYSQL_DELETE_OPTION_UNASSIGNED(dhcp6)
|
||||
},
|
||||
|
||||
// Delete single option from a subnet.
|
||||
{ MySqlConfigBackendDHCPv6Impl::DELETE_OPTION6_SUBNET_ID,
|
||||
MYSQL_DELETE_OPTION(dhcp6,
|
||||
|
@ -762,8 +762,10 @@ MySqlConfigBackendImpl::getOptions(const int index,
|
||||
|
||||
uint64_t last_option_id = 0;
|
||||
|
||||
OptionContainer local_options;
|
||||
|
||||
conn_.selectQuery(index, in_bindings, out_bindings,
|
||||
[this, universe, &options, &last_option_id]
|
||||
[this, universe, &local_options, &last_option_id]
|
||||
(MySqlBindingCollection& out_bindings) {
|
||||
// Parse option.
|
||||
if (!out_bindings[0]->amNull() &&
|
||||
@ -774,11 +776,46 @@ MySqlConfigBackendImpl::getOptions(const int index,
|
||||
OptionDescriptorPtr desc = processOptionRow(universe, out_bindings.begin());
|
||||
if (desc) {
|
||||
// server_tag for the global option
|
||||
desc->setServerTag(out_bindings[12]->getString());
|
||||
static_cast<void>(options.push_back(*desc));
|
||||
ServerTag last_option_server_tag(out_bindings[12]->getString());
|
||||
desc->setServerTag(last_option_server_tag.get());
|
||||
|
||||
// If we're fetching options for a given server (explicit server
|
||||
// tag is provided), it takes precedence over the same option
|
||||
// specified for all servers. Therefore, we check if the given
|
||||
// option already exists and belongs to 'all'.
|
||||
auto& index = local_options.get<1>();
|
||||
auto existing_it_pair = index.equal_range(desc->option_->getType());
|
||||
auto existing_it = existing_it_pair.first;
|
||||
bool found = false;
|
||||
for ( ; existing_it != existing_it_pair.second; ++existing_it) {
|
||||
if (existing_it->space_name_ == desc->space_name_) {
|
||||
found = true;
|
||||
// This option was already fetched. Let's check if we should
|
||||
// replace it or not.
|
||||
if (!last_option_server_tag.amAll() && existing_it->hasAllServerTag()) {
|
||||
index.replace(existing_it, *desc);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no such global option yet or the existing option
|
||||
// belongs to a different server and the inserted option is not
|
||||
// for all servers.
|
||||
if (!found ||
|
||||
(!existing_it->hasServerTag(last_option_server_tag) &&
|
||||
!last_option_server_tag.amAll())) {
|
||||
static_cast<void>(local_options.push_back(*desc));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Append the options fetched by this function into the container supplied
|
||||
// by the caller. The container supplied by the caller may already hold
|
||||
// some options fetched for other server tags.
|
||||
options.insert(options.end(), local_options.begin(), local_options.end());
|
||||
}
|
||||
|
||||
OptionDescriptorPtr
|
||||
|
@ -353,7 +353,7 @@ namespace {
|
||||
"INNER JOIN " #table_prefix "_server AS s" \
|
||||
" ON a.server_id = s.id " \
|
||||
"WHERE (s.tag = ? OR s.id = 1) " #__VA_ARGS__ \
|
||||
" ORDER BY o.option_id"
|
||||
" ORDER BY o.option_id, s.id"
|
||||
|
||||
#define MYSQL_GET_OPTION4(...) \
|
||||
MYSQL_GET_OPTION_COMMON(dhcp4, "", __VA_ARGS__)
|
||||
@ -667,6 +667,14 @@ namespace {
|
||||
"WHERE s.tag = ? " #__VA_ARGS__
|
||||
#endif
|
||||
|
||||
#ifndef MYSQL_DELETE_OPTION_UNASSIGNED
|
||||
#define MYSQL_DELETE_OPTION_UNASSIGNED(table_prefix, ...) \
|
||||
"DELETE o FROM " #table_prefix "_options AS o " \
|
||||
"LEFT JOIN " #table_prefix "_options_server AS a " \
|
||||
" ON o.option_id = a.option_id " \
|
||||
"WHERE a.option_id IS NULL " #__VA_ARGS__
|
||||
#endif
|
||||
|
||||
#ifndef MYSQL_DELETE_OPTION_POOL_RANGE
|
||||
#define MYSQL_DELETE_OPTION_POOL_RANGE(table_prefix, ...) \
|
||||
"DELETE o FROM " #table_prefix "_options AS o " \
|
||||
|
@ -302,6 +302,18 @@ public:
|
||||
desc.space_name_ = "isc";
|
||||
test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc)));
|
||||
|
||||
desc = createOption<OptionString>(Option::V4, DHO_BOOT_FILE_NAME,
|
||||
true, false, "my-boot-file-2");
|
||||
desc.space_name_ = DHCP4_OPTION_SPACE;
|
||||
desc.setContext(user_context);
|
||||
test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc)));
|
||||
|
||||
desc = createOption<OptionString>(Option::V4, DHO_BOOT_FILE_NAME,
|
||||
true, false, "my-boot-file-3");
|
||||
desc.space_name_ = DHCP4_OPTION_SPACE;
|
||||
desc.setContext(user_context);
|
||||
test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc)));
|
||||
|
||||
// Add definitions for DHCPv4 non-standard options in case we need to
|
||||
// compare subnets, networks and pools in JSON format. In that case,
|
||||
// the @c toElement functions require option definitions to generate the
|
||||
@ -1931,8 +1943,9 @@ TEST_F(MySqlConfigBackendDHCPv4Test, createUpdateDeleteOption4) {
|
||||
opt_boot_file_name);
|
||||
|
||||
// Retrieve the option again and make sure that updates were
|
||||
// properly propagated to the database.
|
||||
returned_opt_boot_file_name = cbptr_->getOption4(ServerSelector::ALL(),
|
||||
// properly propagated to the database. Use explicit server selector
|
||||
// which should also return this option.
|
||||
returned_opt_boot_file_name = cbptr_->getOption4(ServerSelector::ONE("server1"),
|
||||
opt_boot_file_name->option_->getType(),
|
||||
opt_boot_file_name->space_name_);
|
||||
ASSERT_TRUE(returned_opt_boot_file_name);
|
||||
@ -1971,6 +1984,189 @@ TEST_F(MySqlConfigBackendDHCPv4Test, createUpdateDeleteOption4) {
|
||||
}
|
||||
}
|
||||
|
||||
// This test verifies that it is possible to differentiate between the
|
||||
// global options by server tag and that the option specified for the
|
||||
// particular server overrides the value specified for all servers.
|
||||
TEST_F(MySqlConfigBackendDHCPv4Test, globalOptions4WithServerTags) {
|
||||
OptionDescriptorPtr opt_boot_file_name1 = test_options_[0];
|
||||
OptionDescriptorPtr opt_boot_file_name2 = test_options_[6];
|
||||
OptionDescriptorPtr opt_boot_file_name3 = test_options_[7];
|
||||
|
||||
EXPECT_THROW(cbptr_->createUpdateOption4(ServerSelector::ONE("server1"),
|
||||
opt_boot_file_name1),
|
||||
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");
|
||||
}
|
||||
|
||||
EXPECT_NO_THROW(cbptr_->createUpdateOption4(ServerSelector::ONE("server1"),
|
||||
opt_boot_file_name1));
|
||||
{
|
||||
SCOPED_TRACE("global option 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 global option.
|
||||
testNewAuditEntry("dhcp4_options",
|
||||
AuditEntry::ModificationType::CREATE,
|
||||
"global option set",
|
||||
ServerSelector::ONE("server1"),
|
||||
3, 1);
|
||||
|
||||
}
|
||||
|
||||
EXPECT_NO_THROW(cbptr_->createUpdateOption4(ServerSelector::ONE("server2"),
|
||||
opt_boot_file_name2));
|
||||
{
|
||||
SCOPED_TRACE("global option 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_options",
|
||||
AuditEntry::ModificationType::CREATE,
|
||||
"global option set",
|
||||
ServerSelector::ONE("server2"),
|
||||
3, 1);
|
||||
|
||||
}
|
||||
|
||||
EXPECT_NO_THROW(cbptr_->createUpdateOption4(ServerSelector::ALL(),
|
||||
opt_boot_file_name3));
|
||||
{
|
||||
SCOPED_TRACE("global option for all servers is set");
|
||||
// There should be one new audit entry for all servers. It logs
|
||||
// the insertion of the global option.
|
||||
testNewAuditEntry("dhcp4_options",
|
||||
AuditEntry::ModificationType::CREATE,
|
||||
"global option set",
|
||||
ServerSelector::ALL(),
|
||||
1, 1);
|
||||
|
||||
}
|
||||
|
||||
OptionDescriptorPtr returned_option;
|
||||
|
||||
// Try to fetch the option specified for all servers. It should return
|
||||
// the third option.
|
||||
EXPECT_NO_THROW(
|
||||
returned_option = cbptr_->getOption4(ServerSelector::ALL(),
|
||||
opt_boot_file_name3->option_->getType(),
|
||||
opt_boot_file_name3->space_name_);
|
||||
);
|
||||
ASSERT_TRUE(returned_option);
|
||||
testOptionsEquivalent(*opt_boot_file_name3, *returned_option);
|
||||
|
||||
// Try to fetch the option specified for the server1. It should override the
|
||||
// option specified for all servers.
|
||||
EXPECT_NO_THROW(
|
||||
returned_option = cbptr_->getOption4(ServerSelector::ONE("server1"),
|
||||
opt_boot_file_name1->option_->getType(),
|
||||
opt_boot_file_name1->space_name_);
|
||||
);
|
||||
ASSERT_TRUE(returned_option);
|
||||
testOptionsEquivalent(*opt_boot_file_name1, *returned_option);
|
||||
|
||||
// The same in case of the server2.
|
||||
EXPECT_NO_THROW(
|
||||
returned_option = cbptr_->getOption4(ServerSelector::ONE("server2"),
|
||||
opt_boot_file_name2->option_->getType(),
|
||||
opt_boot_file_name2->space_name_);
|
||||
);
|
||||
ASSERT_TRUE(returned_option);
|
||||
testOptionsEquivalent(*opt_boot_file_name2, *returned_option);
|
||||
|
||||
OptionContainer returned_options;
|
||||
|
||||
// Try to fetch the collection of global options for the server1, server2
|
||||
// and server3. The server3 does not have an explicit value so for this server
|
||||
// we should get the option associated with "all" servers.
|
||||
EXPECT_NO_THROW(
|
||||
returned_options = cbptr_->getAllOptions4(ServerSelector::
|
||||
MULTIPLE({ "server1", "server2",
|
||||
"server3" }));
|
||||
);
|
||||
ASSERT_EQ(3, returned_options.size());
|
||||
|
||||
// Check that expected options have been returned.
|
||||
auto current_option = returned_options.begin();
|
||||
testOptionsEquivalent(*opt_boot_file_name1, *current_option);
|
||||
testOptionsEquivalent(*opt_boot_file_name2, *(++current_option));
|
||||
testOptionsEquivalent(*opt_boot_file_name3, *(++current_option));
|
||||
|
||||
// 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_options = cbptr_->getAllOptions4(ServerSelector::ALL());
|
||||
);
|
||||
ASSERT_EQ(1, returned_options.size());
|
||||
testOptionsEquivalent(*opt_boot_file_name3, *returned_options.begin());
|
||||
|
||||
// Delete the server1. It should remove associations of this server with the
|
||||
// option and the option itself.
|
||||
EXPECT_NO_THROW(cbptr_->deleteServer4(ServerTag("server1")));
|
||||
EXPECT_NO_THROW(
|
||||
returned_options = cbptr_->getAllOptions4(ServerSelector::ONE("server1"));
|
||||
);
|
||||
ASSERT_EQ(1, returned_options.size());
|
||||
testOptionsEquivalent(*opt_boot_file_name3, *returned_options.begin());
|
||||
|
||||
{
|
||||
SCOPED_TRACE("DELETE audit entry for the global option after server deletion");
|
||||
testNewAuditEntry("dhcp4_options",
|
||||
AuditEntry::ModificationType::DELETE,
|
||||
"deleting a server", ServerSelector::ONE("server1"),
|
||||
2, 1);
|
||||
}
|
||||
|
||||
// Attempt to delete global option for server1.
|
||||
uint64_t deleted_num = 0;
|
||||
EXPECT_NO_THROW(deleted_num = cbptr_->deleteOption4(ServerSelector::ONE("server1"),
|
||||
opt_boot_file_name1->option_->getType(),
|
||||
opt_boot_file_name1->space_name_));
|
||||
EXPECT_EQ(0, deleted_num);
|
||||
|
||||
// Deleting the existing option for server2 should succeed.
|
||||
EXPECT_NO_THROW(deleted_num = cbptr_->deleteOption4(ServerSelector::ONE("server2"),
|
||||
opt_boot_file_name2->option_->getType(),
|
||||
opt_boot_file_name2->space_name_));
|
||||
EXPECT_EQ(1, deleted_num);
|
||||
|
||||
// Create this option again to test that deletion of all servers removes it too.
|
||||
EXPECT_NO_THROW(cbptr_->createUpdateOption4(ServerSelector::ONE("server2"),
|
||||
opt_boot_file_name2));
|
||||
|
||||
// Delete all servers, except 'all'.
|
||||
EXPECT_NO_THROW(deleted_num = cbptr_->deleteAllServers4());
|
||||
EXPECT_NO_THROW(
|
||||
returned_options = cbptr_->getAllOptions4(ServerSelector::ALL());
|
||||
);
|
||||
EXPECT_EQ(1, deleted_num);
|
||||
ASSERT_EQ(1, returned_options.size());
|
||||
testOptionsEquivalent(*opt_boot_file_name3, *returned_options.begin());
|
||||
|
||||
{
|
||||
SCOPED_TRACE("DELETE audit entry for the global option after deletion of"
|
||||
" all servers");
|
||||
testNewAuditEntry("dhcp4_options",
|
||||
AuditEntry::ModificationType::DELETE,
|
||||
"deleting all servers", ServerSelector::ONE("server2"),
|
||||
4, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// This test verifies that all global options can be retrieved.
|
||||
TEST_F(MySqlConfigBackendDHCPv4Test, getAllOptions4) {
|
||||
// Add three global options to the database.
|
||||
|
@ -343,6 +343,18 @@ public:
|
||||
desc.space_name_ = "isc";
|
||||
test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc)));
|
||||
|
||||
desc = createOption<OptionString>(Option::V6, D6O_NEW_POSIX_TIMEZONE,
|
||||
true, false, "my-timezone-2");
|
||||
desc.space_name_ = DHCP6_OPTION_SPACE;
|
||||
desc.setContext(user_context);
|
||||
test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc)));
|
||||
|
||||
desc = createOption<OptionString>(Option::V6, D6O_NEW_POSIX_TIMEZONE,
|
||||
true, false, "my-timezone-3");
|
||||
desc.space_name_ = DHCP6_OPTION_SPACE;
|
||||
desc.setContext(user_context);
|
||||
test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc)));
|
||||
|
||||
// Add definitions for DHCPv6 non-standard options in case we need to
|
||||
// compare subnets, networks and pools in JSON format. In that case,
|
||||
// the @c toElement functions require option definitions to generate the
|
||||
@ -1953,8 +1965,9 @@ TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateDeleteOption6) {
|
||||
opt_posix_timezone);
|
||||
|
||||
// Retrieve the option again and make sure that updates were
|
||||
// properly propagated to the database.
|
||||
returned_opt_posix_timezone = cbptr_->getOption6(ServerSelector::ALL(),
|
||||
// properly propagated to the database. Use explicit server selector
|
||||
// which should also return this option.
|
||||
returned_opt_posix_timezone = cbptr_->getOption6(ServerSelector::ONE("server1"),
|
||||
opt_posix_timezone->option_->getType(),
|
||||
opt_posix_timezone->space_name_);
|
||||
ASSERT_TRUE(returned_opt_posix_timezone);
|
||||
@ -1994,6 +2007,189 @@ TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateDeleteOption6) {
|
||||
}
|
||||
}
|
||||
|
||||
// This test verifies that it is possible to differentiate between the
|
||||
// global options by server tag and that the option specified for the
|
||||
// particular server overrides the value specified for all servers.
|
||||
TEST_F(MySqlConfigBackendDHCPv6Test, globalOptions6WithServerTags) {
|
||||
OptionDescriptorPtr opt_timezone1 = test_options_[0];
|
||||
OptionDescriptorPtr opt_timezone2 = test_options_[6];
|
||||
OptionDescriptorPtr opt_timezone3 = test_options_[7];
|
||||
|
||||
EXPECT_THROW(cbptr_->createUpdateOption6(ServerSelector::ONE("server1"),
|
||||
opt_timezone1),
|
||||
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");
|
||||
}
|
||||
|
||||
EXPECT_NO_THROW(cbptr_->createUpdateOption6(ServerSelector::ONE("server1"),
|
||||
opt_timezone1));
|
||||
{
|
||||
SCOPED_TRACE("global option 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 global option.
|
||||
testNewAuditEntry("dhcp6_options",
|
||||
AuditEntry::ModificationType::CREATE,
|
||||
"global option set",
|
||||
ServerSelector::ONE("server1"),
|
||||
3, 1);
|
||||
|
||||
}
|
||||
|
||||
EXPECT_NO_THROW(cbptr_->createUpdateOption6(ServerSelector::ONE("server2"),
|
||||
opt_timezone2));
|
||||
{
|
||||
SCOPED_TRACE("global option 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_options",
|
||||
AuditEntry::ModificationType::CREATE,
|
||||
"global option set",
|
||||
ServerSelector::ONE("server2"),
|
||||
3, 1);
|
||||
|
||||
}
|
||||
|
||||
EXPECT_NO_THROW(cbptr_->createUpdateOption6(ServerSelector::ALL(),
|
||||
opt_timezone3));
|
||||
{
|
||||
SCOPED_TRACE("global option for all servers is set");
|
||||
// There should be one new audit entry for all servers. It logs
|
||||
// the insertion of the global option.
|
||||
testNewAuditEntry("dhcp6_options",
|
||||
AuditEntry::ModificationType::CREATE,
|
||||
"global option set",
|
||||
ServerSelector::ALL(),
|
||||
1, 1);
|
||||
|
||||
}
|
||||
|
||||
OptionDescriptorPtr returned_option;
|
||||
|
||||
// Try to fetch the option specified for all servers. It should return
|
||||
// the third option.
|
||||
EXPECT_NO_THROW(
|
||||
returned_option = cbptr_->getOption6(ServerSelector::ALL(),
|
||||
opt_timezone3->option_->getType(),
|
||||
opt_timezone3->space_name_);
|
||||
);
|
||||
ASSERT_TRUE(returned_option);
|
||||
testOptionsEquivalent(*opt_timezone3, *returned_option);
|
||||
|
||||
// Try to fetch the option specified for the server1. It should override the
|
||||
// option specified for all servers.
|
||||
EXPECT_NO_THROW(
|
||||
returned_option = cbptr_->getOption6(ServerSelector::ONE("server1"),
|
||||
opt_timezone1->option_->getType(),
|
||||
opt_timezone1->space_name_);
|
||||
);
|
||||
ASSERT_TRUE(returned_option);
|
||||
testOptionsEquivalent(*opt_timezone1, *returned_option);
|
||||
|
||||
// The same in case of the server2.
|
||||
EXPECT_NO_THROW(
|
||||
returned_option = cbptr_->getOption6(ServerSelector::ONE("server2"),
|
||||
opt_timezone2->option_->getType(),
|
||||
opt_timezone2->space_name_);
|
||||
);
|
||||
ASSERT_TRUE(returned_option);
|
||||
testOptionsEquivalent(*opt_timezone2, *returned_option);
|
||||
|
||||
OptionContainer returned_options;
|
||||
|
||||
// Try to fetch the collection of global options for the server1, server2
|
||||
// and server3. The server3 does not have an explicit value so for this server
|
||||
// we should get the option associated with "all" servers.
|
||||
EXPECT_NO_THROW(
|
||||
returned_options = cbptr_->getAllOptions6(ServerSelector::
|
||||
MULTIPLE({ "server1", "server2",
|
||||
"server3" }));
|
||||
);
|
||||
ASSERT_EQ(3, returned_options.size());
|
||||
|
||||
// Check that expected options have been returned.
|
||||
auto current_option = returned_options.begin();
|
||||
testOptionsEquivalent(*opt_timezone1, *current_option);
|
||||
testOptionsEquivalent(*opt_timezone2, *(++current_option));
|
||||
testOptionsEquivalent(*opt_timezone3, *(++current_option));
|
||||
|
||||
// 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_options = cbptr_->getAllOptions6(ServerSelector::ALL());
|
||||
);
|
||||
ASSERT_EQ(1, returned_options.size());
|
||||
testOptionsEquivalent(*opt_timezone3, *returned_options.begin());
|
||||
|
||||
// Delete the server1. It should remove associations of this server with the
|
||||
// option and the option itself.
|
||||
EXPECT_NO_THROW(cbptr_->deleteServer6(ServerTag("server1")));
|
||||
EXPECT_NO_THROW(
|
||||
returned_options = cbptr_->getAllOptions6(ServerSelector::ONE("server1"));
|
||||
);
|
||||
ASSERT_EQ(1, returned_options.size());
|
||||
testOptionsEquivalent(*opt_timezone3, *returned_options.begin());
|
||||
|
||||
{
|
||||
SCOPED_TRACE("DELETE audit entry for the global option after server deletion");
|
||||
testNewAuditEntry("dhcp6_options",
|
||||
AuditEntry::ModificationType::DELETE,
|
||||
"deleting a server", ServerSelector::ONE("server1"),
|
||||
2, 1);
|
||||
}
|
||||
|
||||
// Attempt to delete global option for server1.
|
||||
uint64_t deleted_num = 0;
|
||||
EXPECT_NO_THROW(deleted_num = cbptr_->deleteOption6(ServerSelector::ONE("server1"),
|
||||
opt_timezone1->option_->getType(),
|
||||
opt_timezone1->space_name_));
|
||||
EXPECT_EQ(0, deleted_num);
|
||||
|
||||
// Deleting the existing option for server2 should succeed.
|
||||
EXPECT_NO_THROW(deleted_num = cbptr_->deleteOption6(ServerSelector::ONE("server2"),
|
||||
opt_timezone2->option_->getType(),
|
||||
opt_timezone2->space_name_));
|
||||
EXPECT_EQ(1, deleted_num);
|
||||
|
||||
// Create this option again to test that deletion of all servers removes it too.
|
||||
EXPECT_NO_THROW(cbptr_->createUpdateOption6(ServerSelector::ONE("server2"),
|
||||
opt_timezone2));
|
||||
|
||||
// Delete all servers, except 'all'.
|
||||
EXPECT_NO_THROW(deleted_num = cbptr_->deleteAllServers6());
|
||||
EXPECT_NO_THROW(
|
||||
returned_options = cbptr_->getAllOptions6(ServerSelector::ALL());
|
||||
);
|
||||
EXPECT_EQ(1, deleted_num);
|
||||
ASSERT_EQ(1, returned_options.size());
|
||||
testOptionsEquivalent(*opt_timezone3, *returned_options.begin());
|
||||
|
||||
{
|
||||
SCOPED_TRACE("DELETE audit entry for the global option after deletion of"
|
||||
" all servers");
|
||||
testNewAuditEntry("dhcp6_options",
|
||||
AuditEntry::ModificationType::DELETE,
|
||||
"deleting all servers", ServerSelector::ONE("server2"),
|
||||
4, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// This test verifies that all global options can be retrieved.
|
||||
TEST_F(MySqlConfigBackendDHCPv6Test, getAllOptions6) {
|
||||
// Add three global options to the database.
|
||||
|
Loading…
x
Reference in New Issue
Block a user