]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#93,!35] Implemented fetching option definitions from the database.
authorMarcin Siodelski <marcin@isc.org>
Wed, 26 Sep 2018 16:54:09 +0000 (18:54 +0200)
committerMarcin Siodelski <marcin@isc.org>
Mon, 8 Oct 2018 14:39:22 +0000 (16:39 +0200)
src/bin/admin/tests/mysql_tests.sh.in
src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc
src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc
src/lib/dhcp/option_definition.cc
src/lib/dhcp/option_definition.h
src/share/database/scripts/mysql/dhcpdb_create.mysql
src/share/database/scripts/mysql/upgrade_6.0_to_7.0.sh.in

index 303f82ec2e19966ed62051282594f9ee4a9547f5..e62136b1b351f1f10ea4b48a3eb32fcb5faa0c74 100644 (file)
@@ -494,7 +494,7 @@ EOF
     run_statement "dhcp4_global_parameter_server" "$qry"
 
     # table: dhcp4_option_def
-    qry="select id, code, space, modification_ts, array, encapsulate, record_types, user_context from dhcp4_option_def"
+    qry="select id, code, name, space, type, modification_ts, array, encapsulate, record_types, user_context from dhcp4_option_def"
     run_statement "dhcp4_option_def" "$qry"
 
     # table: dhcp4_option_def_server
@@ -546,7 +546,7 @@ EOF
     run_statement "dhcp6_global_parameter_server" "$qry"
 
     # table: dhcp6_option_def
-    qry="select id, code, space, modification_ts, array, encapsulate, record_types, user_context from dhcp6_option_def"
+    qry="select id, code, name, space, type, modification_ts, array, encapsulate, record_types, user_context from dhcp6_option_def"
     run_statement "dhcp6_option_def" "$qry"
 
     # table: dhcp6_option_def_server
index 6e962fc6b14bc6a10dce80173d4fc4423a7616a7..88667acc86b64c861a369dca6f76df9f088921c1 100644 (file)
@@ -8,6 +8,7 @@
 #include <database/db_exceptions.h>
 #include <dhcp/classify.h>
 #include <dhcp/dhcp6.h>
+#include <dhcp/option_data_types.h>
 #include <dhcpsrv/network.h>
 #include <dhcpsrv/pool.h>
 #include <dhcpsrv/lease.h>
@@ -48,11 +49,16 @@ public:
         GET_SHARED_NETWORK4_NAME,
         GET_ALL_SHARED_NETWORKS4,
         GET_MODIFIED_SHARED_NETWORKS4,
+        GET_OPTION_DEF4_CODE_SPACE,
+        GET_ALL_OPTION_DEFS4,
+        GET_MODIFIED_OPTION_DEFS4,
         INSERT_SUBNET4,
         INSERT_POOL4,
         INSERT_SHARED_NETWORK4,
+        INSERT_OPTION_DEF4,
         UPDATE_SUBNET4,
         UPDATE_SHARED_NETWORK4,
+        UPDATE_OPTION_DEF4,
         DELETE_POOLS4_SUBNET_ID,
         NUM_STATEMENTS
     };
@@ -322,12 +328,6 @@ public:
             (shared_network ? MySqlBinding::createString(shared_network->getName()) :
              MySqlBinding::createNull());
 
-        // Create user context binding if user context exists.
-        auto context_element = subnet->getContext();
-        MySqlBindingPtr context_binding =
-            (context_element ? MySqlBinding::createString(context_element->str()) :
-             MySqlBinding::createNull());
-
         // Create input bindings.
         MySqlBindingCollection in_bindings = {
             MySqlBinding::createInteger<uint32_t>(subnet->getID()),
@@ -348,7 +348,7 @@ public:
             MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(subnet->getHostReservationMode())),
             MySqlBinding::condCreateString(subnet->getSname()),
             shared_network_binding,
-            context_binding,
+            createInputContextBinding(subnet),
             MySqlBinding::createInteger<uint32_t>(subnet->getValid())
         };
 
@@ -452,6 +452,7 @@ public:
             if ((last_network_id == 0) ||
                 (last_network_id != out_bindings[0]->getInteger<uint64_t>())) {
 
+                last_network_id = out_bindings[0]->getInteger<uint64_t>();
                 last_network.reset(new SharedNetwork4(out_bindings[1]->getString()));
 
                 // client_class
@@ -587,7 +588,171 @@ public:
             conn_.insertQuery(MySqlConfigBackendDHCPv4Impl::INSERT_SHARED_NETWORK4,
                               in_bindings);
         }
+    }
+
+    /// @brief Sends query to the database to retrieve multiple option
+    /// definitions.
+    ///
+    /// Query should order option definitions 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] option_defs Reference to the container where fetched
+    /// option definitions will be inserted.
+    void getOptionDefs4(const StatementIndex& index,
+                        const MySqlBindingCollection& in_bindings,
+                        OptionDefContainer& option_defs) {
+        // Create output bindings. The order must match that in the prepared
+        // statement.
+        MySqlBindingCollection out_bindings = {
+            MySqlBinding::createInteger<uint64_t>(), // id
+            MySqlBinding::createInteger<uint8_t>(), // code
+            MySqlBinding::createString(128), // name
+            MySqlBinding::createString(128), // space
+            MySqlBinding::createInteger<uint8_t>(), // type
+            MySqlBinding::createTimestamp(), // modification_ts
+            MySqlBinding::createInteger<uint8_t>(), // array
+            MySqlBinding::createString(128), // encapsulate
+            MySqlBinding::createString(512), // record_types
+            MySqlBinding::createString(65536) // user_context
+        };
+
+        uint64_t last_def_id = 0;
+
+        // Run select query.
+        conn_.selectQuery(index, in_bindings, out_bindings,
+                          [&option_defs, &last_def_id]
+                          (MySqlBindingCollection& out_bindings) {
+            // Get pointer to last fetched option definition.
+            OptionDefinitionPtr last_def;
+            if (!option_defs.empty()) {
+                last_def = *option_defs.rbegin();
+            }
+
+            // See if the last fetched definition is the one for which we now got
+            // the row of data. If not, it means that we need to create new option
+            // definition.
+            if ((last_def_id == 0) ||
+                (last_def_id != out_bindings[0]->getInteger<uint64_t>())) {
+
+                last_def_id = out_bindings[0]->getInteger<uint64_t>();
+
+                // Check array type, because depending on this value we have to use
+                // different constructor.
+                bool array_type = static_cast<bool>(out_bindings[6]->getInteger<uint8_t>());
+                if (array_type) {
+                    // Create array option.
+                    last_def.reset(new OptionDefinition(out_bindings[2]->getString(),
+                                                        out_bindings[1]->getInteger<uint8_t>(),
+                                                        static_cast<OptionDataType>
+                                                        (out_bindings[4]->getInteger<uint8_t>()),
+                                                        array_type));
+                } else {
+                    // Create non-array option.
+                    last_def.reset(new OptionDefinition(out_bindings[2]->getString(),
+                                                        out_bindings[1]->getInteger<uint8_t>(),
+                                                        static_cast<OptionDataType>
+                                                        (out_bindings[4]->getInteger<uint8_t>()),
+                                                        out_bindings[7]->getStringOrDefault("").c_str()));
+                }
+
+                // space
+                last_def->setOptionSpaceName(out_bindings[3]->getStringOrDefault(""));
+
+                // record_types
+                ElementPtr record_types_element = out_bindings[8]->getJSON();
+                if (record_types_element) {
+                    if (record_types_element->getType() != Element::list) {
+                        isc_throw(BadValue, "invalid record_types value "
+                                  << out_bindings[8]->getString());
+                    }
+                    // This element must contain a list of integers specifying
+                    // types of the record fields.
+                    for (auto i = 0; i < record_types_element->size(); ++i) {
+                        auto type_element = record_types_element->get(i);
+                        if (type_element->getType() != Element::integer) {
+                            isc_throw(BadValue, "record type values must be integers");
+                        }
+                        last_def->addRecordField(static_cast<OptionDataType>
+                                                 (type_element->intValue()));
+                    }
+                }
+
+                // Update modification time.
+                last_def->setModificationTime(out_bindings[5]->getTimestamp());
+
+                // Store created option definition.
+                option_defs.push_back(last_def);
+            }
+        });
+    }
+
+    /// @brief Sends query to retrieve single option definition by code and
+    /// option space.
+    ///
+    /// @param selector Server selector.
+    /// @param code Option code.
+    /// @param space Option space name.
+    ///
+    /// @return Pointer to the returned option definition or NULL if such
+    /// option definition doesn't exist.
+    OptionDefinitionPtr getOptionDef4(const ServerSelector& selector,
+                                      const uint16_t code,
+                                      const std::string& space) {
+        OptionDefContainer option_defs;
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(code)),
+            MySqlBinding::createString(space)
+        };
+        getOptionDefs4(GET_OPTION_DEF4_CODE_SPACE, in_bindings, option_defs);
+        return (option_defs.empty() ? OptionDefinitionPtr() : *option_defs.begin());
+    }
+
+    /// @brief Sends query to insert or update option definition.
+    ///
+    /// @param selector Server selector.
+    /// @param option_def Pointer to the option definition to be inserted or updated.
+    void createUpdateOptionDef4(const ServerSelector& selector,
+                                const OptionDefinitionPtr& option_def) {
+        ElementPtr record_types = Element::createList();
+        for (auto field : option_def->getRecordFields()) {
+            record_types->add(Element::create(static_cast<int>(field)));
+        }
+        MySqlBindingPtr record_types_binding = record_types->empty() ?
+            MySqlBinding::createNull() : MySqlBinding::createString(record_types->str());
 
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(option_def->getCode())),
+            MySqlBinding::createString(option_def->getName()),
+            MySqlBinding::createString(option_def->getOptionSpaceName().empty() ?
+                                       "dhcp4" : option_def->getOptionSpaceName()),
+            MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(option_def->getType())),
+            MySqlBinding::createTimestamp(option_def->getModificationTime()),
+            MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(option_def->getArrayType())),
+            MySqlBinding::createString(option_def->getEncapsulatedSpace()),
+            record_types_binding,
+            createInputContextBinding(option_def)
+        };
+
+        // If the shared network exists we are going to update this network.
+        OptionDefinitionPtr existing_definition = getOptionDef4(selector,
+                                                                option_def->getCode(),
+                                                                option_def->getOptionSpaceName());
+        if (existing_definition) {
+            // Need to add two more bindings for WHERE clause.
+            in_bindings.push_back(MySqlBinding::createInteger<uint8_t>(existing_definition->getCode()));
+            in_bindings.push_back(MySqlBinding::createString(existing_definition->getOptionSpaceName()));
+            conn_.updateDeleteQuery(MySqlConfigBackendDHCPv4Impl::UPDATE_OPTION_DEF4,
+                                    in_bindings);
+
+        } else {
+            // If the option definition doesn't exist, let's insert it.
+            conn_.insertQuery(MySqlConfigBackendDHCPv4Impl::INSERT_OPTION_DEF4,
+                              in_bindings);
+        }
     }
 
     /// @brief Creates input binding for relay addresses.
@@ -632,13 +797,14 @@ public:
 
     /// @brief Creates input binding for user context parameter.
     ///
-    /// @param network Pointer to a shared network or subnet for which binding
-    /// should be created.
+    /// @param network Pointer to a shared network, subnet or other configuration
+    /// element for which binding should be created.
     /// @return Pointer to the binding (possibly null binding if context is
     /// null).
-    MySqlBindingPtr createInputContextBinding(const NetworkPtr& network) {
+    template<typename T>
+    MySqlBindingPtr createInputContextBinding(const T& config_element) {
         // Create user context binding if user context exists.
-        auto context_element = network->getContext();
+        auto context_element = config_element->getContext();
         return (context_element ? MySqlBinding::createString(context_element->str()) :
                 MySqlBinding::createNull());
     }
@@ -844,6 +1010,56 @@ TaggedStatementArray tagged_statements = { {
       "WHERE n.modification_ts > ? "
       "ORDER BY n.id" },
 
+    // Retrieves option definition by code and space.
+    { MySqlConfigBackendDHCPv4Impl::GET_OPTION_DEF4_CODE_SPACE,
+      "SELECT"
+      "  d.id,"
+      "  d.code,"
+      "  d.name,"
+      "  d.space,"
+      "  d.type,"
+      "  d.modification_ts,"
+      "  d.array,"
+      "  d.encapsulate,"
+      "  d.record_types,"
+      "  d.user_context "
+      "FROM dhcp4_option_def AS d "
+      "WHERE d.code = ? AND d.space = ? "
+      "ORDER BY d.id" },
+
+    // Retrieves all option definitions.
+    { MySqlConfigBackendDHCPv4Impl::GET_ALL_OPTION_DEFS4,
+      "SELECT"
+      "  d.id,"
+      "  d.code,"
+      "  d.name,"
+      "  d.space,"
+      "  d.type,"
+      "  d.modification_ts,"
+      "  d.array,"
+      "  d.encapsulate,"
+      "  d.record_types,"
+      "  d.user_context "
+      "FROM dhcp4_option_def AS d "
+      "ORDER BY d.id" },
+
+    // Retrieves modified option definitions.
+    { MySqlConfigBackendDHCPv4Impl::GET_MODIFIED_OPTION_DEFS4,
+      "SELECT"
+      "  d.id,"
+      "  d.code,"
+      "  d.name,"
+      "  d.space,"
+      "  d.type,"
+      "  d.modification_ts,"
+      "  d.array,"
+      "  d.encapsulate,"
+      "  d.record_types,"
+      "  d.user_context "
+      "FROM dhcp4_option_def AS d "
+      "WHERE modification_ts > ? "
+      "ORDER BY d.id" },
+
     // Insert a subnet.
     { MySqlConfigBackendDHCPv4Impl::INSERT_SUBNET4,
       "INSERT INTO dhcp4_subnet("
@@ -870,6 +1086,7 @@ TaggedStatementArray tagged_statements = { {
       ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,"
       "?, ?, ?, ?, ?, ?, ?, ?)" },
 
+    // Insert pool for a subnet.
     { MySqlConfigBackendDHCPv4Impl::INSERT_POOL4,
       "INSERT INTO dhcp4_pool("
       "  start_address,"
@@ -895,6 +1112,20 @@ TaggedStatementArray tagged_statements = { {
       "valid_lifetime"
       ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" },
 
+    // Insert option definition.
+    { MySqlConfigBackendDHCPv4Impl::INSERT_OPTION_DEF4,
+      "INSERT INTO dhcp4_option_def ("
+      "code,"
+      "name,"
+      "space,"
+      "type,"
+      "modification_ts,"
+      "array,"
+      "encapsulate,"
+      "record_types,"
+      "user_context"
+      ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" },
+
     // Update existing subnet.
     { MySqlConfigBackendDHCPv4Impl::UPDATE_SUBNET4,
       "UPDATE dhcp4_subnet SET"
@@ -937,6 +1168,20 @@ TaggedStatementArray tagged_statements = { {
       "  valid_lifetime = ? "
       "WHERE name = ?" },
 
+    // Update existing option definition.
+    { MySqlConfigBackendDHCPv4Impl::UPDATE_OPTION_DEF4,
+      "UPDATE dhcp4_option_def SET"
+      "  code = ?,"
+      "  name = ?,"
+      "  space = ?,"
+      "  type = ?,"
+      "  modification_ts = ?,"
+      "  array = ?,"
+      "  encapsulate = ?,"
+      "  record_types = ?,"
+      "  user_context = ? "
+      "WHERE code = ? AND space = ?" },
+
     // Delete pools for a subnet.
     { MySqlConfigBackendDHCPv4Impl::DELETE_POOLS4_SUBNET_ID,
       "DELETE FROM dhcp4_pool "
@@ -1063,19 +1308,29 @@ OptionDefinitionPtr
 MySqlConfigBackendDHCPv4::getOptionDef4(const ServerSelector& selector,
                                         const uint16_t code,
                                         const std::string& space) const {
-    isc_throw(NotImplemented, "not implemented");
+    return (impl_->getOptionDef4(selector, code, space));
 }
 
 OptionDefContainer
 MySqlConfigBackendDHCPv4::getAllOptionDefs4(const ServerSelector& selector) const {
-    isc_throw(NotImplemented, "not implemented");
+    OptionDefContainer option_defs;
+    MySqlBindingCollection in_bindings;
+    impl_->getOptionDefs4(MySqlConfigBackendDHCPv4Impl::GET_ALL_OPTION_DEFS4,
+                          in_bindings, option_defs);
+    return (option_defs);
 }
 
 OptionDefContainer
 MySqlConfigBackendDHCPv4::
 getModifiedOptionDefs4(const ServerSelector& selector,
                        const boost::posix_time::ptime& modification_time) const {
-    isc_throw(NotImplemented, "not implemented");
+    OptionDefContainer option_defs;
+    MySqlBindingCollection in_bindings = {
+        MySqlBinding::createTimestamp(modification_time)
+    };
+    impl_->getOptionDefs4(MySqlConfigBackendDHCPv4Impl::GET_MODIFIED_OPTION_DEFS4,
+                          in_bindings, option_defs);
+    return (option_defs);
 }
 
 util::OptionalValue<std::string>
@@ -1110,6 +1365,7 @@ MySqlConfigBackendDHCPv4::createUpdateSharedNetwork4(const ServerSelector& selec
 void
 MySqlConfigBackendDHCPv4::createUpdateOptionDef4(const ServerSelector& selector,
                                                  const OptionDefinitionPtr& option_def) {
+    impl_->createUpdateOptionDef4(selector, option_def);
 }
 
 void
index 8c24f78c2bf5a1e656985d28c0ab6dde79ff51d2..62f5798bf731ff7c9f18db45b550fa30bae03305 100644 (file)
@@ -51,6 +51,7 @@ public:
         // Create test data.
         initTestSubnets();
         initTestSharedNetworks();
+        initTestOptionDefs();
         initTimestamps();
     }
 
@@ -147,6 +148,31 @@ public:
         test_networks_.push_back(shared_network);
     }
 
+    /// @brief Creates several option definitions used in tests.
+    void initTestOptionDefs() {
+        ElementPtr user_context = Element::createMap();
+        user_context->set("foo", Element::create("bar"));
+
+        OptionDefinitionPtr option_def(new OptionDefinition("foo", 234, "string",
+                                                            "espace"));
+        option_def->setOptionSpaceName("dhcp4");
+        test_option_defs_.push_back(option_def);
+
+        option_def.reset(new OptionDefinition("bar", 234, "uint32", true));
+        option_def->setOptionSpaceName("dhcp4");
+        test_option_defs_.push_back(option_def);
+
+        option_def.reset(new OptionDefinition("fish", 235, "record", true));
+        option_def->setOptionSpaceName("dhcp4");
+        option_def->addRecordField("uint32");
+        option_def->addRecordField("string");
+        test_option_defs_.push_back(option_def);
+
+        option_def.reset(new OptionDefinition("whale", 236, "string"));
+        option_def->setOptionSpaceName("xyz");
+        test_option_defs_.push_back(option_def);
+    }
+
     /// @brief Initialize posix time values used in tests.
     void initTimestamps() {
         // Current time minus 1 hour to make sure it is in the past.
@@ -164,6 +190,9 @@ public:
     /// @brief Holds pointers to shared networks used in tests.
     std::vector<SharedNetwork4Ptr> test_networks_;
 
+    /// @brief Holds pointers to option definitions used in tests.
+    std::vector<OptionDefinitionPtr> test_option_defs_;
+
     /// @brief Holds timestamp values used in tests.
     std::map<std::string, boost::posix_time::ptime> timestamps_;
 
@@ -352,5 +381,95 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getModifiedSharedNetworks4) {
     ASSERT_TRUE(networks.empty());
 }
 
+// Test that subnet can be inserted, fetched, updated and then fetched again.
+TEST_F(MySqlConfigBackendDHCPv4Test, getOptionDef4) {
+    // Insert new option definition.
+    OptionDefinitionPtr option_def = test_option_defs_[0];
+    cbptr_->createUpdateOptionDef4(ServerSelector::UNASSIGNED(), option_def);
+
+    // Fetch this option_definition by subnet identifier.
+    OptionDefinitionPtr returned_option_def =
+        cbptr_->getOptionDef4(ServerSelector::UNASSIGNED(),
+                              test_option_defs_[0]->getCode(),
+                              test_option_defs_[0]->getOptionSpaceName());
+    ASSERT_TRUE(returned_option_def);
+
+    EXPECT_TRUE(returned_option_def->equals(*option_def));
+
+    // Update the option definition in the database.
+    OptionDefinitionPtr option_def2 = test_option_defs_[1];
+    cbptr_->createUpdateOptionDef4(ServerSelector::UNASSIGNED(), option_def2);
+
+    // Fetch updated option definition and see if it matches.
+    returned_option_def = cbptr_->getOptionDef4(ServerSelector::UNASSIGNED(),
+                                                test_option_defs_[1]->getCode(),
+                                                test_option_defs_[1]->getOptionSpaceName());
+    EXPECT_TRUE(returned_option_def->equals(*option_def2));
+}
+
+// Test that all shared networks can be fetched.
+TEST_F(MySqlConfigBackendDHCPv4Test, getAllOptionDefs4) {
+    // Insert test option definitions into the database. Note that the second
+    // option definition will overwrite the first option definition as they use
+    // the same code and space.
+    for (auto option_def : test_option_defs_) {
+        cbptr_->createUpdateOptionDef4(ServerSelector::UNASSIGNED(), option_def);
+    }
+
+    // Fetch all option_definitions.
+    OptionDefContainer option_defs = cbptr_->getAllOptionDefs4(ServerSelector::UNASSIGNED());
+    ASSERT_EQ(test_option_defs_.size() - 1, option_defs.size());
+
+    // See if option definitions are returned ok.
+    for (auto def = option_defs.begin(); def != option_defs.end(); ++def) {
+        bool success = false;
+        for (auto i = 1; i < test_option_defs_.size(); ++i) {
+            if ((*def)->equals(*test_option_defs_[i])) {
+                success = true;
+            }
+        }
+        ASSERT_TRUE(success) << "failed for option definition " << (*def)->getCode()
+            << ", option space " << (*def)->getOptionSpaceName();
+    }
+}
+
+// Test that option definitions modified after given time can be fetched.
+TEST_F(MySqlConfigBackendDHCPv4Test, getModifiedOptionDefinitions4) {
+    // Explicitly set timestamps of option definitions. First option
+    // definition has a timestamp pointing to the future. Second option
+    // definition has timestamp pointing to the past (yesterday).
+    // Third option definitions has a timestamp pointing to the
+    // past (an hour ago).
+    test_option_defs_[1]->setModificationTime(timestamps_["tomorrow"]);
+    test_option_defs_[2]->setModificationTime(timestamps_["yesterday"]);
+    test_option_defs_[3]->setModificationTime(timestamps_["today"]);
+
+    // Insert option definitions into the database.
+    for (int i = 1; i < test_networks_.size(); ++i) {
+        cbptr_->createUpdateOptionDef4(ServerSelector::UNASSIGNED(),
+                                       test_option_defs_[i]);
+    }
+
+    // Fetch option definitions with timestamp later than today. Only one
+    // option definition should be returned.
+    OptionDefContainer
+        option_defs = cbptr_->getModifiedOptionDefs4(ServerSelector::UNASSIGNED(),
+                                                     timestamps_["today"]);
+    ASSERT_EQ(1, option_defs.size());
+
+    // Fetch option definitions with timestamp later than yesterday. We
+    // should get two option definitions.
+    option_defs = cbptr_->getModifiedOptionDefs4(ServerSelector::UNASSIGNED(),
+                                                 timestamps_["yesterday"]);
+    ASSERT_EQ(2, option_defs.size());
+
+    // Fetch option definitions with timestamp later than tomorrow. Nothing
+    // should be returned.
+    option_defs = cbptr_->getModifiedOptionDefs4(ServerSelector::UNASSIGNED(),
+                                              timestamps_["tomorrow"]);
+    ASSERT_TRUE(option_defs.empty());
+}
+
+
 
 }
index 4f77621a3312559a201371e2ab568c8463fe30f6..b353286477b46b2ed13351e314b2ef6a562d2607 100644 (file)
@@ -48,7 +48,10 @@ OptionDefinition::OptionDefinition(const std::string& name,
       code_(code),
       type_(OPT_UNKNOWN_TYPE),
       array_type_(array_type),
-      encapsulated_space_("") {
+      encapsulated_space_(""),
+      record_fields_(),
+      user_context_(),
+      option_space_name_() {
     // Data type is held as enum value by this class.
     // Use the provided option type string to get the
     // corresponding enum value.
@@ -77,7 +80,10 @@ OptionDefinition::OptionDefinition(const std::string& name,
       // corresponding enum value.
       type_(OptionDataTypeUtil::getDataType(type)),
       array_type_(false),
-      encapsulated_space_(encapsulated_space) {
+      encapsulated_space_(encapsulated_space),
+      record_fields_(),
+      user_context_(),
+      option_space_name_() {
 }
 
 OptionDefinition::OptionDefinition(const std::string& name,
@@ -88,7 +94,10 @@ OptionDefinition::OptionDefinition(const std::string& name,
       code_(code),
       type_(type),
       array_type_(false),
-      encapsulated_space_(encapsulated_space) {
+      encapsulated_space_(encapsulated_space),
+      record_fields_(),
+      user_context_(),
+      option_space_name_() {
 }
 
 bool
@@ -98,7 +107,8 @@ OptionDefinition::equals(const OptionDefinition& other) const {
             type_ == other.type_ &&
             array_type_ == other.array_type_ &&
             encapsulated_space_ == other.encapsulated_space_ &&
-            record_fields_ == other.record_fields_);
+            record_fields_ == other.record_fields_ &&
+            option_space_name_ == other.option_space_name_);
 }
 
 void
index 21e021de05d5249d48e9825defa36d65b0362bcf..076b96187b75cf980623a6d3d5ec9ecbf900ec25 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2018 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -10,6 +10,7 @@
 #include <dhcp/option.h>
 #include <dhcp/option_data_types.h>
 #include <dhcp/option_space_container.h>
+#include <cc/stamped_element.h>
 #include <cc/user_context.h>
 
 #include <boost/multi_index/hashed_index.hpp>
@@ -133,7 +134,7 @@ class OptionIntArray;
 /// @todo Extend this class to use custom namespaces.
 /// @todo Extend this class with more factory functions.
 /// @todo Derive from UserContext without breaking the multi index.
-class OptionDefinition {
+class OptionDefinition : public data::StampedElement {
 public:
 
     /// List of fields within the record.
@@ -307,6 +308,32 @@ public:
         user_context_.contextToElement(map);
     }
 
+    /// @brief Returns option space name.
+    ///
+    /// Option definitions are associated with option spaces. Typically,
+    /// such association is made when the option definition is put into
+    /// the @c CfgOptionDef structure. However, in some cases it is also
+    /// required to associate option definition with the particular option
+    /// space outside of that structure. In particular, when the option
+    /// definition is fetched from a database. The database configuration
+    /// backend will set option space upon return of the option definition.
+    /// In other cases this value won't be set.
+    ///
+    /// @return Option space name or empty string if option space
+    /// name is not set.
+    std::string getOptionSpaceName() const {
+        return (option_space_name_);
+    }
+
+    /// @brief Sets option space name for option definition.
+    ///
+    /// See @c getOptionSpaceName to learn when option space name is set.
+    ///
+    /// @param option_space_name New option space name.
+    void setOptionSpaceName(const std::string& option_space_name) {
+        option_space_name_ = option_space_name;
+    }
+
     /// @brief Check if the option definition is valid.
     ///
     /// Note that it is a responsibility of the code that created
@@ -733,6 +760,8 @@ private:
     RecordFieldsCollection record_fields_;
     /// User context
     data::UserContext user_context_;
+    /// Option space name
+    std::string option_space_name_;
 };
 
 
index fcec8e1779f90109e71b1dfaea3b6d294f570f91..6c7511d5b7c19d9aa8e4e50ab203b17d657fa199 100644 (file)
@@ -857,7 +857,9 @@ CREATE TABLE IF NOT EXISTS dhcp4_global_parameter_server (
 CREATE TABLE IF NOT EXISTS dhcp4_option_def (
     id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
     code TINYINT(3) UNSIGNED NOT NULL,
+    name VARCHAR(128) NOT NULL,
     space VARCHAR(128) NOT NULL,
+    type TINYINT UNSIGNED NOT NULL,
     modification_ts TIMESTAMP NOT NULL,
     array TINYINT(1) NOT NULL,
     encapsulate VARCHAR(128) NOT NULL,
@@ -1005,7 +1007,7 @@ ALTER TABLE dhcp4_options MODIFY option_id BIGINT(20) UNSIGNED NOT NULL AUTO_INC
 ALTER TABLE dhcp4_options
     ADD COLUMN shared_network_name VARCHAR(128) DEFAULT NULL,
     ADD COLUMN pool_id BIGINT(20) UNSIGNED DEFAULT NULL,
-    ADD COLUMN modification_ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;
+    ADD COLUMN modification_ts TIMESTAMP NOT NULL;
 
 -- -----------------------------------------------------
 -- Table `dhcp4_options_server`
@@ -1096,7 +1098,9 @@ CREATE TABLE IF NOT EXISTS dhcp6_global_parameter_server (
 CREATE TABLE IF NOT EXISTS dhcp6_option_def (
     id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
     code TINYINT(3) UNSIGNED NOT NULL,
+    name VARCHAR(128) NOT NULL,
     space VARCHAR(128) NOT NULL,
+    type TINYINT UNSIGNED NOT NULL,
     modification_ts TIMESTAMP NOT NULL,
     array TINYINT(1) NOT NULL,
     encapsulate VARCHAR(128) NOT NULL,
@@ -1258,7 +1262,7 @@ ALTER TABLE dhcp6_options
     ADD COLUMN shared_network_name VARCHAR(128) DEFAULT NULL,
     ADD COLUMN pool_id BIGINT(20) UNSIGNED DEFAULT NULL,
     ADD COLUMN pd_pool_id BIGINT(20) UNSIGNED DEFAULT NULL,
-    ADD COLUMN modification_ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;
+    ADD COLUMN modification_ts TIMESTAMP NOT NULL;
 
 -- -----------------------------------------------------
 -- Table `dhcp6_options_server`
index fcb10c2d3150dc07a8f82e33434a726bcae04072..9549d40355e5a04f1ef91b1e86b3a2c63f638890 100644 (file)
@@ -192,6 +192,7 @@ CREATE TABLE IF NOT EXISTS dhcp4_global_parameter_server (
 CREATE TABLE IF NOT EXISTS dhcp4_option_def (
     id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
     code TINYINT(3) UNSIGNED NOT NULL,
+    name VARCHAR(128) NOT NULL,
     space VARCHAR(128) NOT NULL,
     modification_ts TIMESTAMP NOT NULL,
     array TINYINT(1) NOT NULL,
@@ -333,7 +334,7 @@ ALTER TABLE dhcp4_options MODIFY option_id BIGINT(20) UNSIGNED NOT NULL AUTO_INC
 ALTER TABLE dhcp4_options
     ADD COLUMN shared_network_name VARCHAR(128) DEFAULT NULL,
     ADD COLUMN pool_id BIGINT(20) UNSIGNED DEFAULT NULL,
-    ADD COLUMN modification_ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;
+    ADD COLUMN modification_ts TIMESTAMP NOT NULL;
 
 # Create table dhcp4_options_server
 # M-to-M cross-reference between options and servers
@@ -417,6 +418,7 @@ CREATE TABLE IF NOT EXISTS dhcp6_global_parameter_server (
 CREATE TABLE IF NOT EXISTS dhcp6_option_def (
     id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
     code TINYINT(3) UNSIGNED NOT NULL,
+    name VARCHAR(128) NOT NULL,
     space VARCHAR(128) NOT NULL,
     modification_ts TIMESTAMP NOT NULL,
     array TINYINT(1) NOT NULL,
@@ -569,7 +571,7 @@ ALTER TABLE dhcp6_options
     ADD COLUMN shared_network_name VARCHAR(128) DEFAULT NULL,
     ADD COLUMN pool_id BIGINT(20) UNSIGNED DEFAULT NULL,
     ADD COLUMN pd_pool_id BIGINT(20) UNSIGNED DEFAULT NULL,
-    ADD COLUMN modification_ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;
+    ADD COLUMN modification_ts TIMESTAMP NOT NULL;
 
 # Create table dhcp6_options_server
 # M-to-M cross-reference between options and servers