Commit a4791908 authored by Marcin Siodelski's avatar Marcin Siodelski

[#717] Associate subnets with server tags.

parent e121ec4e
This diff is collapsed.
This diff is collapsed.
......@@ -49,7 +49,7 @@ namespace {
#endif
#ifndef MYSQL_GET_SUBNET4
#define MYSQL_GET_SUBNET4(...) \
#define MYSQL_GET_SUBNET4_COMMON(server_join, ...) \
"SELECT" \
" s.subnet_id," \
" s.subnet_prefix," \
......@@ -108,19 +108,41 @@ namespace {
" s.max_valid_lifetime," \
" srv.tag " \
"FROM dhcp4_subnet AS s " \
"INNER JOIN dhcp4_subnet_server AS a " \
" ON s.subnet_id = a.subnet_id " \
"INNER JOIN dhcp4_server AS srv " \
" ON (a.server_id = srv.id) OR (a.server_id = 1) " \
server_join \
"LEFT JOIN dhcp4_pool AS p ON s.subnet_id = p.subnet_id " \
"LEFT JOIN dhcp4_options AS x ON x.scope_id = 5 AND p.id = x.pool_id " \
"LEFT JOIN dhcp4_options AS o ON o.scope_id = 1 AND s.subnet_id = o.dhcp4_subnet_id " \
"WHERE (srv.tag = ? OR srv.id = 1) " #__VA_ARGS__ \
#__VA_ARGS__ \
" ORDER BY s.subnet_id, p.id, x.option_id, o.option_id"
#define MYSQL_GET_SUBNET4_NO_TAG(...) \
MYSQL_GET_SUBNET4_COMMON( \
"INNER JOIN dhcp4_subnet_server AS a " \
" ON s.subnet_id = a.subnet_id " \
"INNER JOIN dhcp4_server AS srv " \
" ON (a.server_id = srv.id) ", \
__VA_ARGS__)
#define MYSQL_GET_SUBNET4_ANY(...) \
MYSQL_GET_SUBNET4_COMMON( \
"LEFT JOIN dhcp4_subnet_server AS a "\
" ON s.subnet_id = a.subnet_id " \
"LEFT JOIN dhcp4_server AS srv " \
" ON a.server_id = srv.id ", \
__VA_ARGS__)
#define MYSQL_GET_SUBNET4_UNASSIGNED(...) \
MYSQL_GET_SUBNET4_COMMON( \
"LEFT JOIN dhcp4_subnet_server AS a "\
" ON s.subnet_id = a.subnet_id " \
"LEFT JOIN dhcp4_server AS srv " \
" ON a.server_id = srv.id ", \
WHERE a.subnet_id IS NULL __VA_ARGS__)
#endif
#ifndef MYSQL_GET_SUBNET6
#define MYSQL_GET_SUBNET6(...) \
#define MYSQL_GET_SUBNET6_COMMON(server_join, ...) \
"SELECT" \
" s.subnet_id," \
" s.subnet_prefix," \
......@@ -197,17 +219,39 @@ namespace {
" s.max_valid_lifetime," \
" srv.tag " \
"FROM dhcp6_subnet AS s " \
"INNER JOIN dhcp6_subnet_server AS a " \
" ON s.subnet_id = a.subnet_id " \
"INNER JOIN dhcp6_server AS srv " \
" ON (a.server_id = srv.id) OR (a.server_id = 1) " \
server_join \
"LEFT JOIN dhcp6_pool AS p ON s.subnet_id = p.subnet_id " \
"LEFT JOIN dhcp6_pd_pool AS d ON s.subnet_id = d.subnet_id " \
"LEFT JOIN dhcp6_options AS x ON x.scope_id = 5 AND p.id = x.pool_id " \
"LEFT JOIN dhcp6_options AS y ON y.scope_id = 6 AND d.id = y.pd_pool_id " \
"LEFT JOIN dhcp6_options AS o ON o.scope_id = 1 AND s.subnet_id = o.dhcp6_subnet_id " \
"WHERE (srv.tag = ? OR srv.id = 1) " #__VA_ARGS__ \
#__VA_ARGS__ \
" ORDER BY s.subnet_id, p.id, d.id, x.option_id, o.option_id"
#define MYSQL_GET_SUBNET6_NO_TAG(...) \
MYSQL_GET_SUBNET6_COMMON( \
"INNER JOIN dhcp6_subnet_server AS a " \
" ON s.subnet_id = a.subnet_id " \
"INNER JOIN dhcp6_server AS srv " \
" ON (a.server_id = srv.id) ", \
__VA_ARGS__)
#define MYSQL_GET_SUBNET6_ANY(...) \
MYSQL_GET_SUBNET6_COMMON( \
"LEFT JOIN dhcp6_subnet_server AS a "\
" ON s.subnet_id = a.subnet_id " \
"LEFT JOIN dhcp6_server AS srv " \
" ON a.server_id = srv.id ", \
__VA_ARGS__)
#define MYSQL_GET_SUBNET6_UNASSIGNED(...) \
MYSQL_GET_SUBNET6_COMMON( \
"LEFT JOIN dhcp6_subnet_server AS a "\
" ON s.subnet_id = a.subnet_id " \
"LEFT JOIN dhcp6_server AS srv " \
" ON a.server_id = srv.id ", \
WHERE a.subnet_id IS NULL __VA_ARGS__)
#endif
#ifndef MYSQL_GET_SHARED_NETWORK4
......@@ -676,6 +720,12 @@ namespace {
"WHERE srv.tag = ? " #__VA_ARGS__
#endif
#ifndef MYSQL_DELETE_SUBNET_SERVER
#define MYSQL_DELETE_SUBNET_SERVER(table_prefix) \
"DELETE FROM " #table_prefix "_subnet_server " \
"WHERE subnet_id = ?"
#endif
#ifndef MYSQL_DELETE_POOLS
#define MYSQL_DELETE_POOLS(table_prefix) \
"DELETE FROM " #table_prefix "_pool " \
......
......@@ -1005,21 +1005,20 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getModifiedGlobalParameters4) {
// Test that subnet can be inserted, fetched, updated and then fetched again.
TEST_F(MySqlConfigBackendDHCPv4Test, getSubnet4) {
// Insert new subnet.
Subnet4Ptr subnet = test_subnets_[0];
cbptr_->createUpdateSubnet4(ServerSelector::ALL(), subnet);
// Fetch this subnet by subnet identifier.
Subnet4Ptr returned_subnet = cbptr_->getSubnet4(ServerSelector::ALL(),
test_subnets_[0]->getID());
ASSERT_TRUE(returned_subnet);
ASSERT_EQ(1, returned_subnet->getServerTags().size());
EXPECT_EQ("all", returned_subnet->getServerTags().begin()->get());
// Insert the server2 into the database.
EXPECT_NO_THROW(cbptr_->createUpdateServer4(test_servers_[2]));
{
SCOPED_TRACE("CREATE audit entry for server");
testNewAuditEntry("dhcp4_server",
AuditEntry::ModificationType::CREATE,
"server set");
}
// The easiest way to verify whether the returned subnet matches the inserted
// subnet is to convert both to text.
EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
auto subnet = test_subnets_[0];
auto subnet2 = test_subnets_[2];
// Insert two subnets, one for all servers and one for server2.
EXPECT_NO_THROW(cbptr_->createUpdateSubnet4(ServerSelector::ALL(), subnet));
{
SCOPED_TRACE("CREATE audit entry for the subnet");
testNewAuditEntry("dhcp4_subnet",
......@@ -1027,44 +1026,97 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getSubnet4) {
"subnet set");
}
// Update the subnet in the database (both use the same ID).
Subnet4Ptr subnet2 = test_subnets_[1];
cbptr_->createUpdateSubnet4(ServerSelector::ALL(), subnet2);
// Fetch updated subnet and see if it matches.
returned_subnet = cbptr_->getSubnet4(ServerSelector::ALL(),
SubnetID(1024));
EXPECT_EQ(subnet2->toElement()->str(), returned_subnet->toElement()->str());
EXPECT_NO_THROW(cbptr_->createUpdateSubnet4(ServerSelector::ONE("server2"), subnet2));
{
SCOPED_TRACE("CREATE audit entry for the subnet");
testNewAuditEntry("dhcp4_subnet",
AuditEntry::ModificationType::CREATE,
"subnet set", ServerSelector::ONE("subnet2"),
2, 1);
}
// Fetching the subnet for an explicitly specified server tag should
// succeed too.
returned_subnet = cbptr_->getSubnet4(ServerSelector::ONE("server1"),
SubnetID(1024));
EXPECT_EQ(subnet2->toElement()->str(), returned_subnet->toElement()->str());
// We are not going to support selection of a single entry for multiple servers.
EXPECT_THROW(cbptr_->getSubnet4(ServerSelector::MULTIPLE({ "server1", "server2" }),
subnet->getID()),
isc::InvalidOperation);
EXPECT_THROW(cbptr_->getSubnet4(ServerSelector::MULTIPLE({ "server1", "server2" }),
subnet->toText()),
isc::InvalidOperation);
// Test that this subnet will be fetched for various server selectors.
auto test_get_subnet = [this, &subnet] (const std::string& test_case_name,
const ServerSelector& server_selector,
const std::string& expected_tag = ServerTag::ALL) {
SCOPED_TRACE(test_case_name);
// Test fetching subnet by id.
Subnet4Ptr returned_subnet;
ASSERT_NO_THROW(returned_subnet = cbptr_->getSubnet4(server_selector, subnet->getID()));
ASSERT_TRUE(returned_subnet);
ASSERT_EQ(1, returned_subnet->getServerTags().size());
EXPECT_TRUE(returned_subnet->hasServerTag(ServerTag(expected_tag)));
EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
// Test fetching subnet by prefix.
ASSERT_NO_THROW(returned_subnet = cbptr_->getSubnet4(server_selector,
subnet->toText()));
ASSERT_TRUE(returned_subnet);
ASSERT_EQ(1, returned_subnet->getServerTags().size());
EXPECT_TRUE(returned_subnet->hasServerTag(ServerTag(expected_tag)));
EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
};
{
SCOPED_TRACE("UPDATE audit entry for the subnet");
SCOPED_TRACE("testing various server selectors before update");
test_get_subnet("all servers", ServerSelector::ALL());
test_get_subnet("one server", ServerSelector::ONE("server1"));
test_get_subnet("any server", ServerSelector::ANY());
}
subnet = subnet2;
{
SCOPED_TRACE("testing server selectors for another server");
test_get_subnet("one server", ServerSelector::ONE("server2"), "server2");
test_get_subnet("any server", ServerSelector::ANY(), "server2");
}
// Update the subnet in the database (both use the same ID).
subnet = test_subnets_[1];
EXPECT_NO_THROW(cbptr_->createUpdateSubnet4(ServerSelector::ALL(), subnet));
{
SCOPED_TRACE("CREATE audit entry for the subnet");
testNewAuditEntry("dhcp4_subnet",
AuditEntry::ModificationType::UPDATE,
"subnet set");
}
// Insert another subnet.
cbptr_->createUpdateSubnet4(ServerSelector::ALL(), test_subnets_[2]);
{
SCOPED_TRACE("testing various server selectors after update");
test_get_subnet("all servers", ServerSelector::ALL());
test_get_subnet("one server", ServerSelector::ONE("server1"));
test_get_subnet("any server", ServerSelector::ANY());
}
// Fetch this subnet by prefix and verify it matches.
returned_subnet = cbptr_->getSubnet4(ServerSelector::ALL(),
test_subnets_[2]->toText());
ASSERT_TRUE(returned_subnet);
EXPECT_EQ(test_subnets_[2]->toElement()->str(), returned_subnet->toElement()->str());
// The server2 specific subnet should not be returned if the server selector
// is not matching.
EXPECT_FALSE(cbptr_->getSubnet4(ServerSelector::ALL(), subnet2->getID()));
EXPECT_FALSE(cbptr_->getSubnet4(ServerSelector::ALL(), subnet2->toText()));
EXPECT_FALSE(cbptr_->getSubnet4(ServerSelector::ONE("server1"), subnet2->getID()));
EXPECT_FALSE(cbptr_->getSubnet4(ServerSelector::ONE("server1"), subnet2->toText()));
// Update the subnet in the database (both use the same prefix).
subnet2.reset(new Subnet4(IOAddress("192.0.3.0"), 24, 30, 40, 60, 8192));
cbptr_->createUpdateSubnet4(ServerSelector::ALL(), subnet2);
EXPECT_NO_THROW(cbptr_->createUpdateSubnet4(ServerSelector::ONE("server2"), subnet2));
// Fetch again and verify.
returned_subnet = cbptr_->getSubnet4(ServerSelector::ALL(),
test_subnets_[2]->toText());
auto returned_subnet = cbptr_->getSubnet4(ServerSelector::ONE("server2"),
subnet2->toText());
ASSERT_TRUE(returned_subnet);
EXPECT_EQ(subnet2->toElement()->str(), returned_subnet->toElement()->str());
......@@ -1072,7 +1124,7 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getSubnet4) {
// with different subnets. This should throw.
// Subnets are 10.0.0.0/8 id 1024 and 192.0.3.0/24 id 8192
subnet2.reset(new Subnet4(IOAddress("10.0.0.0"), 8, 30, 40, 60, 8192));
EXPECT_THROW(cbptr_->createUpdateSubnet4(ServerSelector::ALL(), subnet2),
EXPECT_THROW(cbptr_->createUpdateSubnet4(ServerSelector::ONE("server2"), subnet2),
DuplicateEntry);
}
......
......@@ -1037,21 +1037,20 @@ TEST_F(MySqlConfigBackendDHCPv6Test, getModifiedGlobalParameters6) {
// Test that subnet can be inserted, fetched, updated and then fetched again.
TEST_F(MySqlConfigBackendDHCPv6Test, getSubnet6) {
// Insert new subnet.
Subnet6Ptr subnet = test_subnets_[0];
cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet);
// Fetch this subnet by subnet identifier.
Subnet6Ptr returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
test_subnets_[0]->getID());
ASSERT_TRUE(returned_subnet);
ASSERT_EQ(1, returned_subnet->getServerTags().size());
EXPECT_EQ("all", returned_subnet->getServerTags().begin()->get());
// Insert the server2 into the database.
EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[2]));
{
SCOPED_TRACE("CREATE audit entry for server");
testNewAuditEntry("dhcp6_server",
AuditEntry::ModificationType::CREATE,
"server set");
}
// The easiest way to verify whether the returned subnet matches the inserted
// subnet is to convert both to text.
EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
auto subnet = test_subnets_[0];
auto subnet2 = test_subnets_[2];
// Insert two subnets, one for all servers and one for server2.
EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet));
{
SCOPED_TRACE("CREATE audit entry for the subnet");
testNewAuditEntry("dhcp6_subnet",
......@@ -1059,45 +1058,98 @@ TEST_F(MySqlConfigBackendDHCPv6Test, getSubnet6) {
"subnet set");
}
// Update the subnet in the database (both use the same ID).
Subnet6Ptr subnet2 = test_subnets_[1];
cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet2);
// Fetch updated subnet and see if it matches.
returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
SubnetID(1024));
EXPECT_EQ(subnet2->toElement()->str(), returned_subnet->toElement()->str());
EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ONE("server2"), subnet2));
{
SCOPED_TRACE("CREATE audit entry for the subnet");
testNewAuditEntry("dhcp6_subnet",
AuditEntry::ModificationType::CREATE,
"subnet set", ServerSelector::ONE("subnet2"),
2, 1);
}
// Fetching the subnet for an explicitly specified server tag should
// succeed too.
returned_subnet = cbptr_->getSubnet6(ServerSelector::ONE("server1"),
SubnetID(1024));
EXPECT_EQ(subnet2->toElement()->str(), returned_subnet->toElement()->str());
// We are not going to support selection of a single entry for multiple servers.
EXPECT_THROW(cbptr_->getSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
subnet->getID()),
isc::InvalidOperation);
EXPECT_THROW(cbptr_->getSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
subnet->toText()),
isc::InvalidOperation);
// Test that this subnet will be fetched for various server selectors.
auto test_get_subnet = [this, &subnet] (const std::string& test_case_name,
const ServerSelector& server_selector,
const std::string& expected_tag = ServerTag::ALL) {
SCOPED_TRACE(test_case_name);
// Test fetching subnet by id.
Subnet6Ptr returned_subnet;
ASSERT_NO_THROW(returned_subnet = cbptr_->getSubnet6(server_selector, subnet->getID()));
ASSERT_TRUE(returned_subnet);
ASSERT_EQ(1, returned_subnet->getServerTags().size());
EXPECT_TRUE(returned_subnet->hasServerTag(ServerTag(expected_tag)));
EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
// Test fetching subnet by prefix.
ASSERT_NO_THROW(returned_subnet = cbptr_->getSubnet6(server_selector,
subnet->toText()));
ASSERT_TRUE(returned_subnet);
ASSERT_EQ(1, returned_subnet->getServerTags().size());
EXPECT_TRUE(returned_subnet->hasServerTag(ServerTag(expected_tag)));
EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
};
{
SCOPED_TRACE("UPDATE audit entry for the subnet");
SCOPED_TRACE("testing various server selectors before update");
test_get_subnet("all servers", ServerSelector::ALL());
test_get_subnet("one server", ServerSelector::ONE("server1"));
test_get_subnet("any server", ServerSelector::ANY());
}
subnet = subnet2;
{
SCOPED_TRACE("testing server selectors for another server");
test_get_subnet("one server", ServerSelector::ONE("server2"), "server2");
test_get_subnet("any server", ServerSelector::ANY(), "server2");
}
// Update the subnet in the database (both use the same ID).
subnet = test_subnets_[1];
EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet));
{
SCOPED_TRACE("CREATE audit entry for the subnet");
testNewAuditEntry("dhcp6_subnet",
AuditEntry::ModificationType::UPDATE,
"subnet set");
}
// Insert another subnet.
cbptr_->createUpdateSubnet6(ServerSelector::ALL(), test_subnets_[2]);
{
SCOPED_TRACE("testing various server selectors after update");
test_get_subnet("all servers", ServerSelector::ALL());
test_get_subnet("one server", ServerSelector::ONE("server1"));
test_get_subnet("any server", ServerSelector::ANY());
}
// Fetch this subnet by prefix and verify it matches.
returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
test_subnets_[2]->toText());
ASSERT_TRUE(returned_subnet);
EXPECT_EQ(test_subnets_[2]->toElement()->str(), returned_subnet->toElement()->str());
// The server2 specific subnet should not be returned if the server selector
// is not matching.
EXPECT_FALSE(cbptr_->getSubnet6(ServerSelector::ALL(), subnet2->getID()));
EXPECT_FALSE(cbptr_->getSubnet6(ServerSelector::ALL(), subnet2->toText()));
EXPECT_FALSE(cbptr_->getSubnet6(ServerSelector::ONE("server1"), subnet2->getID()));
EXPECT_FALSE(cbptr_->getSubnet6(ServerSelector::ONE("server1"), subnet2->toText()));
// Update the subnet in the database (both use the same prefix).
subnet2.reset(new Subnet6(IOAddress("2001:db8:3::"),
64, 30, 40, 50, 80, 8192));
cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet2);
EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ONE("server2"), subnet2));
// Fetch again and verify.
returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
test_subnets_[2]->toText());
auto returned_subnet = cbptr_->getSubnet6(ServerSelector::ONE("server2"),
subnet2->toText());
ASSERT_TRUE(returned_subnet);
EXPECT_EQ(subnet2->toElement()->str(), returned_subnet->toElement()->str());
......@@ -1106,7 +1158,7 @@ TEST_F(MySqlConfigBackendDHCPv6Test, getSubnet6) {
// Subnets are 2001:db8:1::/48 id 1024 and 2001:db8:3::/64 id 8192
subnet2.reset(new Subnet6(IOAddress("2001:db8:1::"),
48, 30, 40, 50, 80, 8192));
EXPECT_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet2),
EXPECT_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ONE("server2"), subnet2),
DuplicateEntry);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment