]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#93,!63] Implemented server selection for global parameters in MySQL.
authorMarcin Siodelski <marcin@isc.org>
Mon, 8 Oct 2018 14:32:16 +0000 (16:32 +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 1a44182085d212556398dd0126677935a60d9b6c..d770ac7329addd877670ef49ed8b9e9f49ff597c 100644 (file)
@@ -71,6 +71,7 @@ public:
         GET_OPTION4_POOL_ID_CODE_SPACE,
         GET_OPTION4_SHARED_NETWORK_CODE_SPACE,
         INSERT_GLOBAL_PARAMETER4,
+        INSERT_GLOBAL_PARAMETER4_SERVER,
         INSERT_SUBNET4,
         INSERT_POOL4,
         INSERT_SHARED_NETWORK4,
@@ -156,12 +157,17 @@ public:
     /// doesn't exist.
     StampedValuePtr getGlobalParameter4(const ServerSelector& /* server_selector */,
                                         const std::string& name) {
-        MySqlBindingCollection in_bindings = {
-            MySqlBinding::createString(name)
-        };
-
         StampedValueCollection parameters;
-        getGlobalParameters4(GET_GLOBAL_PARAMETER4, in_bindings, parameters);
+
+        auto tags = getServerTags(server_selector);
+        for (auto tag : tags) {
+            MySqlBindingCollection in_bindings = {
+                MySqlBinding::createString(tag),
+                MySqlBinding::createString(name)
+            };
+
+            getGlobalParameters4(GET_GLOBAL_PARAMETER4, in_bindings, parameters);
+        }
 
         return (parameters.empty() ? StampedValuePtr() : *parameters.begin());
     }
@@ -173,10 +179,27 @@ public:
     /// @param value Value of the global parameter.
     void createUpdateGlobalParameter4(const db::ServerSelector& /* server_selector */,
                                       const StampedValuePtr& value) {
+        MySqlTransaction transaction(conn_);
+
+        auto tags = getServerTags(server_selector);
+
+        /// @todo Currently we allow only one server tag for creation and update
+        /// of the global parameters. If we allow more, this is getting very tricky,
+        /// because we combine updates and insertions. We have to define how we
+        /// want to update an object shared by multiple servers. What if selector
+        /// contains one tag, but the parameter is shared by multiple? Should it
+        /// update one or all?.
+        if (tags.size() != 1) {
+            isc_throw(InvalidOperation, "expected exactly one server tag to be"
+                      " specified while creating or updating global configuration"
+                      " parameter. Got: " << getServerTagsAsText(server_selector));
+        }
+
         MySqlBindingCollection in_bindings = {
             MySqlBinding::createString(value->getName()),
             MySqlBinding::createString(value->getValue()),
             MySqlBinding::createTimestamp(value->getModificationTime()),
+            MySqlBinding::createString(*tags.begin()),
             MySqlBinding::createString(value->getName())
         };
 
@@ -189,7 +212,27 @@ public:
             in_bindings.pop_back();
             conn_.insertQuery(MySqlConfigBackendDHCPv4Impl::INSERT_GLOBAL_PARAMETER4,
                               in_bindings);
+
+            // Successfully inserted global parameter. Now, we have to associate it
+            // with the server tag.
+
+            // Let's first get the primary key of the global parameter.
+            uint64_t id = mysql_insert_id(conn_.mysql_);
+
+            // Create bindings for inserting the association into
+            // dhcp4_global_parameter_server table.
+            MySqlBindingCollection in_server_bindings = {
+                MySqlBinding::createInteger<uint64_t>(id), // parameter_id
+                MySqlBinding::createString(*tags.begin()), // tag used to obtain server_id
+                MySqlBinding::createTimestamp(value->getModificationTime()), // modification_ts
+            };
+
+            // Insert association.
+            conn_.insertQuery(MySqlConfigBackendDHCPv4Impl::INSERT_GLOBAL_PARAMETER4_SERVER,
+                              in_server_bindings);
         }
+
+        transaction.commit();
     }
 
     /// @brief Sends query to the database to retrieve multiple subnets.
@@ -1519,36 +1562,49 @@ TaggedStatementArray tagged_statements = { {
     // Select global parameter by name.
     { MySqlConfigBackendDHCPv4Impl::GET_GLOBAL_PARAMETER4,
       "SELECT"
-      "  id,"
-      "  name,"
-      "  value,"
-      "  modification_ts "
-      "FROM dhcp4_global_parameter "
-      "WHERE name = ? "
-      "ORDER BY id"
+      "  g.id,"
+      "  g.name,"
+      "  g.value,"
+      "  g.modification_ts "
+      "FROM dhcp4_global_parameter AS g "
+      "INNER JOIN dhcp4_global_parameter_server AS a "
+      "  ON g.id = a.parameter_id "
+      "INNER JOIN dhcp4_server AS s "
+      "  ON a.server_id = s.id "
+      "WHERE s.tag = ? AND g.name = ? "
+      "ORDER BY g.id"
     },
 
     // Select all global parameters.
     { MySqlConfigBackendDHCPv4Impl::GET_ALL_GLOBAL_PARAMETERS4,
       "SELECT"
-      "  id,"
-      "  name,"
-      "  value,"
-      "  modification_ts "
-      "FROM dhcp4_global_parameter "
-      "ORDER BY id"
+      "  g.id,"
+      "  g.name,"
+      "  g.value,"
+      "  g.modification_ts "
+      "FROM dhcp4_global_parameter AS g "
+      "INNER JOIN dhcp4_global_parameter_server AS a "
+      "  ON g.id = a.parameter_id "
+      "INNER JOIN dhcp4_server AS s "
+      "  ON a.server_id = s.id "
+      "WHERE s.tag = ? "
+      "ORDER BY g.id"
     },
 
     // Select modified global parameters.
     { MySqlConfigBackendDHCPv4Impl::GET_MODIFIED_GLOBAL_PARAMETERS4,
       "SELECT"
-      "  id,"
-      "  name,"
-      "  value,"
-      "  modification_ts "
-      "FROM dhcp4_global_parameter "
-      "WHERE modification_ts > ? "
-      "ORDER BY id"
+      "  g.id,"
+      "  g.name,"
+      "  g.value,"
+      "  g.modification_ts "
+      "FROM dhcp4_global_parameter AS g "
+      "INNER JOIN dhcp4_global_parameter_server AS a "
+      "  ON g.id = a.parameter_id "
+      "INNER JOIN dhcp4_server AS s "
+      "  ON a.server_id = s.id "
+      "WHERE s.tag = ? AND g.modification_ts > ? "
+      "ORDER BY g.id"
     },
 
     // Select subnet by id.
@@ -2088,6 +2144,14 @@ TaggedStatementArray tagged_statements = { {
       "  modification_ts"
       ") VALUES (?, ?, ?)" },
 
+    // Insert association of the global parameter with a server.
+    { MySqlConfigBackendDHCPv4Impl::INSERT_GLOBAL_PARAMETER4_SERVER,
+      "INSERT INTO dhcp4_global_parameter_server("
+      "  parameter_id,"
+      "  server_id,"
+      "  modification_ts"
+      ") VALUES (?, (SELECT id FROM dhcp4_server WHERE tag = ?), ?)" },
+
     // Insert a subnet.
     { MySqlConfigBackendDHCPv4Impl::INSERT_SUBNET4,
       "INSERT INTO dhcp4_subnet("
@@ -2173,11 +2237,17 @@ TaggedStatementArray tagged_statements = { {
 
     // Update existing global parameter.
     { MySqlConfigBackendDHCPv4Impl::UPDATE_GLOBAL_PARAMETER4,
-      "UPDATE dhcp4_global_parameter SET"
-      "  name = ?,"
-      "  value = ?,"
-      "  modification_ts = ? "
-      "WHERE name = ?" },
+      "UPDATE dhcp4_global_parameter AS g "
+      "INNER JOIN dhcp4_global_parameter_server AS a"
+      "  ON g.id = a.parameter_id "
+      "INNER JOIN dhcp4_server AS s"
+      "  ON a.server_id = s.id "
+      "SET"
+      "  g.name = ?,"
+      "  g.value = ?,"
+      "  g.modification_ts = ? "
+      "WHERE s.tag = ? AND g.name = ?"
+    },
 
     // Update existing subnet.
     { MySqlConfigBackendDHCPv4Impl::UPDATE_SUBNET4,
@@ -2514,10 +2584,14 @@ MySqlConfigBackendDHCPv4::getGlobalParameter4(const ServerSelector& server_selec
 
 StampedValueCollection
 MySqlConfigBackendDHCPv4::getAllGlobalParameters4(const ServerSelector& /* server_selector */) const {
-    MySqlBindingCollection in_bindings;
     StampedValueCollection parameters;
-    impl_->getGlobalParameters4(MySqlConfigBackendDHCPv4Impl::GET_ALL_GLOBAL_PARAMETERS4,
-                                in_bindings, parameters);
+
+    auto tags = impl_->getServerTags(server_selector);
+    for (auto tag : tags) {
+        MySqlBindingCollection in_bindings = { MySqlBinding::createString(tag) };
+        impl_->getGlobalParameters4(MySqlConfigBackendDHCPv4Impl::GET_ALL_GLOBAL_PARAMETERS4,
+                                    in_bindings, parameters);
+    }
     return (parameters);
 }
 
@@ -2525,12 +2599,18 @@ StampedValueCollection
 MySqlConfigBackendDHCPv4::
 getModifiedGlobalParameters4(const db::ServerSelector& /* server_selector */,
                              const boost::posix_time::ptime& modification_time) const {
-    MySqlBindingCollection in_bindings = {
-        MySqlBinding::createTimestamp(modification_time)
-    };
     StampedValueCollection parameters;
-    impl_->getGlobalParameters4(MySqlConfigBackendDHCPv4Impl::GET_MODIFIED_GLOBAL_PARAMETERS4,
-                                in_bindings, parameters);
+
+    auto tags = impl_->getServerTags(server_selector);
+    for (auto tag : tags) {
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createString(tag),
+            MySqlBinding::createTimestamp(modification_time)
+        };
+        impl_->getGlobalParameters4(MySqlConfigBackendDHCPv4Impl::GET_MODIFIED_GLOBAL_PARAMETERS4,
+                                    in_bindings, parameters);
+    }
+
     return (parameters);
 }
 
index 94ec71f0cab736bdf5449144f1ab23c021a8adcf..f08059d21fff6ef49a2e742516a1ae4b2cf05d92 100644 (file)
@@ -15,6 +15,7 @@
 #include <mysql.h>
 #include <mysqld_error.h>
 #include <cstdint>
+#include <sstream>
 #include <utility>
 
 using namespace isc::cb;
@@ -67,6 +68,40 @@ MySqlConfigBackendImpl::~MySqlConfigBackendImpl() {
     }
 }
 
+std::set<std::string>
+MySqlConfigBackendImpl::getServerTags(const ServerSelector& server_selector) const {
+    std::set<std::string> tags;
+    switch (server_selector.getType()) {
+    case ServerSelector::Type::UNASSIGNED:
+        tags.insert("unassigned");
+        return (tags);
+
+    case ServerSelector::Type::ALL:
+        tags.insert("all");
+        return (tags);
+
+    default:
+        return (server_selector.getTags());
+    }
+
+    // Impossible condition.
+    return (tags);
+}
+
+std::string
+MySqlConfigBackendImpl::getServerTagsAsText(const db::ServerSelector& server_selector) const {
+    std::ostringstream s;
+    auto server_tags = getServerTags(server_selector);
+    for (auto tag : server_tags) {
+        if (s.tellp() != 0) {
+            s << ", ";
+        }
+        s << tag;
+    }
+
+    return (s.str());
+}
+
 uint64_t
 MySqlConfigBackendImpl::deleteFromTable(const int index) {
     MySqlBindingCollection in_bindings;
index dff9de82820f33bb9777a374655124b818de876f..09e127c6d100920cf8c327f62cc835f2ca814027 100644 (file)
@@ -8,12 +8,14 @@
 #define MYSQL_CONFIG_BACKEND_IMPL_H
 
 #include <database/database_connection.h>
+#include <database/server_selector.h>
 #include <dhcp/option.h>
 #include <dhcp/option_definition.h>
 #include <dhcpsrv/cfg_option.h>
 #include <dhcpsrv/network.h>
 #include <mysql/mysql_binding.h>
 #include <mysql/mysql_connection.h>
+#include <set>
 #include <string>
 
 namespace isc {
@@ -35,6 +37,18 @@ public:
     /// @brief Destructor.
     ~MySqlConfigBackendImpl();
 
+    /// @brief Returns server tags associated with the particular selector.
+    ///
+    /// @param server_selector Server selector.
+    /// @return Set of server tags.
+    std::set<std::string> getServerTags(const db::ServerSelector& server_selector) const;
+
+    /// @brief Returns server tags associated with the particular selector
+    /// as text.
+    ///
+    /// This method is useful for logging purposes.
+    std::string getServerTagsAsText(const db::ServerSelector& server_selector) const;
+
     /// @brief Sends query to delete rows from a table.
     ///
     /// @param index Index of the statement to be executed.
index edfadc1dc89fed53c4238a47509157b58c898922..662a3e7b8f8b8fefc9ce35dd89b8b4a114b11e6d 100644 (file)
@@ -306,6 +306,8 @@ public:
 // deleted.
 TEST_F(MySqlConfigBackendDHCPv4Test, createUpdateDeleteGlobalParameter4) {
     StampedValuePtr server_tag = StampedValue::create("server-tag", "whale");
+    StampedValuePtr original_server_tag = server_tag;
+
     // Explicitly set modification time to make sure that the time
     // returned from the database is correct.
     server_tag->setModificationTime(timestamps_["yesterday"]);
@@ -321,6 +323,25 @@ TEST_F(MySqlConfigBackendDHCPv4Test, createUpdateDeleteGlobalParameter4) {
     EXPECT_TRUE(returned_server_tag->getModificationTime() ==
                 server_tag->getModificationTime());
 
+    // Check that the parameter is not returned when the server tag is not
+    // matching.
+    returned_server_tag = cbptr_->getGlobalParameter4(ServerSelector::ALL(),
+                                                      "server-tag");
+    EXPECT_FALSE(returned_server_tag);
+
+    // Check that the parameter is not updated when the server tag is not
+    // matching.
+    server_tag = StampedValue::create("server-tag", "fish");
+    cbptr_->createUpdateGlobalParameter4(ServerSelector::ALL(),
+                                         server_tag);
+    returned_server_tag = cbptr_->getGlobalParameter4(ServerSelector::UNASSIGNED()
+                                                      , "server-tag");
+    ASSERT_TRUE(returned_server_tag);
+    EXPECT_EQ("server-tag", returned_server_tag->getName());
+    EXPECT_EQ("whale", returned_server_tag->getValue());
+    EXPECT_TRUE(returned_server_tag->getModificationTime() ==
+                original_server_tag->getModificationTime());
+
     // Check that the parameter is udpated when it already exists in
     // the database.
     server_tag = StampedValue::create("server-tag", "fish");