]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#93,!51] Pool specific options are now managed in MySQL database.
authorMarcin Siodelski <marcin@isc.org>
Tue, 2 Oct 2018 17:09:05 +0000 (19:09 +0200)
committerMarcin Siodelski <marcin@isc.org>
Mon, 8 Oct 2018 18:09:51 +0000 (20:09 +0200)
src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc
src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc
src/share/database/scripts/mysql/dhcpdb_create.mysql
src/share/database/scripts/mysql/upgrade_6.0_to_7.0.sh.in

index 6309ec37a65605214e71b17c3e1674b3b1d6426d..17dab77d9f891a32a7b9437e6fbc715ee3a7d3f1 100644 (file)
@@ -51,6 +51,7 @@ public:
         GET_SUBNET4_PREFIX,
         GET_ALL_SUBNETS4,
         GET_MODIFIED_SUBNETS4,
+        GET_POOL4_RANGE,
         GET_SHARED_NETWORK4_NAME,
         GET_ALL_SHARED_NETWORKS4,
         GET_MODIFIED_SHARED_NETWORKS4,
@@ -61,6 +62,7 @@ public:
         GET_ALL_OPTIONS4,
         GET_MODIFIED_OPTIONS4,
         GET_OPTION4_SUBNET_ID_CODE_SPACE,
+        GET_OPTION4_POOL_ID_CODE_SPACE,
         GET_OPTION4_SHARED_NETWORK_CODE_SPACE,
         INSERT_SUBNET4,
         INSERT_POOL4,
@@ -72,6 +74,7 @@ public:
         UPDATE_OPTION_DEF4,
         UPDATE_OPTION4,
         UPDATE_OPTION4_SUBNET_ID,
+        UPDATE_OPTION4_POOL_ID,
         UPDATE_OPTION4_SHARED_NETWORK,
         DELETE_SUBNET4_ID,
         DELETE_SUBNET4_PREFIX,
@@ -83,6 +86,7 @@ public:
         DELETE_ALL_OPTION_DEFS4,
         DELETE_OPTION4,
         DELETE_OPTION4_SUBNET_ID,
+        DELETE_OPTION4_POOL_RANGE,
         DELETE_OPTION4_SHARED_NETWORK,
         DELETE_OPTIONS4_SUBNET_ID,
         DELETE_OPTIONS4_SHARED_NETWORK,
@@ -138,6 +142,18 @@ public:
             MySqlBinding::createInteger<uint32_t>(), // pool: end_address
             MySqlBinding::createInteger<uint32_t>(), // pool: subnet_id
             MySqlBinding::createTimestamp(), // pool: modification_ts
+            MySqlBinding::createInteger<uint64_t>(), // pool option: option_id
+            MySqlBinding::createInteger<uint8_t>(), // pool option: code
+            MySqlBinding::createBlob(65536), // pool option: value
+            MySqlBinding::createString(8192), // pool option: formatted_value
+            MySqlBinding::createString(128), // pool option: space
+            MySqlBinding::createInteger<uint8_t>(), // pool option: persistent
+            MySqlBinding::createInteger<uint32_t>(), // pool option: dhcp4_subnet_id
+            MySqlBinding::createInteger<uint8_t>(), // pool option: scope_id
+            MySqlBinding::createString(65536), // pool option: user_context
+            MySqlBinding::createString(128), // pool option: shared_network_name
+            MySqlBinding::createInteger<uint64_t>(), // pool option: pool_id
+            MySqlBinding::createTimestamp(), //pool option: modification_ts
             MySqlBinding::createInteger<uint64_t>(), // option: option_id
             MySqlBinding::createInteger<uint8_t>(), // option: code
             MySqlBinding::createBlob(65536), // option: value
@@ -153,11 +169,15 @@ public:
         };
 
         uint64_t last_pool_id = 0;
+        uint64_t last_pool_option_id = 0;
         uint64_t last_option_id = 0;
 
+        Pool4Ptr last_pool;
+
         // Execute actual query.
         conn_.selectQuery(index, in_bindings, out_bindings,
-                          [this, &subnets, &last_pool_id, &last_option_id]
+                          [this, &subnets, &last_pool, &last_pool_id,
+                           &last_pool_option_id, &last_option_id]
                           (MySqlBindingCollection& out_bindings) {
             // Get pointer to the last subnet in the collection.
             Subnet4Ptr last_subnet;
@@ -284,18 +304,30 @@ public:
                 ((last_pool_id == 0) ||
                  (out_bindings[20]->getInteger<uint64_t>() > last_pool_id))) {
                 last_pool_id = out_bindings[20]->getInteger<uint64_t>();
-                Pool4Ptr pool(new Pool4(IOAddress(out_bindings[21]->getInteger<uint32_t>()),
-                                        IOAddress(out_bindings[22]->getInteger<uint32_t>())));
-                last_subnet->addPool(pool);
+                last_pool.reset(new Pool4(IOAddress(out_bindings[21]->getInteger<uint32_t>()),
+                                          IOAddress(out_bindings[22]->getInteger<uint32_t>())));
+                last_subnet->addPool(last_pool);
             }
 
-            // Parse option.
-            if (!out_bindings[25]->amNull() &&
-                ((last_option_id == 0) ||
-                (last_option_id < out_bindings[25]->getInteger<uint64_t>()))) {
-                last_option_id = out_bindings[25]->getInteger<uint64_t>();
+            // Parse pool specific option.
+            if (last_pool && !out_bindings[25]->amNull() &&
+                ((last_pool_option_id == 0) ||
+                (last_pool_option_id < out_bindings[25]->getInteger<uint64_t>()))) {
+                last_pool_option_id = out_bindings[25]->getInteger<uint64_t>();
 
                 OptionDescriptorPtr desc = processOptionRow(Option::V4, out_bindings.begin() + 25);
+                if (desc) {
+                    last_pool->getCfgOption()->add(*desc, desc->space_name_);
+                }
+            }
+
+            // Parse subnet specific option.
+            if (!out_bindings[37]->amNull() &&
+                ((last_option_id == 0) ||
+                (last_option_id < out_bindings[37]->getInteger<uint64_t>()))) {
+                last_option_id = out_bindings[37]->getInteger<uint64_t>();
+
+                OptionDescriptorPtr desc = processOptionRow(Option::V4, out_bindings.begin() + 37);
                 if (desc) {
                     last_subnet->getCfgOption()->add(*desc, desc->space_name_);
                 }
@@ -342,6 +374,106 @@ public:
         return (subnets.empty() ? Subnet4Ptr() : *subnets.begin());
     }
 
+    /// @brief Sends query to retrieve multiple pools.
+    ///
+    /// Query should order pools by id.
+    ///
+    /// @param index Index of the query to be used.
+    /// @param in_bindings Input bindings specifying selection criteria. The
+    /// size of the bindings collection must match the number of placeholders
+    /// in the prepared statement. The input bindings collection must be empty
+    /// if the query contains no WHERE clause.
+    /// @param [out] pools Reference to the container where fetched pools
+    /// will be inserted.
+    /// @param [out] pool_ids Identifiers of the pools returned in @c pools
+    /// argument.
+    void getPools(const StatementIndex& index,
+                  const MySqlBindingCollection& in_bindings,
+                  PoolCollection& pools,
+                  std::vector<uint64_t>& pool_ids) {
+        MySqlBindingCollection out_bindings = {
+            MySqlBinding::createInteger<uint64_t>(), // pool: id
+            MySqlBinding::createInteger<uint32_t>(), // pool: start_address
+            MySqlBinding::createInteger<uint32_t>(), // pool: end_address
+            MySqlBinding::createInteger<uint32_t>(), // pool: subnet_id
+            MySqlBinding::createTimestamp(), // pool: modification_ts
+            MySqlBinding::createInteger<uint64_t>(), // pool option: option_id
+            MySqlBinding::createInteger<uint8_t>(), // pool option: code
+            MySqlBinding::createBlob(65536), // pool option: value
+            MySqlBinding::createString(8192), // pool option: formatted_value
+            MySqlBinding::createString(128), // pool option: space
+            MySqlBinding::createInteger<uint8_t>(), // pool option: persistent
+            MySqlBinding::createInteger<uint32_t>(), // pool option: dhcp4_subnet_id
+            MySqlBinding::createInteger<uint8_t>(), // pool option: scope_id
+            MySqlBinding::createString(65536), // pool option: user_context
+            MySqlBinding::createString(128), // pool option: shared_network_name
+            MySqlBinding::createInteger<uint64_t>(), // pool option: pool_id
+            MySqlBinding::createTimestamp(), //pool option: modification_ts
+        };
+
+        uint64_t last_pool_id = 0;
+        uint64_t last_pool_option_id = 0;
+        Pool4Ptr last_pool;
+
+        conn_.selectQuery(index, in_bindings, out_bindings,
+                          [this, &last_pool_id, &last_pool_option_id, &last_pool,
+                           &pools, &pool_ids]
+                          (MySqlBindingCollection& out_bindings) {
+            if ((last_pool_id == 0) ||
+                (out_bindings[0]->getInteger<uint64_t>() > last_pool_id)) {
+
+                last_pool_id = out_bindings[0]->getInteger<uint64_t>();
+
+                last_pool.reset(new Pool4(IOAddress(out_bindings[1]->getInteger<uint32_t>()),
+                                          IOAddress(out_bindings[2]->getInteger<uint32_t>())));
+                pools.push_back(last_pool);
+                pool_ids.push_back(last_pool_id);
+            }
+
+            // Parse pool specific option.
+            if (last_pool && !out_bindings[5]->amNull() &&
+                ((last_pool_option_id == 0) ||
+                (last_pool_option_id < out_bindings[5]->getInteger<uint64_t>()))) {
+                last_pool_option_id = out_bindings[5]->getInteger<uint64_t>();
+
+                OptionDescriptorPtr desc = processOptionRow(Option::V4, out_bindings.begin() + 5);
+                if (desc) {
+                    last_pool->getCfgOption()->add(*desc, desc->space_name_);
+                }
+            }
+        });
+    }
+
+    /// @brief Sends query to retrieve single pool by address range.
+    ///
+    /// @param selector Server selector.
+    /// @param pool_start_address Lower bound pool address.
+    /// @param pool_end_address Upper bound pool address.
+    /// @param pool_id Pool identifier for the returned pool.
+    /// @return Pointer to the pool or null if no such pool found.
+    Pool4Ptr getPool4(const ServerSelector& /* selector */,
+                      const IOAddress& pool_start_address,
+                      const IOAddress& pool_end_address,
+                      uint64_t& pool_id) {
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createInteger<uint32_t>(pool_start_address.toUint32()),
+            MySqlBinding::createInteger<uint32_t>(pool_end_address.toUint32())
+        };
+
+        PoolCollection pools;
+        std::vector<uint64_t> pool_ids;
+        getPools(GET_POOL4_RANGE, in_bindings, pools, pool_ids);
+
+        if (!pools.empty()) {
+            pool_id = pool_ids[0];
+            return (boost::dynamic_pointer_cast<Pool4>(*pools.begin()));
+        }
+
+        pool_id = 0;
+
+        return (Pool4Ptr());
+    }
+
     /// @brief Sends query to insert or update subnet.
     ///
     /// @param server_selector Server selector.
@@ -428,7 +560,7 @@ public:
 
         // (Re)create pools.
         for (auto pool : subnet->getPools(Lease::TYPE_V4)) {
-            createPool4(boost::dynamic_pointer_cast<Pool4>(pool), subnet);
+            createPool4(selector, boost::dynamic_pointer_cast<Pool4>(pool), subnet);
         }
 
         // (Re)create options.
@@ -447,9 +579,11 @@ public:
 
     /// @brief Inserts new IPv4 pool to the database.
     ///
+    /// @param server_selector Server selector.
     /// @param pool Pointer to the pool to be inserted.
     /// @param subnet Pointer to the subnet that this pool belongs to.
-    void createPool4(const Pool4Ptr& pool, const Subnet4Ptr& subnet) {
+    void createPool4(const ServerSelector& selector, const Pool4Ptr& pool,
+                     const Subnet4Ptr& subnet) {
         MySqlBindingCollection in_bindings = {
             MySqlBinding::createInteger<uint32_t>(pool->getFirstAddress().toUint32()),
             MySqlBinding::createInteger<uint32_t>(pool->getLastAddress().toUint32()),
@@ -459,6 +593,17 @@ public:
 
         // Run INSERT.
         conn_.insertQuery(INSERT_POOL4, in_bindings);
+
+        uint64_t pool_id = mysql_insert_id(conn_.mysql_);
+        auto option_spaces = pool->getCfgOption()->getOptionSpaceNames();
+        for (auto option_space : option_spaces) {
+            OptionContainerPtr options = pool->getCfgOption()->getAll(option_space);
+            for (auto desc = options->begin(); desc != options->end(); ++desc) {
+                OptionDescriptorPtr desc_copy(new OptionDescriptor(*desc));
+                desc_copy->space_name_ = option_space;
+                createUpdateOption4(selector, pool_id, desc_copy);
+            }
+        }
     }
 
     /// @brief Sends query to delete subnet by id.
@@ -791,6 +936,63 @@ public:
         }
     }
 
+    void createUpdateOption4(const ServerSelector& selector,
+                             const IOAddress& pool_start_address,
+                             const IOAddress& pool_end_address,
+                             const OptionDescriptorPtr& option) {
+        uint64_t pool_id = 0;
+        Pool4Ptr pool = getPool4(selector, pool_start_address, pool_end_address,
+                                 pool_id);
+        if (!pool) {
+            isc_throw(BadValue, "no pool found for range of "
+                      << pool_start_address << " : "
+                      << pool_end_address);
+        }
+
+        createUpdateOption4(selector, pool_id, option);
+    }
+
+
+    /// @brief Sends query to insert or update DHCP option in a pool.
+    ///
+    /// @param selector Server selector.
+    /// @param pool_id Identifier of the pool the option belongs to.
+    /// @param option Pointer to the option descriptor encapsulating the option.
+    void createUpdateOption4(const ServerSelector& selector,
+                             const uint64_t pool_id,
+                             const OptionDescriptorPtr& option) {
+
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createInteger<uint8_t>(option->option_->getType()),
+            createOptionValueBinding(option),
+            MySqlBinding::condCreateString(option->formatted_value_),
+            MySqlBinding::condCreateString(option->space_name_),
+            MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(option->persistent_)),
+            MySqlBinding::createNull(),
+            MySqlBinding::createNull(),
+            MySqlBinding::createInteger<uint8_t>(5),
+            createInputContextBinding(option),
+            MySqlBinding::createNull(),
+            MySqlBinding::createInteger<uint64_t>(pool_id),
+            MySqlBinding::createTimestamp(option->getModificationTime())
+        };
+
+        OptionDescriptorPtr existing_option = getOption4(selector, pool_id,
+                                                         option->option_->getType(),
+                                                         option->space_name_);
+        if (existing_option) {
+            in_bindings.push_back(MySqlBinding::createInteger<uint64_t>(pool_id));
+            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_POOL_ID,
+                                    in_bindings);
+
+        } else {
+            conn_.insertQuery(MySqlConfigBackendDHCPv4Impl::INSERT_OPTION4,
+                              in_bindings);
+        }
+    }
+
     /// @brief Sends query to insert or update DHCP option in a shared network.
     ///
     /// @param selector Server selector.
@@ -959,6 +1161,32 @@ public:
                 OptionDescriptorPtr(new OptionDescriptor(*options.begin())));
     }
 
+    /// @brief Sends query to retrieve single option by code and option space
+    /// for a given pool id.
+    ///
+    /// @param selector Server selector.
+    /// @param pool_id Pool identifier in the database.
+    /// @param code Option code.
+    /// @param space Option space name.
+    ///
+    /// @return Pointer to the returned option descriptor or NULL if such
+    /// option doesn't exist.
+    OptionDescriptorPtr getOption4(const ServerSelector& /* selector */,
+                                   const uint64_t pool_id,
+                                   const uint16_t code,
+                                   const std::string& space) {
+        OptionContainer options;
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createInteger<uint64_t>(pool_id),
+            MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(code)),
+            MySqlBinding::createString(space)
+        };
+        getOptions(GET_OPTION4_POOL_ID_CODE_SPACE, in_bindings, Option::V4,
+                   options);
+        return (options.empty() ? OptionDescriptorPtr() :
+                OptionDescriptorPtr(new OptionDescriptor(*options.begin())));
+    }
+
     /// @brief Sends query to retrieve single option by code and option space
     /// for a given shared network.
     ///
@@ -1098,6 +1326,29 @@ public:
         conn_.updateDeleteQuery(DELETE_OPTION4_SUBNET_ID, in_bindings);
     }
 
+    /// @brief Deletes pool level option.
+    ///
+    /// @param selector Server selector.
+    /// @param pool_start_address Lower bound pool address.
+    /// @param pool_end_address  Upper bound pool address.
+    /// @param code Code of the deleted option.
+    /// @param space Option space of the deleted option.
+    void deleteOption4(const db::ServerSelector& /* selector */,
+                       const IOAddress& pool_start_address,
+                       const IOAddress& pool_end_address,
+                       const uint16_t code,
+                       const std::string& space) {
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createInteger<uint32_t>(pool_start_address.toUint32()),
+            MySqlBinding::createInteger<uint32_t>(pool_end_address.toUint32()),
+            MySqlBinding::createInteger<uint8_t>(code),
+            MySqlBinding::createString(space)
+        };
+
+        // Run DELETE.
+        conn_.updateDeleteQuery(DELETE_OPTION4_POOL_RANGE, in_bindings);
+    }
+
     /// @brief Deletes shared network level option.
     ///
     /// @param selector Server selector.
@@ -1181,6 +1432,18 @@ TaggedStatementArray tagged_statements = { {
       "  p.end_address,"
       "  p.subnet_id,"
       "  p.modification_ts,"
+      "  x.option_id,"
+      "  x.code,"
+      "  x.value,"
+      "  x.formatted_value,"
+      "  x.space,"
+      "  x.persistent,"
+      "  x.dhcp4_subnet_id,"
+      "  x.scope_id,"
+      "  x.user_context,"
+      "  x.shared_network_name,"
+      "  x.pool_id,"
+      "  x.modification_ts,"
       "  o.option_id,"
       "  o.code,"
       "  o.value,"
@@ -1195,9 +1458,10 @@ TaggedStatementArray tagged_statements = { {
       "  o.modification_ts "
       "FROM dhcp4_subnet AS s "
       "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 = ? "
-      "ORDER BY s.subnet_id, p.id, o.option_id" },
+      "ORDER BY s.subnet_id, p.id, x.option_id, o.option_id" },
 
     // Select subnet by prefix.
     { MySqlConfigBackendDHCPv4Impl::GET_SUBNET4_PREFIX,
@@ -1227,6 +1491,18 @@ TaggedStatementArray tagged_statements = { {
       "  p.end_address,"
       "  p.subnet_id,"
       "  p.modification_ts,"
+      "  x.option_id,"
+      "  x.code,"
+      "  x.value,"
+      "  x.formatted_value,"
+      "  x.space,"
+      "  x.persistent,"
+      "  x.dhcp4_subnet_id,"
+      "  x.scope_id,"
+      "  x.user_context,"
+      "  x.shared_network_name,"
+      "  x.pool_id,"
+      "  x.modification_ts,"
       "  o.option_id,"
       "  o.code,"
       "  o.value,"
@@ -1241,9 +1517,10 @@ TaggedStatementArray tagged_statements = { {
       "  o.modification_ts "
       "FROM dhcp4_subnet AS s "
       "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 = ? "
-      "ORDER BY s.subnet_id, p.id, o.option_id" },
+      "ORDER BY s.subnet_id, p.id, x.option_id, o.option_id" },
 
     // Select all subnets.
     { MySqlConfigBackendDHCPv4Impl::GET_ALL_SUBNETS4,
@@ -1273,6 +1550,18 @@ TaggedStatementArray tagged_statements = { {
       "  p.end_address,"
       "  p.subnet_id,"
       "  p.modification_ts,"
+      "  x.option_id,"
+      "  x.code,"
+      "  x.value,"
+      "  x.formatted_value,"
+      "  x.space,"
+      "  x.persistent,"
+      "  x.dhcp4_subnet_id,"
+      "  x.scope_id,"
+      "  x.user_context,"
+      "  x.shared_network_name,"
+      "  x.pool_id,"
+      "  x.modification_ts,"
       "  o.option_id,"
       "  o.code,"
       "  o.value,"
@@ -1287,8 +1576,9 @@ TaggedStatementArray tagged_statements = { {
       "  o.modification_ts "
       "FROM dhcp4_subnet AS s "
       "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 "
-      "ORDER BY s.subnet_id, p.id, o.option_id" },
+      "ORDER BY s.subnet_id, p.id, x.option_id, o.option_id" },
 
     // Select subnets having modification time later than X.
     { MySqlConfigBackendDHCPv4Impl::GET_MODIFIED_SUBNETS4,
@@ -1318,6 +1608,18 @@ TaggedStatementArray tagged_statements = { {
       "  p.end_address,"
       "  p.subnet_id,"
       "  p.modification_ts,"
+      "  x.option_id,"
+      "  x.code,"
+      "  x.value,"
+      "  x.formatted_value,"
+      "  x.space,"
+      "  x.persistent,"
+      "  x.dhcp4_subnet_id,"
+      "  x.scope_id,"
+      "  x.user_context,"
+      "  x.shared_network_name,"
+      "  x.pool_id,"
+      "  x.modification_ts,"
       "  o.option_id,"
       "  o.code,"
       "  o.value,"
@@ -1332,9 +1634,36 @@ TaggedStatementArray tagged_statements = { {
       "  o.modification_ts "
       "FROM dhcp4_subnet AS s "
       "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 > ? "
-      "ORDER BY s.subnet_id, p.id, o.option_id" },
+      "ORDER BY s.subnet_id, p.id, x.option_id, o.option_id" },
+
+    // Select pool by address range.
+    { MySqlConfigBackendDHCPv4Impl::GET_POOL4_RANGE,
+      "SELECT"
+      "  p.id,"
+      "  p.start_address,"
+      "  p.end_address,"
+      "  p.subnet_id,"
+      "  p.modification_ts,"
+      "  x.option_id,"
+      "  x.code,"
+      "  x.value,"
+      "  x.formatted_value,"
+      "  x.space,"
+      "  x.persistent,"
+      "  x.dhcp4_subnet_id,"
+      "  x.scope_id,"
+      "  x.user_context,"
+      "  x.shared_network_name,"
+      "  x.pool_id,"
+      "  x.modification_ts "
+      "FROM dhcp4_pool AS p "
+      "LEFT JOIN dhcp4_options AS x ON x.scope_id = 5 AND p.id = x.pool_id "
+      "WHERE p.start_address = ? AND p.end_address = ? "
+      "ORDER BY p.id, x.option_id"
+    },
 
     // Select shared network by name.
     { MySqlConfigBackendDHCPv4Impl::GET_SHARED_NETWORK4_NAME,
@@ -1564,6 +1893,26 @@ TaggedStatementArray tagged_statements = { {
       "  code = ? AND space = ? "
       "ORDER BY option_id" },
 
+    // Retrieves an option for a given pool, option code and space.
+    { MySqlConfigBackendDHCPv4Impl::GET_OPTION4_POOL_ID_CODE_SPACE,
+      "SELECT"
+      "  option_id,"
+      "  code,"
+      "  value,"
+      "  formatted_value,"
+      "  space,"
+      "  persistent,"
+      "  dhcp4_subnet_id,"
+      "  scope_id,"
+      "  user_context,"
+      "  shared_network_name,"
+      "  pool_id,"
+      "  modification_ts "
+      "FROM dhcp4_options "
+      "WHERE scope_id = 5 AND pool_id = ? AND"
+      "  code = ? AND space = ? "
+      "ORDER BY option_id" },
+
     // Retrieves an option for a given shared network, option code and space.
     { MySqlConfigBackendDHCPv4Impl::GET_OPTION4_SHARED_NETWORK_CODE_SPACE,
       "SELECT"
@@ -1759,6 +2108,24 @@ TaggedStatementArray tagged_statements = { {
       "WHERE scope_id = 1 AND dhcp4_subnet_id = ? AND code = ? AND space = ?"
     },
 
+    // Update existing pool level option.
+    { MySqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_POOL_ID,
+      "UPDATE dhcp4_options SET"
+      "  code = ?,"
+      "  value = ?,"
+      "  formatted_value = ?,"
+      "  space = ?,"
+      "  persistent = ?,"
+      "  dhcp_client_class = ?,"
+      "  dhcp4_subnet_id = ?,"
+      "  scope_id = ?,"
+      "  user_context = ?,"
+      "  shared_network_name = ?,"
+      "  pool_id = ?,"
+      "  modification_ts = ? "
+      "WHERE scope_id = 5 AND pool_id = ? AND code = ? AND space = ?"
+    },
+
     // Update existing shared network level option.
     { MySqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_SHARED_NETWORK,
       "UPDATE dhcp4_options SET"
@@ -1825,6 +2192,13 @@ TaggedStatementArray tagged_statements = { {
       "WHERE scope_id = 1 AND dhcp4_subnet_id = ?"
       "  AND code = ? AND space = ?" },
 
+    // Delete single option from a pool.
+    { MySqlConfigBackendDHCPv4Impl::DELETE_OPTION4_POOL_RANGE,
+      "DELETE FROM dhcp4_options "
+      "WHERE scope_id = 5 AND pool_id = "
+      "  (SELECT id FROM dhcp4_pool"
+      "   WHERE start_address = ? AND end_address = ?)" },
+
     // Delete single option from a shared network.
     { MySqlConfigBackendDHCPv4Impl::DELETE_OPTION4_SHARED_NETWORK,
       "DELETE FROM dhcp4_options "
@@ -2014,10 +2388,12 @@ MySqlConfigBackendDHCPv4::createUpdateOption4(const ServerSelector& selector,
 }
 
 void
-MySqlConfigBackendDHCPv4::createUpdateOption4(const ServerSelector& /* server_selector */,
-                                              const asiolink::IOAddress& /* pool_start_address */,
-                                              const asiolink::IOAddress& /* pool_end_address */,
-                                              const OptionDescriptorPtr& /* option */) {
+MySqlConfigBackendDHCPv4::createUpdateOption4(const ServerSelector& selector,
+                                              const asiolink::IOAddress& pool_start_address,
+                                              const asiolink::IOAddress& pool_end_address,
+                                              const OptionDescriptorPtr& option) {
+    impl_->createUpdateOption4(selector, pool_start_address, pool_end_address,
+                               option);
 }
 
 void
@@ -2098,12 +2474,13 @@ MySqlConfigBackendDHCPv4::deleteOption4(const ServerSelector&  selector,
 }
 
 uint64_t
-MySqlConfigBackendDHCPv4::deleteOption4(const ServerSelector& /* server_selector */,
-                                        const asiolink::IOAddress& /* pool_start_address */,
-                                        const asiolink::IOAddress& /* pool_end_address */,
-                                        const uint16_t /* code */,
-                                        const std::string& /* space */) {
-    return (0);
+MySqlConfigBackendDHCPv4::deleteOption4(const ServerSelector& selector,
+                                        const asiolink::IOAddress& pool_start_address,
+                                        const asiolink::IOAddress& pool_end_address,
+                                        const uint16_t code,
+                                        const std::string& space) {
+    impl_->deleteOption4(selector, pool_start_address, pool_end_address,
+                         code, space);
 }
 
 uint64_t
index d040baffb4876811b300f222962e72c1ed84bd44..abc5e341d6daad236aa1b14ce80940fa34043c48 100644 (file)
@@ -127,6 +127,14 @@ public:
         pool1.reset(new Pool4(IOAddress("10.0.0.10"), IOAddress("10.0.0.20")));
         subnet->addPool(pool1);
 
+        pool1->getCfgOption()->add(test_options_[3]->option_,
+                                   test_options_[3]->persistent_,
+                                   test_options_[3]->space_name_);
+
+        pool1->getCfgOption()->add(test_options_[4]->option_,
+                                   test_options_[4]->persistent_,
+                                   test_options_[4]->space_name_);
+
         pool2.reset(new Pool4(IOAddress("10.0.0.50"), IOAddress("10.0.0.60")));
         subnet->addPool(pool2);
 
@@ -803,6 +811,80 @@ TEST_F(MySqlConfigBackendDHCPv4Test, createUpdateDeleteSubnetOption4) {
     EXPECT_FALSE(returned_subnet->getCfgOption()->get(DHCP4_OPTION_SPACE, DHO_BOOT_FILE_NAME).option_);
 }
 
+// This test verifies that option can be inserted, updated and deleted
+// from the pool.
+TEST_F(MySqlConfigBackendDHCPv4Test, createUpdateDeletePoolOption4) {
+    // Insert new subnet.
+    Subnet4Ptr subnet = test_subnets_[1];
+    cbptr_->createUpdateSubnet4(ServerSelector::UNASSIGNED(), subnet);
+
+    // Add an option into the pool.
+    const PoolPtr pool = subnet->getPool(Lease::TYPE_V4, IOAddress("192.0.2.10"));
+    ASSERT_TRUE(pool);
+    OptionDescriptorPtr opt_boot_file_name = test_options_[0];
+    cbptr_->createUpdateOption4(ServerSelector::UNASSIGNED(),
+                                pool->getFirstAddress(),
+                                pool->getLastAddress(),
+                                opt_boot_file_name);
+
+    // Query for a subnet.
+    Subnet4Ptr returned_subnet = cbptr_->getSubnet4(ServerSelector::UNASSIGNED(),
+                                                    subnet->getID());
+    ASSERT_TRUE(returned_subnet);
+
+    // The returned subnet should include our pool.
+    const PoolPtr returned_pool = returned_subnet->getPool(Lease::TYPE_V4,
+                                                           IOAddress("192.0.2.10"));
+    ASSERT_TRUE(returned_pool);
+
+    // The pool should contain option we added earlier.
+    OptionDescriptor returned_opt_boot_file_name =
+        returned_pool->getCfgOption()->get(DHCP4_OPTION_SPACE, DHO_BOOT_FILE_NAME);
+    ASSERT_TRUE(returned_opt_boot_file_name.option_);
+    EXPECT_TRUE(returned_opt_boot_file_name.equals(*opt_boot_file_name));
+
+    // Modify the option and update it in the database.
+    opt_boot_file_name->persistent_ = !opt_boot_file_name->persistent_;
+    cbptr_->createUpdateOption4(ServerSelector::UNASSIGNED(),
+                                pool->getFirstAddress(),
+                                pool->getLastAddress(),
+                                opt_boot_file_name);
+
+    // Fetch the subnet and the corresponding pool.
+    returned_subnet = cbptr_->getSubnet4(ServerSelector::UNASSIGNED(),
+                                         subnet->getID());
+    ASSERT_TRUE(returned_subnet);
+    const PoolPtr returned_pool1 = returned_subnet->getPool(Lease::TYPE_V4,
+                                                            IOAddress("192.0.2.10"));
+    ASSERT_TRUE(returned_pool1);
+
+    // Test that the option has been correctly updated in the database.
+    returned_opt_boot_file_name =
+        returned_pool1->getCfgOption()->get(DHCP4_OPTION_SPACE, DHO_BOOT_FILE_NAME);
+    ASSERT_TRUE(returned_opt_boot_file_name.option_);
+    EXPECT_TRUE(returned_opt_boot_file_name.equals(*opt_boot_file_name));
+
+    // Delete option from the pool.
+    cbptr_->deleteOption4(ServerSelector::UNASSIGNED(),
+                          pool->getFirstAddress(),
+                          pool->getLastAddress(),
+                          opt_boot_file_name->option_->getType(),
+                          opt_boot_file_name->space_name_);
+
+    // Fetch the subnet and the pool from the database again to make sure
+    // that the option is really gone.
+    returned_subnet = cbptr_->getSubnet4(ServerSelector::UNASSIGNED(),
+                                         subnet->getID());
+    ASSERT_TRUE(returned_subnet);
+    const PoolPtr returned_pool2 = returned_subnet->getPool(Lease::TYPE_V4,
+                                                            IOAddress("192.0.2.10"));
+    ASSERT_TRUE(returned_pool2);
+
+    // Option should be gone.
+    EXPECT_FALSE(returned_pool2->getCfgOption()->get(DHCP4_OPTION_SPACE,
+                                                     DHO_BOOT_FILE_NAME).option_);
+}
+
 // This test verifies that shared network level option can be added,
 // updated and deleted.
 TEST_F(MySqlConfigBackendDHCPv4Test, createUpdateDeleteSharedNetworkOption4) {
index b4046f8dc7ae30de18012412a0bfe81ceb2740f4..cf09f993325bab364966d714d3e49ab821a84516 100644 (file)
@@ -773,6 +773,14 @@ ALTER TABLE hosts
 INSERT INTO dhcp_option_scope (scope_id, scope_name)
     VALUES(4, "shared-network");
 
+# Add scope for pool specific options.
+INSERT INTO dhcp_option_scope (scope_id, scope_name)
+    VALUES(5, "pool");
+
+# Add scope for PD pool specific options.
+INSERT INTO dhcp_option_scope (scope_id, scope_name)
+    VALUES(6, "pd-pool");
+
 -- -----------------------------------------------------
 -- Table `modification`
 -- -----------------------------------------------------
@@ -1033,6 +1041,17 @@ CREATE TABLE IF NOT EXISTS dhcp4_options_server (
         ON DELETE NO ACTION ON UPDATE NO ACTION
 ) ENGINE=InnoDB;
 
+# Create trigger which removes pool specific options upon removal of
+# the pool.
+DELIMITER $$
+CREATE TRIGGER dhcp4_pool_BDEL BEFORE DELETE ON dhcp4_pool FOR EACH ROW
+-- Edit trigger body code below this line. Do not edit lines above this one
+BEGIN
+DELETE FROM dhcp4_options WHERE scope_id = 5 AND pool_id = OLD.id;
+END
+$$
+DELIMITER ;
+
 -- -----------------------------------------------------
 -- Table `dhcp6_server`
 -- -----------------------------------------------------
@@ -1288,6 +1307,17 @@ CREATE TABLE IF NOT EXISTS dhcp6_options_server (
         ON DELETE NO ACTION ON UPDATE NO ACTION
 ) ENGINE=InnoDB;
 
+# Create trigger which removes pool specific options upon removal of
+# the pool.
+DELIMITER $$
+CREATE TRIGGER dhcp6_pool_BDEL BEFORE DELETE ON dhcp6_pool FOR EACH ROW
+-- Edit trigger body code below this line. Do not edit lines above this one
+BEGIN
+DELETE FROM dhcp6_options WHERE scope_id = 5 AND pool_id = OLD.id;
+END
+$$
+DELIMITER ;
+
 # Update the schema version number
 UPDATE schema_version
 SET version = '7', minor = '0';
index 89f8896b830856c67d25b3c8265a4a50c6e0c697..fb8beff7ccc6c484cb5c0a8daa241759cb20f5e4 100644 (file)
@@ -116,6 +116,14 @@ UPDATE dhcp6_options SET dhcp6_subnet_id = NULL WHERE dhcp6_subnet_id = 0;
 INSERT INTO dhcp_option_scope (scope_id, scope_name)
     VALUES(4, "shared-network");
 
+# Add scope for pool specific options.
+INSERT INTO dhcp_option_scope (scope_id, scope_name)
+    VALUES(5, "pool");
+
+# Add scope for PD pool specific options.
+INSERT INTO dhcp_option_scope (scope_id, scope_name)
+    VALUES(6, "pd-pool");
+
 # Create table modification
 CREATE TABLE IF NOT EXISTS modification (
   id TINYINT(3) NOT NULL,
@@ -359,6 +367,17 @@ CREATE TABLE IF NOT EXISTS dhcp4_options_server (
         ON DELETE NO ACTION ON UPDATE NO ACTION
 ) ENGINE=InnoDB;
 
+# Create trigger which removes pool specific options upon removal of
+# the pool.
+DELIMITER $$
+CREATE TRIGGER dhcp4_pool_BDEL BEFORE DELETE ON dhcp4_pool FOR EACH ROW
+-- Edit trigger body code below this line. Do not edit lines above this one
+BEGIN
+DELETE FROM dhcp4_options WHERE scope_id = 5 AND pool_id = OLD.id;
+END
+$$
+DELIMITER ;
+
 # Create table dhcp6_server
 #
 CREATE TABLE IF NOT EXISTS dhcp6_server (
@@ -597,6 +616,17 @@ CREATE TABLE IF NOT EXISTS dhcp6_options_server (
         ON DELETE NO ACTION ON UPDATE NO ACTION
 ) ENGINE=InnoDB;
 
+# Create trigger which removes pool specific options upon removal of
+# the pool.
+DELIMITER $$
+CREATE TRIGGER dhcp6_pool_BDEL BEFORE DELETE ON dhcp6_pool FOR EACH ROW
+-- Edit trigger body code below this line. Do not edit lines above this one
+BEGIN
+DELETE FROM dhcp6_options WHERE scope_id = 5 AND pool_id = OLD.id;
+END
+$$
+DELIMITER ;
+
 # Update the schema version number
 UPDATE schema_version
 SET version = '7', minor = '0';