]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#93,!63] Server selection partially working for subnets.
authorMarcin Siodelski <marcin@isc.org>
Wed, 10 Oct 2018 11:23:43 +0000 (13:23 +0200)
committerMarcin Siodelski <marcin@isc.org>
Thu, 18 Oct 2018 11:35:08 +0000 (13:35 +0200)
src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc
src/hooks/dhcp/mysql_cb/mysql_cb_impl.cc
src/hooks/dhcp/mysql_cb/mysql_cb_impl.h
src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc

index be0868e99ad8e84ccd05c946a21740dff0bf4abb..df4f0408cfac439f84f92895bbcd2462e940ba99 100644 (file)
@@ -73,6 +73,7 @@ public:
         INSERT_GLOBAL_PARAMETER4,
         INSERT_GLOBAL_PARAMETER4_SERVER,
         INSERT_SUBNET4,
+        INSERT_SUBNET4_SERVER,
         INSERT_POOL4,
         INSERT_SHARED_NETWORK4,
         INSERT_OPTION_DEF4,
@@ -476,13 +477,19 @@ public:
     ///
     /// @return Pointer to the returned subnet or NULL if such subnet
     /// doesn't exist.
-    Subnet4Ptr getSubnet4(const ServerSelector& /* server_selector */,
+    Subnet4Ptr getSubnet4(const ServerSelector& server_selector,
                           const SubnetID& subnet_id) {
-        MySqlBindingCollection in_bindings;
-        in_bindings.push_back(MySqlBinding::createInteger<uint32_t>(subnet_id));
-
         Subnet4Collection subnets;
-        getSubnets4(GET_SUBNET4_ID, in_bindings, subnets);
+
+        auto tags = getServerTags(server_selector);
+        for (auto tag : tags) {
+            MySqlBindingCollection in_bindings = {
+                MySqlBinding::createString(tag),
+                MySqlBinding::createInteger<uint32_t>(subnet_id)
+            };
+
+            getSubnets4(GET_SUBNET4_ID, in_bindings, subnets);
+        }
 
         return (subnets.empty() ? Subnet4Ptr() : *subnets.begin());
     }
@@ -496,17 +503,62 @@ public:
     ///
     /// @return Pointer to the returned subnet or NULL if such subnet
     /// doesn't exist.
-    Subnet4Ptr getSubnet4(const ServerSelector& /* server_selector */,
+    Subnet4Ptr getSubnet4(const ServerSelector& server_selector,
                           const std::string& subnet_prefix) {
-        MySqlBindingCollection in_bindings;
-        in_bindings.push_back(MySqlBinding::createString(subnet_prefix));
-
         Subnet4Collection subnets;
-        getSubnets4(GET_SUBNET4_PREFIX, in_bindings, subnets);
+
+        auto tags = getServerTags(server_selector);
+        for (auto tag : tags) {
+            MySqlBindingCollection in_bindings = {
+                MySqlBinding::createString(tag),
+                MySqlBinding::createString(subnet_prefix)
+            };
+
+            getSubnets4(GET_SUBNET4_PREFIX, in_bindings, subnets);
+        }
 
         return (subnets.empty() ? Subnet4Ptr() : *subnets.begin());
     }
 
+    /// @brief Sends query to retrieve all subnets.
+    ///
+    /// @param server_selector Server selector.
+    /// @param [out] subnets Reference to the subnet collection structure where
+    /// subnets should be inserted.
+    void getAllSubnets4(const ServerSelector& server_selector,
+                        Subnet4Collection& subnets) {
+        auto tags = getServerTags(server_selector);
+
+        for (auto tag : tags) {
+            MySqlBindingCollection in_bindings = {
+                MySqlBinding::createString(tag)
+            };
+
+            getSubnets4(GET_ALL_SUBNETS4, in_bindings, subnets);
+        }
+    }
+
+    /// @brief Sends query to retrieve modified subnets.
+    ///
+    /// @param server_selector Server selector.
+    /// @param modification_ts Lower bound modification timestamp.
+    /// @param [out] subnets Reference to the subnet collection structure where
+    /// subnets should be inserted.
+    void getModifiedSubnets4(const ServerSelector& server_selector,
+                             const boost::posix_time::ptime& modification_ts,
+                             Subnet4Collection& subnets) {
+        auto tags = getServerTags(server_selector);
+
+        for (auto tag : tags) {
+            MySqlBindingCollection in_bindings = {
+                MySqlBinding::createString(tag),
+                MySqlBinding::createTimestamp(modification_ts)
+            };
+
+            getSubnets4(GET_MODIFIED_SUBNETS4, in_bindings, subnets);
+        }
+    }
+
     /// @brief Sends query to retrieve multiple pools.
     ///
     /// Query should order pools by id.
@@ -611,6 +663,16 @@ public:
     /// @param subnet Pointer to the subnet to be inserted or updated.
     void createUpdateSubnet4(const ServerSelector& server_selector,
                              const Subnet4Ptr& subnet) {
+
+        auto tags = getServerTags(server_selector);
+
+        /// @todo Extend support to multiple server tags.
+        if (tags.size() != 1) {
+            isc_throw(InvalidOperation, "expected exactly one server tag to be"
+                      " specified while creating or updating subnet configuration."
+                      " Got: " << getServerTagsAsText(server_selector));
+        }
+
         // Convert DHCPv4o6 interface id to text.
         OptionPtr dhcp4o6_interface_id = subnet->get4o6().getInterfaceId();
         std::string dhcp4o6_interface_id_text;
@@ -679,6 +741,19 @@ public:
             conn_.insertQuery(MySqlConfigBackendDHCPv4Impl::INSERT_SUBNET4,
                               in_bindings);
 
+            // Create bindings for inserting the association into
+            // dhcp4_subnet_server table.
+            MySqlBindingCollection in_server_bindings = {
+                MySqlBinding::createInteger<uint32_t>(subnet->getID()), // subnet_id
+                MySqlBinding::createString(*tags.begin()), // tag used to obtain server_id
+                MySqlBinding::createTimestamp(subnet->getModificationTime()), // modification_ts
+            };
+
+            // Insert association.
+            conn_.insertQuery(MySqlConfigBackendDHCPv4Impl::INSERT_SUBNET4_SERVER,
+                              in_server_bindings);
+
+
         } catch (const DuplicateEntry&) {
             deletePools4(subnet);
             deleteOptions4(subnet);
@@ -743,13 +818,10 @@ public:
     /// @param server_selector Server selector.
     /// @param subnet_id Identifier of the subnet to be deleted.
     /// @return Number of deleted subnets.
-    uint64_t deleteSubnet4(const ServerSelector& /* server_selector */,
+    uint64_t deleteSubnet4(const ServerSelector& server_selector,
                        const SubnetID& subnet_id) {
-        MySqlBindingCollection in_bindings;
-        in_bindings.push_back(MySqlBinding::createInteger<uint32_t>(subnet_id));
-
-        // Run DELETE.
-        return (conn_.updateDeleteQuery(DELETE_SUBNET4_ID, in_bindings));
+        return (deleteFromTable<uint32_t>(DELETE_SUBNET4_ID, server_selector,
+                                          subnet_id));
     }
 
     /// @brief Deletes pools belonging to a subnet from the database.
@@ -1604,7 +1676,7 @@ TaggedStatementArray tagged_statements = { {
       "  ON g.id = a.parameter_id "
       "INNER JOIN dhcp4_server AS s "
       "  ON (a.server_id = s.id) OR (a.server_id = 1) "
-      "WHERE (s.tag = ? OR s.id=1) AND g.modification_ts > ? "
+      "WHERE (s.tag = ? OR s.id = 1) AND (g.modification_ts > ?) "
       "ORDER BY g.id"
     },
 
@@ -1661,10 +1733,14 @@ TaggedStatementArray tagged_statements = { {
       "  o.pool_id,"
       "  o.modification_ts "
       "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) "
       "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 s.subnet_id = ? "
+      "WHERE (srv.tag = ? OR srv.id = 1) AND s.subnet_id = ? "
       "ORDER BY s.subnet_id, p.id, x.option_id, o.option_id" },
 
     // Select subnet by prefix.
@@ -1720,10 +1796,14 @@ TaggedStatementArray tagged_statements = { {
       "  o.pool_id,"
       "  o.modification_ts "
       "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) "
       "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 s.subnet_prefix = ? "
+      "WHERE (srv.tag = ? OR srv.id = 1) AND s.subnet_prefix = ? "
       "ORDER BY s.subnet_id, p.id, x.option_id, o.option_id" },
 
     // Select all subnets.
@@ -1779,9 +1859,14 @@ TaggedStatementArray tagged_statements = { {
       "  o.pool_id,"
       "  o.modification_ts "
       "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) "
       "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) "
       "ORDER BY s.subnet_id, p.id, x.option_id, o.option_id" },
 
     // Select subnets having modification time later than X.
@@ -1837,10 +1922,14 @@ TaggedStatementArray tagged_statements = { {
       "  o.pool_id,"
       "  o.modification_ts "
       "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) "
       "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 s.modification_ts > ? "
+      "WHERE (srv.tag = ? OR srv.id = 1) AND s.modification_ts > ? "
       "ORDER BY s.subnet_id, p.id, x.option_id, o.option_id" },
 
     // Select pool by address range.
@@ -2179,6 +2268,14 @@ TaggedStatementArray tagged_statements = { {
       ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,"
       "?, ?, ?, ?, ?, ?, ?, ?)" },
 
+    // Insert association of the subnet with a server.
+    { MySqlConfigBackendDHCPv4Impl::INSERT_SUBNET4_SERVER,
+      "INSERT INTO dhcp4_subnet_server("
+      "  subnet_id,"
+      "  server_id,"
+      "  modification_ts"
+      ") VALUES (?, (SELECT id FROM dhcp4_server WHERE tag = ?), ?)" },
+
     // Insert pool for a subnet.
     { MySqlConfigBackendDHCPv4Impl::INSERT_POOL4,
       "INSERT INTO dhcp4_pool("
@@ -2400,17 +2497,30 @@ TaggedStatementArray tagged_statements = { {
 
     // Delete subnet by id.
     { MySqlConfigBackendDHCPv4Impl::DELETE_SUBNET4_ID,
-      "DELETE FROM dhcp4_subnet "
-      "WHERE subnet_id = ?" },
+      "DELETE s 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 "
+      "WHERE srv.tag = ? AND s.subnet_id = ?" },
 
     // Delete subnet by prefix.
     { MySqlConfigBackendDHCPv4Impl::DELETE_SUBNET4_PREFIX,
-      "DELETE FROM dhcp4_subnet "
-      "WHERE subnet_prefix = ?" },
+      "DELETE s 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 "
+      "WHERE srv.tag = ? AND s.subnet_prefix = ?" },
 
     // Delete all subnets.
     { MySqlConfigBackendDHCPv4Impl::DELETE_ALL_SUBNETS4,
-      "DELETE FROM dhcp4_subnet" },
+      "DELETE s 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 "
+      "WHERE srv.tag = ?" },
 
     // Delete pools for a subnet.
     { MySqlConfigBackendDHCPv4Impl::DELETE_POOLS4_SUBNET_ID,
@@ -2500,23 +2610,17 @@ MySqlConfigBackendDHCPv4::getSubnet4(const ServerSelector& server_selector,
 }
 
 Subnet4Collection
-MySqlConfigBackendDHCPv4::getAllSubnets4(const ServerSelector& /* server_selector */) const {
+MySqlConfigBackendDHCPv4::getAllSubnets4(const ServerSelector& server_selector) const {
     Subnet4Collection subnets;
-    MySqlBindingCollection in_bindings;
-    impl_->getSubnets4(MySqlConfigBackendDHCPv4Impl::GET_ALL_SUBNETS4,
-                      in_bindings, subnets);
+    impl_->getAllSubnets4(server_selector, subnets);
     return (subnets);
 }
 
 Subnet4Collection
-MySqlConfigBackendDHCPv4::getModifiedSubnets4(const ServerSelector& /* server_selector */,
+MySqlConfigBackendDHCPv4::getModifiedSubnets4(const ServerSelector& server_selector,
                                               const boost::posix_time::ptime& modification_time) const {
     Subnet4Collection subnets;
-    MySqlBindingCollection in_bindings = {
-        MySqlBinding::createTimestamp(modification_time)
-    };
-    impl_->getSubnets4(MySqlConfigBackendDHCPv4Impl::GET_MODIFIED_SUBNETS4,
-                       in_bindings, subnets);
+    impl_->getModifiedSubnets4(server_selector, modification_time, subnets);
     return (subnets);
 }
 
@@ -2678,10 +2782,10 @@ MySqlConfigBackendDHCPv4::createUpdateGlobalParameter4(const ServerSelector& ser
 }
 
 uint64_t
-MySqlConfigBackendDHCPv4::deleteSubnet4(const ServerSelector& /* server_selector */,
+MySqlConfigBackendDHCPv4::deleteSubnet4(const ServerSelector& server_selector,
                                         const std::string& subnet_prefix) {
     return(impl_->deleteFromTable(MySqlConfigBackendDHCPv4Impl::DELETE_SUBNET4_PREFIX,
-                                  subnet_prefix));
+                                  server_selector, subnet_prefix));
 }
 
 uint64_t
@@ -2691,8 +2795,9 @@ MySqlConfigBackendDHCPv4::deleteSubnet4(const ServerSelector& server_selector,
 }
 
 uint64_t
-MySqlConfigBackendDHCPv4::deleteAllSubnets4(const ServerSelector& /* server_selector */) {
-    return (impl_->deleteFromTable(MySqlConfigBackendDHCPv4Impl::DELETE_ALL_SUBNETS4));
+MySqlConfigBackendDHCPv4::deleteAllSubnets4(const ServerSelector& server_selector) {
+    return (impl_->deleteFromTable(MySqlConfigBackendDHCPv4Impl::DELETE_ALL_SUBNETS4,
+                                   server_selector));
 }
 
 uint64_t
index 5498eacc38b3fc7528041ea855030a79ded3cc02..06f55e846d6530c86860958de592c31fda38125c 100644 (file)
@@ -114,8 +114,7 @@ MySqlConfigBackendImpl::deleteFromTable(const int index, const std::string& key)
 
 uint64_t
 MySqlConfigBackendImpl::deleteFromTable(const int index,
-                                        const ServerSelector& server_selector,
-                                        const std::string& key) {
+                                        const ServerSelector& server_selector) {
     uint64_t deleted_entries = 0;
 
     auto tags = getServerTags(server_selector);
@@ -124,11 +123,6 @@ MySqlConfigBackendImpl::deleteFromTable(const int index,
             MySqlBinding::createString(tag)
         };
 
-        // Optionally add the key.
-        if (!key.empty()) {
-            in_bindings.push_back(MySqlBinding::createString(key));
-        }
-
         deleted_entries += conn_.updateDeleteQuery(index, in_bindings);
     }
 
index 5b4c64e64c9de0f8de448515c8f85c0e60a390c8..2c0e0913bc35f5162bd1aa9d7df030b7e0c4f70f 100644 (file)
@@ -60,7 +60,7 @@ public:
     /// @param index Index of the statement to be executed.
     /// @param key String value to be used as input binding to the delete
     /// statement
-    /// @return number of deleted rows.
+    /// @return Number of deleted rows.
     uint64_t deleteFromTable(const int index,
                              const std::string& key);
 
@@ -68,13 +68,45 @@ public:
     ///
     /// @param index Index of the statement to be executed.
     /// @param server_selector Server selector.
-    /// @param key String value to be used as input binding to the delete
+    /// @return Number of deleted rows.
+    uint64_t deleteFromTable(const int index,
+                             const db::ServerSelector& server_selector);
+
+    /// @brief Sends query to delete rows from a table.
+    ///
+    /// @tparam KeyType Type of the key used as the second binding. The
+    /// server tag is used as first binding.
+    ///
+    /// @param index Index of the statement to be executed.
+    /// @param server_selector Server selector.
+    /// @param key Value to be used as input binding to the delete
     /// statement. The default value is empty which indicates that the
     /// key should not be used in the query.
-    /// @return number of deleted rows.
+    /// @return Number of deleted rows.
+    template<typename KeyType>
     uint64_t deleteFromTable(const int index,
                              const db::ServerSelector& server_selector,
-                             const std::string& key = "");
+                             KeyType key) {
+        uint64_t deleted_entries = 0;
+
+        auto tags = getServerTags(server_selector);
+        for (auto tag : tags) {
+            db::MySqlBindingCollection in_bindings = {
+                db::MySqlBinding::createString(tag)
+            };
+
+            if (db::MySqlBindingTraits<KeyType>::column_type == MYSQL_TYPE_STRING) {
+                in_bindings.push_back(db::MySqlBinding::createString(key));
+
+            } else {
+                in_bindings.push_back(db::MySqlBinding::createInteger<KeyType>(key));
+            }
+
+            deleted_entries += conn_.updateDeleteQuery(index, in_bindings);
+        }
+
+        return (deleted_entries);
+    }
 
     /// @brief Sends query to the database to retrieve multiple option
     /// definitions.
index 193e3e50fbdc112e4d642ea923958ccdde5f77a5..b3c4cd7b6ed945fcd6694978624e159440062233 100644 (file)
@@ -448,6 +448,12 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getSubnet4) {
     returned_subnet = cbptr_->getSubnet4(ServerSelector::ALL(),
                                          SubnetID(1024));
     EXPECT_EQ(subnet2->toElement()->str(), returned_subnet->toElement()->str());
+
+    // Fetching the subnet for an explicitly specified server tag should
+    // succeeed too.
+    returned_subnet = cbptr_->getSubnet4(ServerSelector::ONE("server1"),
+                                         SubnetID(1024));
+    EXPECT_EQ(subnet2->toElement()->str(), returned_subnet->toElement()->str());
 }
 
 // Test that subnet can be associated with a shared network.
@@ -492,6 +498,12 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getSubnet4ByPrefix) {
 
     // Verify subnet contents.
     EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
+
+    // Fetching the subnet for an explicitly specified server tag should
+    // succeeed too.
+    returned_subnet = cbptr_->getSubnet4(ServerSelector::ONE("server1"),
+                                         "192.0.2.0/24");
+    EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
 }
 
 // Test that all subnets can be fetched and then deleted.
@@ -506,6 +518,10 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getAllSubnets4) {
     Subnet4Collection subnets = cbptr_->getAllSubnets4(ServerSelector::ALL());
     ASSERT_EQ(test_subnets_.size() - 1, subnets.size());
 
+    // All subnets should also be returned for explicitly specified server tag.
+    subnets = cbptr_->getAllSubnets4(ServerSelector::ONE("server1"));
+    ASSERT_EQ(test_subnets_.size() - 1, subnets.size());
+
     // See if the subnets are returned ok.
     for (auto i = 0; i < subnets.size(); ++i) {
         EXPECT_EQ(test_subnets_[i + 1]->toElement()->str(),
@@ -519,6 +535,18 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getAllSubnets4) {
     // All subnets should be still there.
     ASSERT_EQ(test_subnets_.size() - 1, subnets.size());
 
+    // Should not delete the subnet for explicit server tag because
+    // out subnet is for all servers.
+    EXPECT_EQ(0, cbptr_->deleteSubnet4(ServerSelector::ONE("server1"),
+                                       test_subnets_[1]->getID()));
+
+    // Also, verify that behavior when deleting by prefix.
+    EXPECT_EQ(0, cbptr_->deleteSubnet4(ServerSelector::ONE("server1"),
+                                       test_subnets_[2]->toText()));
+
+    // Same for all subnets.
+    EXPECT_EQ(0, cbptr_->deleteAllSubnets4(ServerSelector::ONE("server1")));
+
     // Delete first subnet by id and verify that it is gone.
     EXPECT_EQ(1, cbptr_->deleteSubnet4(ServerSelector::ALL(),
                                        test_subnets_[1]->getID()));
@@ -560,6 +588,11 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getModifiedSubnets4) {
                                               timestamps_["today"]);
     ASSERT_EQ(1, subnets.size());
 
+    // All subnets should also be returned for explicitly specified server tag.
+    subnets = cbptr_->getModifiedSubnets4(ServerSelector::ONE("server1"),
+                                          timestamps_["today"]);
+    ASSERT_EQ(1, subnets.size());
+
     // Fetch subnets with timestamp later than yesterday. We should get
     // two subnets.
     subnets = cbptr_->getModifiedSubnets4(ServerSelector::ALL(),