uint64_t deleteOption4(const ServerSelector& server_selector,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes) {
+ const ClientClassesPtr client_classes) {
MySqlBindingCollection in_bindings = {
MySqlBinding::createInteger<uint8_t>(code),
MySqlBinding::createString(space),
const SubnetID& subnet_id,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes) {
+ const ClientClassesPtr client_classes) {
MySqlBindingCollection in_bindings = {
MySqlBinding::createInteger<uint32_t>(static_cast<uint32_t>(subnet_id)),
MySqlBinding::createInteger<uint8_t>(code),
const IOAddress& pool_end_address,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes) {
+ const ClientClassesPtr client_classes) {
MySqlBindingCollection in_bindings = {
MySqlBinding::createInteger<uint8_t>(code),
MySqlBinding::createString(space),
+ createClientClassesForWhereClause(client_classes),
MySqlBinding::createInteger<uint32_t>(pool_start_address.toUint32()),
- MySqlBinding::createInteger<uint32_t>(pool_end_address.toUint32()),
- createClientClassesForWhereClause(client_classes)
+ MySqlBinding::createInteger<uint32_t>(pool_end_address.toUint32())
};
// Run DELETE.
const std::string& shared_network_name,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes) {
+ const ClientClassesPtr client_classes) {
MySqlBindingCollection in_bindings = {
MySqlBinding::createString(shared_network_name),
MySqlBinding::createInteger<uint8_t>(code),
MYSQL_GET_OPTION_DEF(dhcp4, AND d.modification_ts >= ?)
},
- // Retrieves global option by code and space.
+ // Retrieves global option by code, space and client-classes.
{ MySqlConfigBackendDHCPv4Impl::GET_OPTION4_CODE_SPACE,
- MYSQL_GET_OPTION4(AND o.scope_id = 0 AND o.code = ? AND o.space = ?)
+ MYSQL_GET_OPTION4(AND o.scope_id = 0 AND o.code = ? AND o.space = ?
+ AND o.client_classes LIKE ?)
},
// Retrieves all global options.
// Delete single option from a subnet.
{ MySqlConfigBackendDHCPv4Impl::DELETE_OPTION4_SUBNET_ID,
MYSQL_DELETE_OPTION_NO_TAG(dhcp4,
- WHERE o.scope_id = 1 AND o.dhcp4_subnet_id = ? AND o.code = ? AND o.space = ?)
+ WHERE (o.scope_id = 1 AND o.dhcp4_subnet_id = ? AND o.code = ? AND o.space = ?
+ AND o.client_classes LIKE ?))
},
// Delete single option from a pool.
{ MySqlConfigBackendDHCPv4Impl::DELETE_OPTION4_POOL_RANGE,
- MYSQL_DELETE_OPTION_POOL_RANGE(dhcp4, o.scope_id = 5 AND o.code = ? AND o.space = ?)
+ MYSQL_DELETE_OPTION_POOL_RANGE(dhcp4, o.scope_id = 5 AND o.code = ? AND o.space = ?
+ AND o.client_classes LIKE ?)
},
// Delete single option from a shared network.
{ MySqlConfigBackendDHCPv4Impl::DELETE_OPTION4_SHARED_NETWORK,
MYSQL_DELETE_OPTION_NO_TAG(dhcp4,
- WHERE o.scope_id = 4 AND o.shared_network_name = ? AND o.code = ? AND o.space = ?)
+ WHERE (o.scope_id = 4 AND o.shared_network_name = ? AND o.code = ? AND o.space = ?
+ AND o.client_classes LIKE ?))
},
// Delete options belonging to a subnet.
OptionDescriptorPtr
MySqlConfigBackendDHCPv4::getOption4(const ServerSelector& server_selector,
const uint16_t code,
- const std::string& space) const {
+ const std::string& space,
+ const ClientClassesPtr client_classes) const {
LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_GET_OPTION4)
.arg(code).arg(space);
return (impl_->getOption(MySqlConfigBackendDHCPv4Impl::GET_OPTION4_CODE_SPACE,
- Option::V4, server_selector, code, space));
+ Option::V4, server_selector, code, space, client_classes));
}
OptionContainer
MySqlConfigBackendDHCPv4::deleteOption4(const ServerSelector& server_selector,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes) {
+ const ClientClassesPtr client_classes) {
LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_OPTION4)
.arg(code).arg(space);
uint64_t result = impl_->deleteOption4(server_selector, code, space, client_classes);
const std::string& shared_network_name,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes) {
+ const ClientClassesPtr client_classes) {
/// @todo In the future we might use the server selector to make sure that the
/// option is only deleted if the pool belongs to a given server. For now, we
/// just delete it when there is a match with the parent object.
const SubnetID& subnet_id,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes) {
+ const ClientClassesPtr client_classes) {
/// @todo In the future we might use the server selector to make sure that the
/// option is only deleted if the pool belongs to a given server. For now, we
/// just delete it when there is a match with the parent object.
const asiolink::IOAddress& pool_end_address,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes) {
+ const ClientClassesPtr client_classes) {
/// @todo In the future we might use the server selector to make sure that the
/// option is only deleted if the pool belongs to a given server. For now, we
/// just delete it when there is a match with the parent object.
/// @brief Retrieves single option by code and space.
///
/// @param server_selector Server selector.
+ /// @param code code of the option to be deleted.
+ /// @param space option space of the option to be deleted.
+ /// @param client_classes Optional client classes list of the option to be deleted.
+ /// Defaults to an empty pointer.
/// @return Pointer to the retrieved option descriptor or null if
/// no option was found.
/// @throw NotImplemented if server selector is "unassigned".
virtual OptionDescriptorPtr
getOption4(const db::ServerSelector& server_selector, const uint16_t code,
- const std::string& space) const;
+ const std::string& space,
+ const ClientClassesPtr client_classes = ClientClassesPtr()) const;
/// @brief Retrieves all global options.
///
virtual uint64_t
deleteOption4(const db::ServerSelector& server_selector, const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes = ClientClassesPtr());
+ const ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes shared network level option.
///
const std::string& shared_network_name,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes = ClientClassesPtr());
+ const ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes subnet level option.
///
virtual uint64_t
deleteOption4(const db::ServerSelector& server_selector, const SubnetID& subnet_id,
const uint16_t code, const std::string& space,
- ClientClassesPtr client_classes = ClientClassesPtr());
+ const ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes pool level option.
///
const asiolink::IOAddress& pool_end_address,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes = ClientClassesPtr());
+ const ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes global parameter.
///
const Option::Universe& universe,
const ServerSelector& server_selector,
const uint16_t code,
- const std::string& space) {
+ const std::string& space,
+ const ClientClassesPtr client_classes) {
if (server_selector.amUnassigned()) {
isc_throw(NotImplemented, "managing configuration for no particular server"
in_bindings.push_back(MySqlBinding::createInteger<uint16_t>(code));
}
in_bindings.push_back(MySqlBinding::createString(space));
+
+ /// @todo Remove the if when v6 is ready for this.
+ if (universe == Option::V4) {
+ in_bindings.push_back(createClientClassesForWhereClause(client_classes));
+ }
getOptions(index, in_bindings, universe, options);
return (options.empty() ? OptionDescriptorPtr() :
OptionDescriptor::create(*options.begin()));
}
+
OptionContainer
MySqlConfigBackendImpl::getAllOptions(const int index,
const Option::Universe& universe,
}
db::MySqlBindingPtr
-MySqlConfigBackendImpl::createClientClassesForWhereClause(ClientClassesPtr client_classes) {
+MySqlConfigBackendImpl::createClientClassesForWhereClause(const ClientClassesPtr client_classes) {
return (client_classes ? createInputClientClassesBinding(*client_classes)
: MySqlBinding::createString("%"));
}
/// @param server_selector Server selector.
/// @param code Option code.
/// @param space Option space name.
+ /// @param client_classes ClientClasses collection containing the class names.
+ /// Defaults to an empty pointer.
///
/// @return Pointer to the returned option or NULL if such option
/// doesn't exist.
const Option::Universe& universe,
const db::ServerSelector& server_selector,
const uint16_t code,
- const std::string& space);
+ const std::string& space,
+ const ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Sends query to retrieve all global options.
///
multipleAuditEntriesTest();
}
+TEST_F(MySqlConfigBackendDHCPv4Test, globalOption4WithClientClassesTest) {
+ globalOption4WithClientClassesTest();
+}
+
+TEST_F(MySqlConfigBackendDHCPv4Test, sharedNetworkOption4WithClientClassesTest) {
+ sharedNetworkOption4WithClientClassesTest();
+}
+
+TEST_F(MySqlConfigBackendDHCPv4Test, subnetOption4WithClientClassesTest) {
+ subnetOption4WithClientClassesTest();
+}
+
+TEST_F(MySqlConfigBackendDHCPv4Test, poolOption4WithClientClassesTest) {
+ poolOption4WithClientClassesTest();
+}
+
/// @brief Test fixture for verifying database connection loss-recovery
/// behavior.
class MySqlConfigBackendDHCPv4DbLostCallbackTest : public GenericConfigBackendDbLostCallbackTest {
uint64_t deleteOption4(const ServerSelector& server_selector,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes) {
+ const ClientClassesPtr client_classes) {
PsqlBindArray in_bindings;
in_bindings.add(code);
in_bindings.add(space);
const SubnetID& subnet_id,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes) {
+ const ClientClassesPtr client_classes) {
PsqlBindArray in_bindings;
in_bindings.add(subnet_id);
in_bindings.add(code);
const IOAddress& pool_end_address,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes) {
+ const ClientClassesPtr client_classes) {
PsqlBindArray in_bindings;
in_bindings.addInet4(pool_start_address);
in_bindings.addInet4(pool_end_address);
const std::string& shared_network_name,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes) {
+ const ClientClassesPtr client_classes) {
PsqlBindArray in_bindings;
in_bindings.add(shared_network_name);
in_bindings.add(code);
// Retrieves global option by code and space.
{
// PgSqlConfigBackendDHCPv4Impl::GET_OPTION4_CODE_SPACE,
- 3,
+ 4,
{
OID_VARCHAR, // 1 server_tag
OID_INT2, // 2 code
- OID_VARCHAR // 3 space
+ OID_VARCHAR, // 3 space
+ OID_TEXT // 4 client_classes
},
"GET_OPTION4_CODE_SPACE",
- PGSQL_GET_OPTION4(AND o.scope_id = 0 AND o.code = $2 AND o.space = $3)
+ PGSQL_GET_OPTION4(AND o.scope_id = 0 AND o.code = $2 AND o.space = $3
+ AND o.client_classes LIKE $4)
},
// Retrieves all global options.
OptionDescriptorPtr
PgSqlConfigBackendDHCPv4::getOption4(const ServerSelector& server_selector,
const uint16_t code,
- const std::string& space) const {
+ const std::string& space,
+ const ClientClassesPtr client_classes) const {
LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_GET_OPTION4)
.arg(code).arg(space);
return (impl_->getOption(PgSqlConfigBackendDHCPv4Impl::GET_OPTION4_CODE_SPACE,
- Option::V4, server_selector, code, space));
+ Option::V4, server_selector, code, space, client_classes));
}
OptionContainer
PgSqlConfigBackendDHCPv4::deleteOption4(const ServerSelector& server_selector,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes) {
+ const ClientClassesPtr client_classes) {
LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_OPTION4)
.arg(code).arg(space);
uint64_t result = impl_->deleteOption4(server_selector, code, space, client_classes);
const std::string& shared_network_name,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes) {
+ const ClientClassesPtr client_classes) {
/// @todo In the future we might use the server selector to make sure that the
/// option is only deleted if the pool belongs to a given server. For now, we
/// just delete it when there is a match with the parent object.
const SubnetID& subnet_id,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes) {
+ const ClientClassesPtr client_classes) {
/// @todo In the future we might use the server selector to make sure that the
/// option is only deleted if the pool belongs to a given server. For now, we
/// just delete it when there is a match with the parent object.
const asiolink::IOAddress& pool_end_address,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes) {
+ const ClientClassesPtr client_classes) {
/// @todo In the future we might use the server selector to make sure that the
/// option is only deleted if the pool belongs to a given server. For now, we
/// just delete it when there is a match with the parent object.
/// @brief Retrieves single option by code and space.
///
/// @param server_selector Server selector.
+ /// @param code Code of the option to be deleted.
+ /// @param space Option space of the option to be deleted.
+ /// @param client_classes Optional client classes list of the option to be deleted.
+ /// Defaults to an empty pointer.
/// @return Pointer to the retrieved option descriptor or null if
/// no option was found.
/// @throw NotImplemented if server selector is "unassigned".
virtual OptionDescriptorPtr
getOption4(const db::ServerSelector& server_selector, const uint16_t code,
- const std::string& space) const;
+ const std::string& space,
+ const ClientClassesPtr client_classes = ClientClassesPtr()) const;
/// @brief Retrieves all global options.
///
virtual uint64_t
deleteOption4(const db::ServerSelector& server_selector, const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes = ClientClassesPtr());
+ const ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes shared network level option.
///
const std::string& shared_network_name,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes = ClientClassesPtr());
+ const ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes subnet level option.
///
const SubnetID& subnet_id,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes = ClientClassesPtr());
+ const ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes pool level option.
///
const asiolink::IOAddress& pool_end_address,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes = ClientClassesPtr());
+ const ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes global parameter.
///
const Option::Universe& universe,
const ServerSelector& server_selector,
const uint16_t code,
- const std::string& space) {
-
+ const std::string& space,
+ const ClientClassesPtr client_classes
+ /* = ClientClassesPtr() */) {
if (server_selector.amUnassigned()) {
isc_throw(NotImplemented, "managing configuration for no particular server"
" (unassigned) is unsupported at the moment");
in_bindings.add(tag);
in_bindings.add(code);
in_bindings.add(space);
+ /// @todo TKM remove if when v6 is ready.
+ if (universe == Option::V4) {
+ addClientClassesForWhereClause(in_bindings, client_classes);
+ }
getOptions(index, in_bindings, universe, options);
return (options.empty() ? OptionDescriptorPtr() :
/// @param server_selector Server selector.
/// @param code Option code.
/// @param space Option space name.
+ /// @param client_classes Optional client classes list of the option to be deleted.
+ /// Defaults to an empty pointer.
///
/// @return Pointer to the returned option or NULL if such option
/// doesn't exist.
const Option::Universe& universe,
const db::ServerSelector& server_selector,
const uint16_t code,
- const std::string& space);
+ const std::string& space,
+ const ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Sends query to retrieve all global options.
///
multipleAuditEntriesTest();
}
-TEST_F(PgSqlConfigBackendDHCPv4Test, subnetOption4WithClienClassesTest) {
- subnetOption4WithClienClassesTest();
+TEST_F(PgSqlConfigBackendDHCPv4Test, globalOption4WithClientClassesTest) {
+ globalOption4WithClientClassesTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv4Test, sharedNetworkOption4WithClientClassesTest) {
+ sharedNetworkOption4WithClientClassesTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv4Test, subnetOption4WithClientClassesTest) {
+ subnetOption4WithClientClassesTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv4Test, poolOption4WithClientClassesTest) {
+ poolOption4WithClientClassesTest();
}
/// @brief Test fixture for verifying database connection loss-recovery
template<typename Selector>
OptionDescriptor get(const Selector& key,
const uint16_t option_code,
- ClientClasses& client_classes) const {
+ const ClientClasses& client_classes) const {
// Check for presence of options.
OptionContainerPtr options = getAll(key);
/// @param server_selector Server selector.
/// @param code Option code.
/// @param space Option space.
+ /// @param client_classes Optional client classes list of the option to be deleted.
+ /// Defaults to an empty pointer.
/// @return Pointer to the retrieved option descriptor or null if
/// no option was found.
virtual OptionDescriptorPtr
getOption4(const db::ServerSelector& server_selector, const uint16_t code,
- const std::string& space) const = 0;
+ const std::string& space,
+ const ClientClassesPtr client_classes = ClientClassesPtr()) const = 0;
/// @brief Retrieves all global options.
///
ConfigBackendPoolDHCPv4::getOption4(const BackendSelector& backend_selector,
const ServerSelector& server_selector,
const uint16_t code,
- const std::string& space) const {
+ const std::string& space,
+ const ClientClassesPtr client_classes
+ /* = ClientClassesPtr */) const {
OptionDescriptorPtr option;
getPropertyPtrConst<OptionDescriptorPtr, uint16_t, const std::string&>
(&ConfigBackendDHCPv4::getOption4, backend_selector, server_selector,
- option, code, space);
+ option, code, space, client_classes);
return (option);
}
const ServerSelector& server_selector,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes /* = ClientClassesPtr */) {
+ const ClientClassesPtr client_classes /* = ClientClassesPtr */) {
return (createUpdateDeleteProperty<uint64_t, uint16_t, const std::string&>
(&ConfigBackendDHCPv4::deleteOption4, backend_selector, server_selector,
const std::string& shared_network_name,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes /* = ClientClassesPtr */) {
+ const ClientClassesPtr client_classes /* = ClientClassesPtr */) {
return (createUpdateDeleteProperty<uint64_t, const std::string&, uint16_t,
const std::string&>
(&ConfigBackendDHCPv4::deleteOption4, backend_selector, server_selector,
const SubnetID& subnet_id,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes /* = ClientClassesPtr */) {
+ const ClientClassesPtr client_classes /* = ClientClassesPtr */) {
return (createUpdateDeleteProperty<uint64_t, const SubnetID&, uint16_t, const std::string&>
(&ConfigBackendDHCPv4::deleteOption4, backend_selector, server_selector,
subnet_id, code, space, client_classes));
const asiolink::IOAddress& pool_end_address,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes /* = ClientClassesPtr */) {
+ const ClientClassesPtr client_classes /* = ClientClassesPtr */) {
return (createUpdateDeleteProperty<uint64_t, const IOAddress&, const IOAddress&,
uint16_t, const std::string&>
(&ConfigBackendDHCPv4::deleteOption4, backend_selector, server_selector,
/// @param server_selector Server selector.
/// @param code Option code.
/// @param space Option space.
+ /// @param client_classes Optional client classes list of the option to be deleted.
+ /// Defaults to an empty pointer.
/// @return Pointer to the retrieved option descriptor or null if
/// no option was found.
virtual OptionDescriptorPtr
getOption4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const uint16_t code,
- const std::string& space) const;
+ const std::string& space,
+ const ClientClassesPtr client_classes = ClientClassesPtr()) const;
/// @brief Retrieves all global options.
///
const db::ServerSelector& server_selector,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes = ClientClassesPtr());
+ const ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes shared network level option.
///
const std::string& shared_network_name,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes = ClientClassesPtr());
+ const ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes subnet level option.
///
const db::ServerSelector& server_selector,
const SubnetID& subnet_id,
const uint16_t code, const std::string& space,
- ClientClassesPtr client_classes = ClientClassesPtr());
+ const ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes pool level option.
///
const asiolink::IOAddress& pool_end_address,
const uint16_t code,
const std::string& space,
- ClientClassesPtr client_classes = ClientClassesPtr());
+ const ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes global parameter.
///
}
}
+std::list<OptionDescriptorPtr>
+GenericConfigBackendDHCPv4Test::makeClassTaggedOptions() {
+ // Describes an option to create.
+ struct OptData {
+ uint16_t code_;
+ std::string value_;
+ std::string cclass_;
+ };
+
+ // List of options to create.
+ std::list<OptData> opts_to_make = {
+ { DHO_TCODE, "T100", "cc-one" },
+ { DHO_PCODE, "P100", "cc-one" },
+ { DHO_PCODE, "P300", "" },
+ { DHO_TCODE, "T200", "" },
+ { DHO_PCODE, "P200", "cc-two" }
+ };
+
+ std::list<OptionDescriptorPtr> tagged_options;
+ for ( auto const& opt_to_make : opts_to_make) {
+ OptionDescriptor desc = createOption<OptionString>(Option::V4, opt_to_make.code_,
+ true, false, false, opt_to_make.value_);
+ desc.space_name_ = DHCP4_OPTION_SPACE;
+ if (!opt_to_make.cclass_.empty()) {
+ desc.addClientClass(opt_to_make.cclass_);
+ }
+
+ tagged_options.push_back(OptionDescriptorPtr(new OptionDescriptor(desc)));
+ }
+
+ return (tagged_options);
+}
+
void
-GenericConfigBackendDHCPv4Test::subnetOption4WithClienClassesTest() {
+GenericConfigBackendDHCPv4Test::updateClassTaggedOptions(
+ std::list<OptionDescriptorPtr>& options) {
+ for ( auto& desc : options) {
+ OptionStringPtr opt = boost::dynamic_pointer_cast<OptionString>(desc->option_);
+ ASSERT_TRUE(opt);
+ std::string new_value(opt->getValue() + std::string(".") + opt->getValue());
+ opt->setValue(new_value);
+ }
+}
- Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 24,
- 30, 40, 60, 1024));
+// Macro the make SCOPED_TRACE around equivalance functon more compact and helpful.
+#define SCOPED_OPT_COMPARE(exp_opt,test_opt)\
+{\
+ std::stringstream oss;\
+ oss << "Options not equal:\n"\
+ << " exp_opt: " << exp_opt.option_->toText() << "\n"\
+ << " test_opt: " << (test_opt.option_ ? test_opt.option_->toText() : "<null>") << "\n";\
+ SCOPED_TRACE(oss.str());\
+ testOptionsEquivalent(exp_opt,test_opt);\
+}
- // Add several options to the subnet.
- std::vector<OptionDescriptor> options;
- OptionDescriptor desc = createOption<OptionString>(Option::V4, DHO_BOOT_FILE_NAME,
- true, false, false, "boot-file-one");
- desc.space_name_ = DHCP4_OPTION_SPACE;
- desc.addClientClass("class3");
- options.push_back(desc);
+// Verify that one can add multiple global instances of the same option code
+// and that they can be distinguished via their client_classes.
+void
+GenericConfigBackendDHCPv4Test::globalOption4WithClientClassesTest() {
+ // Add the options to global scope.
+ auto ref_options = makeClassTaggedOptions();
+ for (auto const& ref_option : ref_options) {
+ // Add option to the config back end.
+ cbptr_->createUpdateOption4(ServerSelector::ALL(), ref_option);
+ }
+
+ // Make sure that we can find each option.
+ OptionDescriptorPtr found_option;
+ for (auto const& ref_option : ref_options) {
+ // Find the option by code and client_classes.
+ ClientClassesPtr cclasses(new ClientClasses(ref_option->client_classes_));
+ found_option = cbptr_->getOption4(ServerSelector::ALL(),
+ ref_option->option_->getType(),
+ DHCP4_OPTION_SPACE,
+ cclasses);
+ ASSERT_TRUE(found_option);
+ SCOPED_OPT_COMPARE((*ref_option), (*found_option));
+ }
+
+ // Update the option values.
+ updateClassTaggedOptions(ref_options);
+
+ // Update each option in the backend.
+ for (auto const& ref_option : ref_options) {
+ ClientClassesPtr cclasses(new ClientClasses(ref_option->client_classes_));
+
+ // Update option in the config back end.
+ cbptr_->createUpdateOption4(ServerSelector::ALL(), ref_option);
+
+ // Fetch and verify the updated option.
+ found_option = cbptr_->getOption4(ServerSelector::ALL(),
+ ref_option->option_->getType(),
+ DHCP4_OPTION_SPACE,
+ cclasses);
+ ASSERT_TRUE(found_option);
+ SCOPED_OPT_COMPARE((*ref_option), (*found_option));
+ }
+
+ // Delete each option from the backend.
+ for (auto const& ref_option : ref_options) {
+ ClientClassesPtr cclasses(new ClientClasses(ref_option->client_classes_));
+
+ // Delete the option by code and client_classes.
+ ASSERT_EQ(1, cbptr_->deleteOption4(ServerSelector::ALL(),
+ ref_option->option_->getType(),
+ DHCP4_OPTION_SPACE,
+ cclasses));
+
+ // Finding the option by code and client_classes should fail.
+ found_option = cbptr_->getOption4(ServerSelector::ALL(),
+ ref_option->option_->getType(),
+ DHCP4_OPTION_SPACE,
+ cclasses);
+ ASSERT_FALSE(found_option);
+ }
+}
- desc = createOption<OptionString>(Option::V4, DHO_BOOT_FILE_NAME,
- true, false, false, "boot-file-two");
- desc.space_name_ = DHCP4_OPTION_SPACE;
- desc.addClientClass("class1");
- desc.addClientClass("class2");
- options.push_back(desc);
+// Verify that one can add multiple instances of the same option code
+// to a shared-network and that they can be distinguished via their client_classes.
+void
+GenericConfigBackendDHCPv4Test::sharedNetworkOption4WithClientClassesTest() {
+ // Make a network with options.
+ SharedNetwork4Ptr network(new SharedNetwork4("net1"));
+ auto ref_options = makeClassTaggedOptions();
+ for ( auto const& ref_option : ref_options) {
+ network->getCfgOption()->add(*ref_option, ref_option->space_name_);
+ }
- desc = createOption<OptionString>(Option::V4, DHO_BOOT_FILE_NAME,
- true, false, false, "boot-file-three");
- desc.space_name_ = DHCP4_OPTION_SPACE;
- options.push_back(desc);
+ // Add the network to config back end.
+ cbptr_->createUpdateSharedNetwork4(ServerSelector::ALL(), network);
+
+ // Fetch the network.
+ SharedNetwork4Ptr returned_network = cbptr_->getSharedNetwork4(ServerSelector::ALL(),
+ network->getName());
+ ASSERT_TRUE(returned_network);
+
+ // Make sure that CfgOption->get() with client_classes finds each ref option.
+ for (auto const& ref_option : ref_options) {
+ auto cfg_option = returned_network->getCfgOption()->get(DHCP4_OPTION_SPACE,
+ ref_option->option_->getType(),
+ ref_option->client_classes_);
+ SCOPED_OPT_COMPARE((*ref_option), cfg_option);
+ }
+
+ // Now make sure that we can set the options individually.
+ updateClassTaggedOptions(ref_options);
+ for (auto const& ref_option : ref_options) {
+ cbptr_->createUpdateOption4(ServerSelector::ALL(), network->getName(), ref_option);
+ }
+
+ // Re-fetch the network.
+ returned_network = cbptr_->getSharedNetwork4(ServerSelector::ALL(), network->getName());
+ ASSERT_TRUE(returned_network);
+
+ // Make sure that CfgOption->get() with client_classes finds each ref option.
+ for (auto const& ref_option : ref_options) {
+ auto cfg_option = returned_network->getCfgOption()->get(DHCP4_OPTION_SPACE,
+ ref_option->option_->getType(),
+ ref_option->client_classes_);
+ SCOPED_OPT_COMPARE((*ref_option), cfg_option);
+ }
+
+ // Now make sure that we can delete the options individually.
+ updateClassTaggedOptions(ref_options);
+ for (auto const& ref_option : ref_options) {
+ ClientClassesPtr cclasses(new ClientClasses(ref_option->client_classes_));
+ ASSERT_EQ(1, cbptr_->deleteOption4(ServerSelector::ANY(),
+ network->getName(),
+ ref_option->option_->getType(),
+ DHCP4_OPTION_SPACE,
+ cclasses)) << "code:" << ref_option->option_->getType()
+ << " classes: " << cclasses->toText();
+ }
- subnet->getCfgOption()->add(options[2], options[0].space_name_);
- subnet->getCfgOption()->add(options[0], options[1].space_name_);
- subnet->getCfgOption()->add(options[1], options[2].space_name_);
+ // Re-fetch the network.
+ returned_network = cbptr_->getSharedNetwork4(ServerSelector::ALL(), network->getName());
+ ASSERT_TRUE(returned_network);
- auto found_opts = subnet->getCfgOption()->getList(DHCP4_OPTION_SPACE, DHO_BOOT_FILE_NAME);
- ASSERT_EQ(3, found_opts.size());
+ // Make sure that CfgOption is empty
+ auto cfg_option = returned_network->getCfgOption()->getAll(DHCP4_OPTION_SPACE);
+ EXPECT_TRUE(cfg_option->empty());
+}
+
+// Verify that one can add multiple instances of the same option code
+// to a subnet and that they can be distinguished via their client_classes.
+void
+GenericConfigBackendDHCPv4Test::subnetOption4WithClientClassesTest() {
+ // Make a subnet with options.
+ Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 24,
+ 30, 40, 60, 1024));
+ auto ref_options = makeClassTaggedOptions();
+ for ( auto const& ref_option : ref_options) {
+ subnet->getCfgOption()->add(*ref_option, ref_option->space_name_);
+ }
// Add the subnet to config back end.
cbptr_->createUpdateSubnet4(ServerSelector::ALL(), subnet);
- // Fetch this subnet by subnet identifier.
- Subnet4Ptr returned_subnet = cbptr_->getSubnet4(ServerSelector::ALL(),
- subnet->getID());
+ // Fetch the subnet.
+ Subnet4Ptr returned_subnet = cbptr_->getSubnet4(ServerSelector::ALL(), subnet->getID());
ASSERT_TRUE(returned_subnet);
- {
- SCOPED_TRACE("CREATE audit entry for a new subnet");
- testNewAuditEntry("dhcp4_subnet",
- AuditEntry::ModificationType::CREATE,
- "subnet set");
+ // Make sure that CfgOption->get() with client_classes finds each ref option.
+ for (auto const& ref_option : ref_options) {
+ auto cfg_option = returned_subnet->getCfgOption()->get(DHCP4_OPTION_SPACE,
+ ref_option->option_->getType(),
+ ref_option->client_classes_);
+ SCOPED_OPT_COMPARE((*ref_option), cfg_option);
}
- // The inserted subnet contains three options.
- ASSERT_EQ(3, countRows("dhcp4_options"));
-#if 0
- auto first_opt = subnet->getCfgOption()->get(DHCP4_OPTION_SPACE, DHO_BOOT_FILE_NAME);
- EXPECT_EQ(options[0].option_->toText(), first_opt.option_->toText());
-#endif
-
- found_opts = returned_subnet->getCfgOption()->getList(DHCP4_OPTION_SPACE, DHO_BOOT_FILE_NAME);
- ASSERT_EQ(3, found_opts.size());
- for (auto fo : found_opts) {
- std::cout << "FO cclient_classes: " << fo.client_classes_.toText()
- << ", opt: " << fo.option_->toText() << std::endl;
- }
-#if 0
- EXPECT_EQ(options[0].option_->toText(), found_opts[0].option_->toText());
- EXPECT_EQ(options[1].option_->toText(), found_opts[1].option_->toText());
- EXPECT_EQ(options[2].option_->toText(), found_opts[2].option_->toText());
-#endif
-
-#if 0
- OptionDescriptorPtr opt_boot_file_name = test_options_[0];
- cbptr_->createUpdateOption4(ServerSelector::ANY(), subnet->getID(),
- opt_boot_file_name);
+ // Now make sure that we can set the options individually.
+ updateClassTaggedOptions(ref_options);
+ for (auto const& ref_option : ref_options) {
+ cbptr_->createUpdateOption4(ServerSelector::ALL(), subnet->getID(), ref_option);
+ }
- returned_subnet = cbptr_->getSubnet4(ServerSelector::ALL(),
- subnet->getID());
+ // Re-fetch the subnet.
+ returned_subnet = cbptr_->getSubnet4(ServerSelector::ALL(), subnet->getID());
ASSERT_TRUE(returned_subnet);
- OptionDescriptor returned_opt_boot_file_name =
- returned_subnet->getCfgOption()->get(DHCP4_OPTION_SPACE, DHO_BOOT_FILE_NAME);
- ASSERT_TRUE(returned_opt_boot_file_name.option_);
-
- {
- SCOPED_TRACE("verify returned option");
- testOptionsEquivalent(*opt_boot_file_name, returned_opt_boot_file_name);
- EXPECT_GT(returned_opt_boot_file_name.getId(), 0);
+ // Make sure that CfgOption->get() with client_classes finds each ref option.
+ for (auto const& ref_option : ref_options) {
+ auto cfg_option = returned_subnet->getCfgOption()->get(DHCP4_OPTION_SPACE,
+ ref_option->option_->getType(),
+ ref_option->client_classes_);
+ SCOPED_OPT_COMPARE((*ref_option), cfg_option);
}
- {
- SCOPED_TRACE("UPDATE audit entry for an added subnet option");
- // Instead of adding an audit entry for an option we add an audit
- // entry for the entire subnet so as the server refreshes the
- // subnet with the new option. Note that the server doesn't
- // have means to retrieve only the newly added option.
- testNewAuditEntry("dhcp4_subnet",
- AuditEntry::ModificationType::UPDATE,
- "subnet specific option set");
+ // Now make sure that we can delete the options individually.
+ updateClassTaggedOptions(ref_options);
+ for (auto const& ref_option : ref_options) {
+ ClientClassesPtr cclasses(new ClientClasses(ref_option->client_classes_));
+ ASSERT_EQ(1, cbptr_->deleteOption4(ServerSelector::ANY(),
+ subnet->getID(),
+ ref_option->option_->getType(),
+ DHCP4_OPTION_SPACE,
+ cclasses));
}
- // We have added one option to the existing subnet. We should now have
- // three options.
- ASSERT_EQ(3, countRows("dhcp4_options"));
+ // Re-fetch the subnet.
+ returned_subnet = cbptr_->getSubnet4(ServerSelector::ALL(), subnet->getID());
+ ASSERT_TRUE(returned_subnet);
- opt_boot_file_name->persistent_ = !opt_boot_file_name->persistent_;
- opt_boot_file_name->cancelled_ = !opt_boot_file_name->cancelled_;
- cbptr_->createUpdateOption4(ServerSelector::ANY(), subnet->getID(),
- opt_boot_file_name);
+ // Make sure that CfgOption is empty
+ auto cfg_option = returned_subnet->getCfgOption()->getAll(DHCP4_OPTION_SPACE);
+ EXPECT_TRUE(cfg_option->empty());
+}
- returned_subnet = cbptr_->getSubnet4(ServerSelector::ANY(),
- subnet->getID());
- ASSERT_TRUE(returned_subnet);
- returned_opt_boot_file_name =
- returned_subnet->getCfgOption()->get(DHCP4_OPTION_SPACE, DHO_BOOT_FILE_NAME);
- ASSERT_TRUE(returned_opt_boot_file_name.option_);
+// Verify that one can add multiple instances of the same option code
+// to a pool and that they can be distinguished via their client_classes.
+void
+GenericConfigBackendDHCPv4Test::poolOption4WithClientClassesTest() {
+ // Make subnet with a pool with options.
+ Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 24,
+ 30, 40, 60, 1024));
- {
- SCOPED_TRACE("verify returned option with modified persistence");
- testOptionsEquivalent(*opt_boot_file_name, returned_opt_boot_file_name);
- }
+ Pool4Ptr pool(new Pool4(IOAddress("192.0.2.10"), IOAddress("192.0.2.20")));
+ subnet->addPool(pool);
- {
- SCOPED_TRACE("UPDATE audit entry for an updated subnet option");
- testNewAuditEntry("dhcp4_subnet",
- AuditEntry::ModificationType::UPDATE,
- "subnet specific option set");
+ // Add the options to the pool.
+ auto ref_options = makeClassTaggedOptions();
+ for ( auto const& ref_option : ref_options) {
+ pool->getCfgOption()->add(*ref_option, ref_option->space_name_);
}
- // Updating the option should replace the existing instance with the new
- // instance. Therefore, we should still have three options.
- ASSERT_EQ(3, countRows("dhcp4_options"));
+ // Add the subnet to config back end.
+ cbptr_->createUpdateSubnet4(ServerSelector::ALL(), subnet);
- // It should succeed for any server.
- EXPECT_EQ(1, cbptr_->deleteOption4(ServerSelector::ANY(), subnet->getID(),
- opt_boot_file_name->option_->getType(),
- opt_boot_file_name->space_name_));
+ // Fetch this subnet by subnet identifier.
+ Subnet4Ptr returned_subnet = cbptr_->getSubnet4(ServerSelector::ALL(),
+ subnet->getID());
+ ASSERT_TRUE(returned_subnet);
- returned_subnet = cbptr_->getSubnet4(ServerSelector::ALL(),
- subnet->getID());
+ PoolPtr returned_pool = returned_subnet->getPool(Lease::TYPE_V4, IOAddress("192.0.2.10"));
+ ASSERT_TRUE(returned_pool);
+
+ // Make sure that CfgOption->get() with client_classes finds each ref option.
+ for (auto const& ref_option : ref_options) {
+ auto cfg_option = returned_pool->getCfgOption()->get(DHCP4_OPTION_SPACE,
+ ref_option->option_->getType(),
+ ref_option->client_classes_);
+ SCOPED_OPT_COMPARE((*ref_option), cfg_option);
+ }
+
+ // Now make sure that we can set the options individually.
+ updateClassTaggedOptions(ref_options);
+ for (auto const& ref_option : ref_options) {
+ cbptr_->createUpdateOption4(ServerSelector::ALL(),
+ pool->getFirstAddress(),
+ pool->getLastAddress(),
+ ref_option);
+ }
+
+ // Re-fetch the subnet.
+ returned_subnet = cbptr_->getSubnet4(ServerSelector::ALL(), subnet->getID());
ASSERT_TRUE(returned_subnet);
- EXPECT_FALSE(returned_subnet->getCfgOption()->get(DHCP4_OPTION_SPACE, DHO_BOOT_FILE_NAME).option_);
+ returned_pool = returned_subnet->getPool(Lease::TYPE_V4, IOAddress("192.0.2.10"));
+ ASSERT_TRUE(returned_pool);
- {
- SCOPED_TRACE("UPDATE audit entry for a deleted subnet option");
- testNewAuditEntry("dhcp4_subnet",
- AuditEntry::ModificationType::UPDATE,
- "subnet specific option deleted");
+ // Make sure that CfgOption->get() with client_classes finds each ref option.
+ for (auto const& ref_option : ref_options) {
+ auto cfg_option = returned_pool->getCfgOption()->get(DHCP4_OPTION_SPACE,
+ ref_option->option_->getType(),
+ ref_option->client_classes_);
+ SCOPED_OPT_COMPARE((*ref_option), cfg_option);
}
- // We should have only two options after deleting one of them.
- ASSERT_EQ(2, countRows("dhcp4_options"));
-#endif
+ // Now make sure that we can delete the options individually.
+ for (auto const& ref_option : ref_options) {
+ ClientClassesPtr cclasses(new ClientClasses(ref_option->client_classes_));
+ ASSERT_EQ(1, cbptr_->deleteOption4(ServerSelector::ANY(),
+ pool->getFirstAddress(),
+ pool->getLastAddress(),
+ ref_option->option_->getType(),
+ DHCP4_OPTION_SPACE,
+ cclasses));
+ }
+
+ // Re-fetch the subnet.
+ returned_subnet = cbptr_->getSubnet4(ServerSelector::ALL(), subnet->getID());
+ ASSERT_TRUE(returned_subnet);
+
+ returned_pool = returned_subnet->getPool(Lease::TYPE_V4, IOAddress("192.0.2.10"));
+ ASSERT_TRUE(returned_pool);
+
+ // Make sure that CfgOption is empty
+ auto cfg_option = returned_pool->getCfgOption()->getAll(DHCP4_OPTION_SPACE);
+ EXPECT_TRUE(cfg_option->empty());
}
/// event and it does not matter).
void multipleAuditEntriesTest();
- void subnetOption4WithClienClassesTest();
+ /// @brief Creates a list of string options with and without client_class tags.
+ /// It creates 3 DHO_TCODE options and 2 DHO_PCODE options.
+ std::list<OptionDescriptorPtr> makeClassTaggedOptions();
+
+ /// @brief Updates the value of each string option in the list.
+ void updateClassTaggedOptions(std::list<OptionDescriptorPtr>& options);
+
+ /// @brief This test verifies that multiple instances of an option can
+ /// be added to global scope and be distinguished from one another
+ /// by their client-classes content.
+ void globalOption4WithClientClassesTest();
+
+ /// @brief This test verifies that multiple instances of an option can
+ /// be added to a shared-network and be distinguished from one another
+ /// by their client-classes content.
+ void sharedNetworkOption4WithClientClassesTest();
+
+ /// @brief This test verifies that multiple instances of an option can
+ /// be added to a subnet and be distinguished from one another
+ /// by their client-classes content.
+ void subnetOption4WithClientClassesTest();
+
+ /// @brief This test verifies that multiple instances of an option can
+ /// be added to a pool and be distinguished from one another
+ /// by their client-classes content.
+ void poolOption4WithClientClassesTest();
/// @brief Holds pointers to subnets used in tests.
std::vector<Subnet4Ptr> test_subnets_;
OptionDescriptorPtr
TestConfigBackendDHCPv4::getOption4(const db::ServerSelector& server_selector,
const uint16_t code,
- const std::string& space) const {
+ const std::string& space,
+ const ClientClassesPtr client_classes) const {
auto tags = server_selector.getTags();
auto candidate = OptionDescriptorPtr();
auto const& index = options_.get<1>();
BOOST_FOREACH(auto const& option_it, option_it_pair) {
if (option_it.space_name_ == space) {
+ if (client_classes && (option_it.client_classes_ != *client_classes)) {
+ continue;
+ }
+
for (auto const& tag : tags) {
if (option_it.hasServerTag(ServerTag(tag))) {
return (OptionDescriptorPtr(new OptionDescriptor(option_it)));
}
}
+
if (option_it.hasAllServerTag()) {
candidate = OptionDescriptorPtr(new OptionDescriptor(option_it));
}
/// @brief Retrieves single option by code and space.
///
/// @param server_selector Server selector.
+ /// @param code Code of the option to be deleted.
+ /// @param space Option space of the option to be deleted.
+ /// @param client_classes Optional client classes list of the option to be deleted.
+ /// Defaults to an empty pointer.
/// @return Pointer to the retrieved option descriptor or null if
/// no option was found.
virtual OptionDescriptorPtr
getOption4(const db::ServerSelector& server_selector, const uint16_t code,
- const std::string& space) const;
+ const std::string& space,
+ const ClientClassesPtr client_classes = ClientClassesPtr()) const;
/// @brief Retrieves all global options.
///