cc_binding,
MySqlBinding::createString(tag),
MySqlBinding::createInteger<uint8_t>(option->option_->getType()),
- MySqlBinding::condCreateString(option->space_name_)
+ MySqlBinding::condCreateString(option->space_name_),
+ cc_binding
};
MySqlTransaction transaction(conn_);
if (conn_.updateDeleteQuery(MySqlConfigBackendDHCPv4Impl::UPDATE_OPTION4,
in_bindings) == 0) {
- // Remove the 3 bindings used only in case of update.
- in_bindings.resize(in_bindings.size() - 3);
+ // Remove the 4 bindings used only in case of update.
+ in_bindings.resize(in_bindings.size() - 4);
insertOption4(server_selector, in_bindings);
}
cc_binding,
MySqlBinding::createInteger<uint32_t>(static_cast<uint32_t>(subnet_id)),
MySqlBinding::createInteger<uint8_t>(option->option_->getType()),
- MySqlBinding::condCreateString(option->space_name_)
+ MySqlBinding::condCreateString(option->space_name_),
+ cc_binding
};
boost::scoped_ptr<MySqlTransaction> transaction;
if (conn_.updateDeleteQuery(MySqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_SUBNET_ID,
in_bindings) == 0) {
- // Remove the 3 bindings used only in case of update.
- in_bindings.resize(in_bindings.size() - 3);
+ // Remove the 4 bindings used only in case of update.
+ in_bindings.resize(in_bindings.size() - 4);
insertOption4(server_selector, in_bindings);
}
cc_binding,
MySqlBinding::createInteger<uint64_t>(pool_id),
MySqlBinding::createInteger<uint8_t>(option->option_->getType()),
- MySqlBinding::condCreateString(option->space_name_)
+ MySqlBinding::condCreateString(option->space_name_),
+ cc_binding
};
MySqlTransaction transaction(conn_);
if (conn_.updateDeleteQuery(MySqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_POOL_ID,
in_bindings) == 0) {
- // Remove the 3 bindings used only in case of update.
- in_bindings.resize(in_bindings.size() - 3);
+ // Remove the 4 bindings used only in case of update.
+ in_bindings.resize(in_bindings.size() - 4);
insertOption4(server_selector, in_bindings);
}
cc_binding,
MySqlBinding::createString(shared_network_name),
MySqlBinding::createInteger<uint8_t>(option->option_->getType()),
- MySqlBinding::condCreateString(option->space_name_)
+ MySqlBinding::condCreateString(option->space_name_),
+ cc_binding
};
boost::scoped_ptr<MySqlTransaction> transaction;
if (conn_.updateDeleteQuery(MySqlConfigBackendDHCPv4Impl::
UPDATE_OPTION4_SHARED_NETWORK,
in_bindings) == 0) {
- // Remove the 3 bindings used only in case of update.
- in_bindings.resize(in_bindings.size() - 3);
+ // Remove the 4 bindings used only in case of update.
+ in_bindings.resize(in_bindings.size() - 4);
insertOption4(server_selector, in_bindings);
}
cc_binding,
MySqlBinding::createString(client_class->getName()),
MySqlBinding::createInteger<uint8_t>(option->option_->getType()),
- MySqlBinding::condCreateString(option->space_name_)
+ MySqlBinding::condCreateString(option->space_name_),
+ cc_binding
};
// Create scoped audit revision. As long as this instance exists
if (conn_.updateDeleteQuery(MySqlConfigBackendDHCPv4Impl::
UPDATE_OPTION4_CLIENT_CLASS,
in_bindings) == 0) {
- // Remove the 3 bindings used only in case of update.
- in_bindings.resize(in_bindings.size() - 3);
+ // Remove the 4 bindings used only in case of update.
+ in_bindings.resize(in_bindings.size() - 4);
insertOption4(server_selector, in_bindings);
}
}
/// @param server_selector Server selector.
/// @param code Code of the deleted option.
/// @param space Option space of the deleted option.
+ /// @param client_classes Optional client classes list of the option to be deleted.
/// @return Number of deleted options.
uint64_t deleteOption4(const ServerSelector& server_selector,
const uint16_t code,
- const std::string& space) {
+ const std::string& space,
+ ClientClassesPtr client_classes) {
MySqlBindingCollection in_bindings = {
MySqlBinding::createInteger<uint8_t>(code),
- MySqlBinding::createString(space)
+ MySqlBinding::createString(space),
+ createClientClassesForWhereClause(client_classes)
};
// Run DELETE.
/// belongs.
/// @param code Code of the deleted option.
/// @param space Option space of the deleted option.
+ /// @param client_classes Optional client classes list of the option to be deleted.
/// @return Number of deleted options.
uint64_t deleteOption4(const ServerSelector& server_selector,
const SubnetID& subnet_id,
const uint16_t code,
- const std::string& space) {
+ const std::string& space,
+ ClientClassesPtr client_classes) {
MySqlBindingCollection in_bindings = {
MySqlBinding::createInteger<uint32_t>(static_cast<uint32_t>(subnet_id)),
MySqlBinding::createInteger<uint8_t>(code),
- MySqlBinding::createString(space)
+ MySqlBinding::createString(space),
+ createClientClassesForWhereClause(client_classes)
};
// Run DELETE.
/// @param pool_end_address Upper bound pool address.
/// @param code Code of the deleted option.
/// @param space Option space of the deleted option.
+ /// @param client_classes Optional client classes list of the option to be deleted.
/// @return Number of deleted options.
uint64_t deleteOption4(const db::ServerSelector& server_selector,
const IOAddress& pool_start_address,
const IOAddress& pool_end_address,
const uint16_t code,
- const std::string& space) {
+ const std::string& space,
+ ClientClassesPtr client_classes) {
MySqlBindingCollection in_bindings = {
MySqlBinding::createInteger<uint8_t>(code),
MySqlBinding::createString(space),
MySqlBinding::createInteger<uint32_t>(pool_start_address.toUint32()),
- MySqlBinding::createInteger<uint32_t>(pool_end_address.toUint32())
+ MySqlBinding::createInteger<uint32_t>(pool_end_address.toUint32()),
+ createClientClassesForWhereClause(client_classes)
};
// Run DELETE.
/// option belongs to
/// @param code Code of the deleted option.
/// @param space Option space of the deleted option.
+ /// @param client_classes Optional client classes list of the option to be deleted.
/// @return Number of deleted options.
uint64_t deleteOption4(const db::ServerSelector& server_selector,
const std::string& shared_network_name,
const uint16_t code,
- const std::string& space) {
+ const std::string& space,
+ ClientClassesPtr client_classes) {
MySqlBindingCollection in_bindings = {
MySqlBinding::createString(shared_network_name),
MySqlBinding::createInteger<uint8_t>(code),
- MySqlBinding::createString(space)
+ MySqlBinding::createString(space),
+ createClientClassesForWhereClause(client_classes)
};
// Run DELETE.
// Update existing global option.
{ MySqlConfigBackendDHCPv4Impl::UPDATE_OPTION4,
- MYSQL_UPDATE_OPTION4_WITH_TAG(AND o.scope_id = 0 AND o.code = ? AND o.space = ?)
+ MYSQL_UPDATE_OPTION4_WITH_TAG(AND o.scope_id = 0 AND o.code = ? AND o.space = ? AND o.client_classes = ?)
},
// Update existing subnet level option.
{ MySqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_SUBNET_ID,
- MYSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 1 AND o.dhcp4_subnet_id = ? AND o.code = ? AND o.space = ?)
+ MYSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 1 AND o.dhcp4_subnet_id = ? AND o.code = ? AND o.space = ?
+ AND o.client_classes = ?)
},
// Update existing pool level option.
{ MySqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_POOL_ID,
- MYSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 5 AND o.pool_id = ? AND o.code = ? AND o.space = ?)
+ MYSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 5 AND o.pool_id = ? AND o.code = ? AND o.space = ?
+ AND o.client_classes = ?)
},
// Update existing shared network level option.
{ MySqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_SHARED_NETWORK,
- MYSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 4 AND o.shared_network_name = ? AND o.code = ? AND o.space = ?)
+ MYSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 4 AND o.shared_network_name = ? AND o.code = ? AND o.space = ?
+ AND o.client_classes = ?)
},
// Update existing client class level option.
{ MySqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_CLIENT_CLASS,
- MYSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 2 AND o.dhcp_client_class = ? AND o.code = ? AND o.space = ?)
+ MYSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 2 AND o.dhcp_client_class = ? AND o.code = ? AND o.space = ?
+ AND o.client_classes = ?)
},
// Update existing client class with specifying its position.
// Delete single global option.
{ MySqlConfigBackendDHCPv4Impl::DELETE_OPTION4,
- MYSQL_DELETE_OPTION_WITH_TAG(dhcp4, AND o.scope_id = 0 AND o.code = ? AND o.space = ?)
+ MYSQL_DELETE_OPTION_WITH_TAG(dhcp4, AND o.scope_id = 0 AND o.code = ? AND o.space = ?
+ AND o.client_classes LIKE ?)
},
// Delete all global options which are unassigned to any servers.
uint64_t
MySqlConfigBackendDHCPv4::deleteOption4(const ServerSelector& server_selector,
const uint16_t code,
- const std::string& space) {
+ const std::string& space,
+ 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);
+ uint64_t result = impl_->deleteOption4(server_selector, code, space, client_classes);
LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_OPTION4_RESULT)
.arg(result);
return (result);
MySqlConfigBackendDHCPv4::deleteOption4(const ServerSelector& /* server_selector */,
const std::string& shared_network_name,
const uint16_t code,
- const std::string& space) {
+ const std::string& space,
+ 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.
LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_SHARED_NETWORK_OPTION4)
.arg(shared_network_name).arg(code).arg(space);
uint64_t result = impl_->deleteOption4(ServerSelector::ANY(), shared_network_name,
- code, space);
+ code, space, client_classes);
LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_SHARED_NETWORK_OPTION4_RESULT)
.arg(result);
return (result);
MySqlConfigBackendDHCPv4::deleteOption4(const ServerSelector& /* server_selector */,
const SubnetID& subnet_id,
const uint16_t code,
- const std::string& space) {
+ const std::string& space,
+ 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.
LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_BY_SUBNET_ID_OPTION4)
.arg(subnet_id).arg(code).arg(space);
- uint64_t result = impl_->deleteOption4(ServerSelector::ANY(), subnet_id, code, space);
+ uint64_t result = impl_->deleteOption4(ServerSelector::ANY(), subnet_id, code, space,
+ client_classes);
LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_BY_SUBNET_ID_OPTION4_RESULT)
.arg(result);
return (result);
const asiolink::IOAddress& pool_start_address,
const asiolink::IOAddress& pool_end_address,
const uint16_t code,
- const std::string& space) {
+ const std::string& space,
+ 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.
LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_BY_POOL_OPTION4)
.arg(pool_start_address.toText()).arg(pool_end_address.toText()).arg(code).arg(space);
uint64_t result = impl_->deleteOption4(ServerSelector::ANY(), pool_start_address,
- pool_end_address, code, space);
+ pool_end_address, code, space, client_classes);
LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_BY_POOL_OPTION4_RESULT)
.arg(result);
return (result);
/// @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 Number of deleted options.
/// @throw NotImplemented if server selector is "unassigned".
virtual uint64_t
deleteOption4(const db::ServerSelector& server_selector, const uint16_t code,
- const std::string& space);
+ const std::string& space,
+ ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes shared network level option.
///
/// option belongs to
/// @param code Code of the deleted option.
/// @param space Option space of the deleted option.
+ /// @param client_classes Optional client classes list of the option to be deleted.
+ /// Defaults to an empty pointer.
/// @throw NotImplemented if server selector is "unassigned".
virtual uint64_t
deleteOption4(const db::ServerSelector& server_selector,
const std::string& shared_network_name,
const uint16_t code,
- const std::string& space);
+ const std::string& space,
+ ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes subnet level option.
///
/// belongs.
/// @param code Code of the deleted option.
/// @param space Option space of the deleted option.
+ /// @param client_classes Optional client classes list of the option to be deleted.
+ /// Defaults to an empty pointer.
/// @return Number of deleted options.
/// @throw NotImplemented if server selector is "unassigned".
virtual uint64_t
deleteOption4(const db::ServerSelector& server_selector, const SubnetID& subnet_id,
- const uint16_t code, const std::string& space);
+ const uint16_t code, const std::string& space,
+ ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes pool level option.
///
/// deleted option belongs.
/// @param code Code of the deleted option.
/// @param space Option space of the deleted option.
+ /// @param client_classes Optional client classes list of the option to be deleted.
+ /// Defaults to an empty pointer.
/// @return Number of deleted options.
/// @throw NotImplemented if server selector is "unassigned".
virtual uint64_t
const asiolink::IOAddress& pool_start_address,
const asiolink::IOAddress& pool_end_address,
const uint16_t code,
- const std::string& space);
+ const std::string& space,
+ ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes global parameter.
///
}
db::MySqlBindingPtr
-MySqlConfigBackendImpl::createInputClientClassesBinding(const ClientClasses& client_classes) {
- if (client_classes.empty()) {
- return(db::MySqlBinding::createNull());
- }
+MySqlConfigBackendImpl::createClientClassesForWhereClause(ClientClassesPtr client_classes) {
+ return (client_classes ? createInputClientClassesBinding(*client_classes)
+ : MySqlBinding::createString("%"));
+}
+db::MySqlBindingPtr
+MySqlConfigBackendImpl::createInputClientClassesBinding(const ClientClasses& client_classes) {
// Create JSON list of client classes.
data::ElementPtr client_classes_element = data::Element::createList();
for (auto const& client_class : client_classes) {
/// relay addresses specified).
db::MySqlBindingPtr createInputRelayBinding(const NetworkPtr& network);
+ /// @brief Creates input binding from a list of client classes
+ ///
+ /// @param client_classes ClientClasses collection containing the class names
+ /// @return Pointer to the binding (possibly null binding if there are no
+ /// classes specified).
+ db::MySqlBindingPtr createClientClassesForWhereClause(ClientClassesPtr client_classes);
+
/// @brief Creates input binding from a list of client classes
///
/// @param client_classes ClientClasses collection containing the class names
in_bindings.add(tag);
in_bindings.add(option->option_->getType());
in_bindings.addOptional(option->space_name_);
+ addClientClassesBinding(in_bindings, option->client_classes_);
// Start transaction.
PgSqlTransaction transaction(conn_);
in_bindings.add(subnet_id);
in_bindings.add(option->option_->getType());
in_bindings.addOptional(option->space_name_);
+ addClientClassesBinding(in_bindings, option->client_classes_);
// Start transaction.
PgSqlTransaction transaction(conn_);
in_bindings.add(pool_id);
in_bindings.add(option->option_->getType());
in_bindings.addOptional(option->space_name_);
+ addClientClassesBinding(in_bindings, option->client_classes_);
// Start transaction.
PgSqlTransaction transaction(conn_);
in_bindings.add(shared_network_name);
in_bindings.add(option->option_->getType());
in_bindings.addOptional(option->space_name_);
+ addClientClassesBinding(in_bindings, option->client_classes_);
// Start transaction.
PgSqlTransaction transaction(conn_);
in_bindings.add(class_name);
in_bindings.add(option->option_->getType());
in_bindings.addOptional(option->space_name_);
+ addClientClassesBinding(in_bindings, option->client_classes_);
// Create scoped audit revision. As long as this instance exists
// no new audit revisions are created in any subsequent calls.
/// @param server_selector Server selector.
/// @param code Code of the deleted option.
/// @param space Option space of the deleted option.
+ /// @param client_classes Optional client classes list of the option to be deleted.
/// @return Number of deleted options.
uint64_t deleteOption4(const ServerSelector& server_selector,
const uint16_t code,
- const std::string& space) {
+ const std::string& space,
+ ClientClassesPtr client_classes) {
PsqlBindArray in_bindings;
in_bindings.add(code);
in_bindings.add(space);
+ addClientClassesForWhereClause(in_bindings, client_classes);
// Run DELETE.
return (deleteTransactional(PgSqlConfigBackendDHCPv4Impl::DELETE_OPTION4,
/// belongs.
/// @param code Code of the deleted option.
/// @param space Option space of the deleted option.
+ /// @param client_classes Optional client classes list of the option to be deleted.
/// @return Number of deleted options.
uint64_t deleteOption4(const ServerSelector& server_selector,
const SubnetID& subnet_id,
const uint16_t code,
- const std::string& space) {
+ const std::string& space,
+ ClientClassesPtr client_classes) {
PsqlBindArray in_bindings;
in_bindings.add(subnet_id);
in_bindings.add(code);
in_bindings.add(space);
+ addClientClassesForWhereClause(in_bindings, client_classes);
// Run DELETE.
return (deleteTransactional(PgSqlConfigBackendDHCPv4Impl::DELETE_OPTION4_SUBNET_ID,
/// @param pool_end_address Upper bound pool address.
/// @param code Code of the deleted option.
/// @param space Option space of the deleted option.
+ /// @param client_classes Optional client classes list of the option to be deleted.
/// @return Number of deleted options.
uint64_t deleteOption4(const db::ServerSelector& server_selector,
const IOAddress& pool_start_address,
const IOAddress& pool_end_address,
const uint16_t code,
- const std::string& space) {
+ const std::string& space,
+ ClientClassesPtr client_classes) {
PsqlBindArray in_bindings;
in_bindings.addInet4(pool_start_address);
in_bindings.addInet4(pool_end_address);
in_bindings.add(code);
in_bindings.add(space);
+ addClientClassesForWhereClause(in_bindings, client_classes);
// Run DELETE.
return (deleteTransactional(PgSqlConfigBackendDHCPv4Impl::DELETE_OPTION4_POOL_RANGE,
/// option belongs to
/// @param code Code of the deleted option.
/// @param space Option space of the deleted option.
+ /// @param client_classes Optional client classes list of the option to be deleted.
/// @return Number of deleted options.
uint64_t deleteOption4(const db::ServerSelector& server_selector,
const std::string& shared_network_name,
const uint16_t code,
- const std::string& space) {
+ const std::string& space,
+ ClientClassesPtr client_classes) {
PsqlBindArray in_bindings;
in_bindings.add(shared_network_name);
in_bindings.add(code);
in_bindings.add(space);
+ addClientClassesForWhereClause(in_bindings, client_classes);
// Run DELETE.
return (deleteTransactional(PgSqlConfigBackendDHCPv4Impl::DELETE_OPTION4_SHARED_NETWORK,
// Update existing global option.
{
// PgSqlConfigBackendDHCPv4Impl::UPDATE_OPTION4,
- 17,
+ 18,
{
OID_INT2, // 1 code
OID_BYTEA, // 2 value
OID_VARCHAR, // 15 server_tag
OID_INT2, // 16 code (of option to update)
OID_VARCHAR, // 17 space (of option to update)
+ OID_VARCHAR // 18 client_classes (of option to update)
},
"UPDATE_OPTION4",
- PGSQL_UPDATE_OPTION4_WITH_TAG(AND o.scope_id = 0 AND o.code = $16 AND o.space = $17)
+ PGSQL_UPDATE_OPTION4_WITH_TAG(AND o.scope_id = 0 AND o.code = $16 AND o.space = $17 AND o.client_classes = $18)
},
// Update existing subnet level option.
{
// PgSqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_SUBNET_ID,
- 17,
+ 18,
{
OID_INT2, // 1 code
OID_BYTEA, // 2 value
OID_TEXT, // 14 client_classes
OID_INT8, // 15 subnet_id (of option to update)
OID_INT2, // 16 code (of option to update)
- OID_VARCHAR // 17 space (of option to update)
+ OID_VARCHAR, // 17 space (of option to update)
+ OID_VARCHAR // 18 client_classes (of option to update)
},
"UPDATE_OPTION4_SUBNET_ID",
- PGSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 1 AND o.dhcp4_subnet_id = $15 AND o.code = $16 AND o.space = $17)
+ PGSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 1 AND o.dhcp4_subnet_id = $15 AND o.code = $16 AND o.space = $17 AND o.client_classes = $18)
},
// Update existing pool level option.
{
// PgSqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_POOL_ID,
- 17,
+ 18,
{
OID_INT2, // 1 code
OID_BYTEA, // 2 value
OID_TEXT, // 14 client_classes
OID_INT8, // 15 pool_id (of option to update)
OID_INT2, // 16 code (of option to update)
- OID_VARCHAR // 17 space (of option to update)
+ OID_VARCHAR, // 17 space (of option to update)
+ OID_VARCHAR // 18 client_classes (of option to update)
},
"UPDATE_OPTION4_POOL_ID",
- PGSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 5 AND o.pool_id = $15 AND o.code = $16 AND o.space = $17)
+ PGSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 5 AND o.pool_id = $15 AND o.code = $16 AND o.space = $17 AND o.client_classes = $18)
},
// Update existing shared network level option.
{
// PgSqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_SHARED_NETWORK,
- 17,
+ 18,
{
OID_INT2, // 1 code
OID_BYTEA, // 2 value
OID_TEXT, // 14 client_classes
OID_VARCHAR, // 15 shared_network_name (of option to update)
OID_INT2, // 16 code (of option to update)
- OID_VARCHAR // 17 space (of option to update)
+ OID_VARCHAR, // 17 space (of option to update)
+ OID_VARCHAR // 18 client_classes (of option to update)
},
"UPDATE_OPTION4_SHARED_NETWORK",
- PGSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 4 AND o.shared_network_name = $15 AND o.code = $16 AND o.space = $17)
+ PGSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 4 AND o.shared_network_name = $15 AND o.code = $16 AND o.space = $17 AND o.client_classes = $18)
},
// Update existing client class level option.
{
// PgSqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_CLIENT_CLASS,
- 17,
+ 18,
{
OID_INT2, // 1 code
OID_BYTEA, // 2 value
OID_VARCHAR, // 15 dhcp_client_class (of option to update)
OID_INT2, // 16 code (of option to update)
OID_VARCHAR, // 17 space (of option to update)
+ OID_VARCHAR // 18 client_classes (of option to update)
},
"UPDATE_OPTION4_CLIENT_CLASS",
- PGSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 2 AND o.dhcp_client_class = $15 AND o.code = $16 AND o.space = $17)
+ PGSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 2 AND o.dhcp_client_class = $15 AND o.code = $16 AND o.space = $17 AND o.client_classes = $18)
},
// Update existing client class with specifying its position.
// Delete single global option.
{
// PgSqlConfigBackendDHCPv4Impl::DELETE_OPTION4,
- 3,
+ 4,
{
OID_VARCHAR, // 1 server_tag
OID_INT2, // 2 code
- OID_VARCHAR // 3 space
+ OID_VARCHAR, // 3 space
+ OID_TEXT // 4 client_classes
},
"DELETE_OPTION4",
- PGSQL_DELETE_OPTION_WITH_TAG(dhcp4, AND o.scope_id = 0 AND o.code = $2 AND o.space = $3)
+ PGSQL_DELETE_OPTION_WITH_TAG(dhcp4, AND o.scope_id = 0 AND o.code = $2 AND o.space = $3
+ AND o.client_classes LIKE $4)
},
// Delete all global options which are unassigned to any servers.
// Delete single option from a subnet.
{
// PgSqlConfigBackendDHCPv4Impl::DELETE_OPTION4_SUBNET_ID,
- 3,
+ 4,
{
- OID_INT8, // 1 subnet_id
- OID_INT2, // 2 code
- OID_VARCHAR // 3 space
+ OID_INT8, // 1 subnet_id
+ OID_INT2, // 2 code
+ OID_VARCHAR, // 3 space
+ OID_TEXT // 4 client_classes
},
"DELETE_OPTION4_SUBNET_ID",
PGSQL_DELETE_OPTION_NO_TAG(dhcp4,
- WHERE o.scope_id = 1 AND o.dhcp4_subnet_id = $1 AND o.code = $2 AND o.space = $3)
+ WHERE o.scope_id = 1 AND o.dhcp4_subnet_id = $1 AND o.code = $2 AND o.space = $3
+ AND o.client_classes LIKE $4)
},
// Delete single option from a pool.
{
// PgSqlConfigBackendDHCPv4Impl::DELETE_OPTION4_POOL_RANGE,
- 4,
+ 5,
{
- OID_TEXT, // 1 start_address - cast as inet
- OID_TEXT, // 2 start_address - cast as inet
- OID_INT2, // 3 code
- OID_VARCHAR // 4 space
+ OID_TEXT, // 1 start_address - cast as inet
+ OID_TEXT, // 2 start_address - cast as inet
+ OID_INT2, // 3 code
+ OID_VARCHAR, // 4 space
+ OID_TEXT // 5 client_classes
},
"DELETE_OPTION4_POOL_RANGE",
- PGSQL_DELETE_OPTION_POOL_RANGE(dhcp4, o.scope_id = 5 AND o.code = $3 AND o.space = $4)
+ PGSQL_DELETE_OPTION_POOL_RANGE(dhcp4, o.scope_id = 5 AND o.code = $3 AND o.space = $4
+ AND o.client_classes LIKE $5)
},
// Delete single option from a shared network.
{
// PgSqlConfigBackendDHCPv4Impl::DELETE_OPTION4_SHARED_NETWORK,
- 3,
+ 4,
{
OID_VARCHAR, // 1 shared_network_name
OID_INT2, // 2 code
- OID_VARCHAR // 3 space
+ OID_VARCHAR, // 3 space
+ OID_TEXT // 4 client_classes
},
"DELETE_OPTION4_SHARED_NETWORK",
PGSQL_DELETE_OPTION_NO_TAG(dhcp4,
- WHERE o.scope_id = 4 AND o.shared_network_name = $1 AND o.code = $2 AND o.space = $3)
+ WHERE o.scope_id = 4 AND o.shared_network_name = $1 AND o.code = $2 AND o.space = $3
+ AND o.client_classes LIKE $4)
},
// Delete options belonging to a subnet.
uint64_t
PgSqlConfigBackendDHCPv4::deleteOption4(const ServerSelector& server_selector,
const uint16_t code,
- const std::string& space) {
+ const std::string& space,
+ 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);
+ uint64_t result = impl_->deleteOption4(server_selector, code, space, client_classes);
LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_OPTION4_RESULT)
.arg(result);
return (result);
PgSqlConfigBackendDHCPv4::deleteOption4(const ServerSelector& /* server_selector */,
const std::string& shared_network_name,
const uint16_t code,
- const std::string& space) {
+ const std::string& space,
+ 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.
LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_SHARED_NETWORK_OPTION4)
.arg(shared_network_name).arg(code).arg(space);
uint64_t result = impl_->deleteOption4(ServerSelector::ANY(), shared_network_name,
- code, space);
+ code, space, client_classes);
LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_SHARED_NETWORK_OPTION4_RESULT)
.arg(result);
return (result);
PgSqlConfigBackendDHCPv4::deleteOption4(const ServerSelector& /* server_selector */,
const SubnetID& subnet_id,
const uint16_t code,
- const std::string& space) {
+ const std::string& space,
+ 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.
LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_BY_SUBNET_ID_OPTION4)
.arg(subnet_id).arg(code).arg(space);
- uint64_t result = impl_->deleteOption4(ServerSelector::ANY(), subnet_id, code, space);
+ uint64_t result = impl_->deleteOption4(ServerSelector::ANY(), subnet_id, code, space,
+ client_classes);
LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_BY_SUBNET_ID_OPTION4_RESULT)
.arg(result);
return (result);
const asiolink::IOAddress& pool_start_address,
const asiolink::IOAddress& pool_end_address,
const uint16_t code,
- const std::string& space) {
+ const std::string& space,
+ 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.
LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_BY_POOL_OPTION4)
.arg(pool_start_address.toText()).arg(pool_end_address.toText()).arg(code).arg(space);
uint64_t result = impl_->deleteOption4(ServerSelector::ANY(), pool_start_address,
- pool_end_address, code, space);
+ pool_end_address, code, space, client_classes);
LOG_DEBUG(pgsql_cb_logger, DBGLVL_TRACE_BASIC, PGSQL_CB_DELETE_BY_POOL_OPTION4_RESULT)
.arg(result);
return (result);
/// @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 Number of deleted options.
/// @throw NotImplemented if server selector is "unassigned".
virtual uint64_t
deleteOption4(const db::ServerSelector& server_selector, const uint16_t code,
- const std::string& space);
+ const std::string& space,
+ ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes shared network level option.
///
/// option belongs to
/// @param code Code of the deleted option.
/// @param space Option space of the deleted option.
+ /// @param client_classes Optional client classes list of the option to be deleted.
+ /// Defaults to an empty pointer.
/// @throw NotImplemented if server selector is "unassigned".
virtual uint64_t
deleteOption4(const db::ServerSelector& server_selector,
const std::string& shared_network_name,
const uint16_t code,
- const std::string& space);
+ const std::string& space,
+ ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes subnet level option.
///
/// belongs.
/// @param code Code of the deleted option.
/// @param space Option space of the deleted option.
+ /// @param client_classes Optional client classes list of the option to be deleted.
+ /// Defaults to an empty pointer.
/// @return Number of deleted options.
/// @throw NotImplemented if server selector is "unassigned".
virtual uint64_t
- deleteOption4(const db::ServerSelector& server_selector, const SubnetID& subnet_id,
- const uint16_t code, const std::string& space);
+ deleteOption4(const db::ServerSelector& server_selector,
+ const SubnetID& subnet_id,
+ const uint16_t code,
+ const std::string& space,
+ ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes pool level option.
///
/// deleted option belongs.
/// @param code Code of the deleted option.
/// @param space Option space of the deleted option.
+ /// @param client_classes Optional client classes list of the option to be deleted.
+ /// Defaults to an empty pointer.
/// @return Number of deleted options.
/// @throw NotImplemented if server selector is "unassigned".
virtual uint64_t
const asiolink::IOAddress& pool_start_address,
const asiolink::IOAddress& pool_end_address,
const uint16_t code,
- const std::string& space);
+ const std::string& space,
+ ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes global parameter.
///
bindings.add(client_classes_element);
}
+void
+PgSqlConfigBackendImpl::addClientClassesForWhereClause(db::PsqlBindArray& bindings,
+ const ClientClassesPtr client_classes
+ /* = ClientClassePtr() */) {
+ if (client_classes) {
+ addClientClassesBinding(bindings, *client_classes);
+ } else {
+ // Wildcard for LIKE expression.
+ bindings.add("%");
+ }
+}
+
} // end of namespace isc::dhcp
} // end of namespace isc
/// Creates an Element tree of client class names and adds that to the end
/// of the given bind array.
///
+ /// @param bindings PsqlBindArray to which the option value should be added.
/// @param client_classes ClientClasses collection containing the class names
- void addClientClassesBinding(db::PsqlBindArray& bindings,
+ void addClientClassesBinding(db::PsqlBindArray& bindings,
const ClientClasses& client_classes);
+ /// @brief Adds 'client-classes' to a bind array as a where clause argument.
+ ///
+ /// If client_classes parameter is not an empty pointer, it creates an Element
+ /// tree of client class names and adds that to the end of the given bind array.
+ /// Otherwise it adds a the LIKE operator wild-card string value, "%".
+ ///
+ /// @param bindings PsqlBindArray to which the option value should be added.
+ /// @param client_classes ClientClasses collection containing the class names.
+ void addClientClassesForWhereClause(db::PsqlBindArray& bindings,
+ const ClientClassesPtr client_classes
+ = ClientClassesPtr());
+
/// @brief Iterates over the class names in a JSON list element at a
/// given column, adding each name to the given ClientClasses instance.
///
multipleAuditEntriesTest();
}
+TEST_F(PgSqlConfigBackendDHCPv4Test, subnetOption4WithClienClassesTest) {
+ subnetOption4WithClienClassesTest();
+}
+
/// @brief Test fixture for verifying database connection loss-recovery
/// behavior.
class PgSqlConfigBackendDHCPv4DbLostCallbackTest : public GenericConfigBackendDbLostCallbackTest {
ClientClassContainer container_;
};
+/// @brief Smart pointer to ClientClasses object.
+typedef boost::shared_ptr<ClientClasses> ClientClassesPtr;
+
}
}
(cancelled_ == other.cancelled_) &&
(formatted_value_ == other.formatted_value_) &&
(space_name_ == other.space_name_) &&
- option_->equals(other.option_));
+ ((option_ && other.option_) &&
+ option_->equals(other.option_)) &&
+ (client_classes_ == other.client_classes_));
}
void
}
// Find the option we want to replace.
- OptionContainerTypeIndex& idx = options->get<1>();
- auto const& od_itr = idx.find(desc.option_->getType());
- if (od_itr == idx.end()) {
+ auto& idx6 = options->get<6>();
+ auto const& od_itr = idx6.find(boost::make_tuple(desc.option_->getType(), desc.client_classes_));
+ if (od_itr == idx6.end()) {
isc_throw(isc::BadValue, "cannot replace option: "
<< option_space << ":" << desc.option_->getType()
+ << " , client-classes: " << desc.client_classes_.toText()
<< ", it does not exist");
}
- idx.replace(od_itr, desc);
+ idx6.replace(od_itr, desc);
}
std::list<std::string>
return (idx.erase(option_code));
}
+size_t
+CfgOption::del(const std::string& option_space, const uint16_t option_code,
+ const ClientClasses& client_classes) {
+ // Check for presence of options.
+ OptionContainerPtr options = getAll(option_space);
+ if (!options || options->empty()) {
+ // There are no options, so there is nothing to do.
+ return (0);
+ }
+
+ // If this is not top level option we may also need to delete the
+ // option instance from options encapsulating the particular option
+ // space.
+ if ((option_space != DHCP4_OPTION_SPACE) &&
+ (option_space != DHCP6_OPTION_SPACE)) {
+ // For each option space name iterate over the existing options.
+ auto option_space_names = getOptionSpaceNames();
+ for (auto const& option_space_from_list : option_space_names) {
+ // Get all options within the particular option space.
+ auto const& options_in_space = getAll(option_space_from_list);
+ for (auto const& option_it : *options_in_space) {
+
+ // Check if the option encapsulates our option space and
+ // it does, try to delete our option.
+ if (option_it.option_ &&
+ (option_it.option_->getEncapsulatedSpace() == option_space)) {
+ option_it.option_->delOption(option_code);
+ }
+ }
+ }
+ }
+
+ auto& idx6 = options->get<6>();
+ auto range = idx6.equal_range(boost::make_tuple(option_code, client_classes));
+ auto count = std::distance(range.first, range.second);
+ if (count) {
+ idx6.erase(range.first, range.second);
+ }
+
+ return(count);
+}
+
size_t
CfgOption::del(const uint32_t vendor_id, const uint16_t option_code) {
// Check for presence of options.
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/mem_fun.hpp>
#include <boost/multi_index/member.hpp>
+#include <boost/multi_index/composite_key.hpp>
#include <boost/shared_ptr.hpp>
#include <stdint.h>
#include <list>
bool,
&OptionDescriptor::cancelled_
>
+ >,
+ // Start definition of index #6.
+ boost::multi_index::hashed_non_unique<
+ boost::multi_index::composite_key<
+ OptionDescriptor,
+ KeyFromKeyExtractor<
+ boost::multi_index::const_mem_fun<
+ Option,
+ uint16_t,
+ &Option::getType
+ >,
+ boost::multi_index::member<
+ OptionDescriptor,
+ OptionPtr,
+ &OptionDescriptor::option_
+ >
+ >,
+ boost::multi_index::member<OptionDescriptor,
+ ClientClasses,
+ &OptionDescriptor::client_classes_>
+ >
>
>
> OptionContainer;
typedef std::pair<OptionContainerCancelIndex::const_iterator,
OptionContainerCancelIndex::const_iterator> OptionContainerCancelRange;
+/// Type of the index #6 - option type + client_classes.
+typedef OptionContainer::nth_index<6>::type OptionContainerTypeClassesIndex;
+typedef std::pair<OptionContainerTypeClassesIndex::const_iterator,
+ OptionContainerTypeClassesIndex::const_iterator> OptionContainerTypeClassesRange;
+
/// @brief Represents option data configuration for the DHCP server.
///
/// This class holds a collection of options to be sent to a DHCP client.
return (list);
}
+ /// @brief Returns option for the specified key, option code and client classes tag.
+ ///
+ /// The key should be a string, in which case it specifies an option space
+ /// name, or an uint32_t value, in which case it specifies a vendor
+ /// identifier.
+ ///
+ /// @param key Option space name or vendor identifier.
+ /// @param option_code Code of the option to be returned.
+ /// @param client_classes client classes tag of the option to be returned.
+ /// @tparam Selector one of: @c std::string or @c uint32_t
+ ///
+ /// @return Descriptor of the option. If option hasn't been found, the
+ /// descriptor holds null option.
+ template<typename Selector>
+ OptionDescriptor get(const Selector& key,
+ const uint16_t option_code,
+ ClientClasses& client_classes) const {
+
+ // Check for presence of options.
+ OptionContainerPtr options = getAll(key);
+ if (!options || options->empty()) {
+ return (OptionDescriptor(false, false));
+ }
+
+ // Some options present, locate the one we are interested in
+ // using code + client_classes index.
+ auto& idx6 = options->get<6>();
+ auto const& od_itr6 = idx6.find(boost::make_tuple(option_code, client_classes));
+ if (od_itr6 == idx6.end()) {
+ return (OptionDescriptor(false, false));
+ }
+
+ return (*od_itr6);
+ }
+
/// @brief Fetches an option for a given code if it is allowed for the given list
/// of client classes.
///
// We treat the empty client-classes case (if present) as a default.
// If we encounter it before we reach the end of the list of options
- // remember it but keep checking the list for an actual match. We do
+ // remember it but keep checking the list for an actual match. We do
// it this way to avoid expecting the entries in any particular order.
auto & index = options->get<1>();
auto range = index.equal_range(option_code);
return (*range.first);
}
break;
- default:
- {
+ default:
+ {
auto default_opt = index.end();
auto otr = range.first;
while (otr != range.second) {
if (!(*otr).client_classes_.empty()) {
return (*otr);
}
-
+
default_opt = otr;
}
-
+
++otr;
}
-
+
// If we have a default return it.
if (default_opt != index.end()) {
return (*default_opt);
}
- }}
+ }}
// None allowed.
return (OptionDescriptor(false, false));
/// @return Number of deleted options.
size_t del(const std::string& option_space, const uint16_t option_code);
+ /// @brief Deletes option for the specified option space and option code.
+ ///
+ /// If the option is encapsulated within some non top level option space,
+ /// it is also deleted from all option instances encapsulating this
+ /// option space.
+ ///
+ /// @param option_space Option space name.
+ /// @param option_code Code of the option to be returned.
+ /// @param client_classes client classes tag of the option to be deleted.
+ ///
+ /// @return Number of deleted options.
+ size_t del(const std::string& option_space, const uint16_t option_code,
+ const ClientClasses& client_classes);
+
/// @brief Deletes vendor option for the specified vendor id.
///
/// @param vendor_id Vendor identifier.
/// @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 Number of deleted options.
virtual uint64_t
deleteOption4(const db::ServerSelector& server_selector,
const uint16_t code,
- const std::string& space) = 0;
+ const std::string& space,
+ ClientClassesPtr client_classes = ClientClassesPtr()) = 0;
/// @brief Deletes shared network level option.
///
/// @param shared_network_name Name of the shared network which option
/// belongs to.
/// @param code Code of the option to be deleted.
+ /// @param client_classes Optional client classes list of the option to be deleted.
+ /// Defaults to an empty pointer.
/// @param space Option space of the option to be deleted.
virtual uint64_t
deleteOption4(const db::ServerSelector& selector,
const std::string& shared_network_name,
const uint16_t code,
- const std::string& space) = 0;
+ const std::string& space,
+ ClientClassesPtr client_classes = ClientClassesPtr()) = 0;
/// @brief Deletes subnet level option.
///
/// belongs.
/// @param code Code of the deleted option.
/// @param space Option space of the deleted option.
+ /// @param client_classes Optional client classes list of the option to be deleted.
+ /// Defaults to an empty pointer.
/// @return Number of deleted options.
virtual uint64_t
deleteOption4(const db::ServerSelector& server_selector,
const SubnetID& subnet_id,
const uint16_t code,
- const std::string& space) = 0;
+ const std::string& space,
+ ClientClassesPtr client_classes = ClientClassesPtr()) = 0;
/// @brief Deletes pool level option.
///
/// deleted option belongs.
/// @param code Code of the deleted option.
/// @param space Option space of the deleted option.
+ /// @param client_classes Optional client classes list of the option to be deleted.
+ /// Defaults to an empty pointer.
/// @return Number of deleted options.
virtual uint64_t
deleteOption4(const db::ServerSelector& server_selector,
const asiolink::IOAddress& pool_start_address,
const asiolink::IOAddress& pool_end_address,
const uint16_t code,
- const std::string& space) = 0;
+ const std::string& space,
+ ClientClassesPtr client_classes = ClientClassesPtr()) = 0;
/// @brief Deletes global parameter.
///
ConfigBackendPoolDHCPv4::deleteOption4(const BackendSelector& backend_selector,
const ServerSelector& server_selector,
const uint16_t code,
- const std::string& space) {
+ const std::string& space,
+ ClientClassesPtr client_classes /* = ClientClassesPtr */) {
+
return (createUpdateDeleteProperty<uint64_t, uint16_t, const std::string&>
(&ConfigBackendDHCPv4::deleteOption4, backend_selector, server_selector,
- code, space));
+ code, space, client_classes));
}
uint64_t
const ServerSelector& server_selector,
const std::string& shared_network_name,
const uint16_t code,
- const std::string& space) {
+ const std::string& space,
+ ClientClassesPtr client_classes /* = ClientClassesPtr */) {
return (createUpdateDeleteProperty<uint64_t, const std::string&, uint16_t,
const std::string&>
(&ConfigBackendDHCPv4::deleteOption4, backend_selector, server_selector,
- shared_network_name, code, space));
+ shared_network_name, code, space, client_classes));
}
uint64_t
const ServerSelector& server_selector,
const SubnetID& subnet_id,
const uint16_t code,
- const std::string& space) {
+ const std::string& space,
+ 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));
+ subnet_id, code, space, client_classes));
}
uint64_t
const asiolink::IOAddress& pool_start_address,
const asiolink::IOAddress& pool_end_address,
const uint16_t code,
- const std::string& space) {
+ const std::string& space,
+ ClientClassesPtr client_classes /* = ClientClassesPtr */) {
return (createUpdateDeleteProperty<uint64_t, const IOAddress&, const IOAddress&,
uint16_t, const std::string&>
(&ConfigBackendDHCPv4::deleteOption4, backend_selector, server_selector,
- pool_start_address, pool_end_address, code, space));
+ pool_start_address, pool_end_address, code, space, client_classes));
}
uint64_t
/// @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 Number of deleted options.
virtual uint64_t
deleteOption4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const uint16_t code,
- const std::string& space);
+ const std::string& space,
+ ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes shared network level option.
///
/// belongs to.
/// @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.
virtual uint64_t
deleteOption4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const std::string& shared_network_name,
const uint16_t code,
- const std::string& space);
+ const std::string& space,
+ ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes subnet level option.
///
/// belongs.
/// @param code Code of the deleted option.
/// @param space Option space of the deleted option.
+ /// @param client_classes Optional client classes list of the option to be deleted.
+ /// Defaults to an empty pointer.
/// @return Number of deleted options.
virtual uint64_t
deleteOption4(const db::BackendSelector& backend_selector,
const db::ServerSelector& server_selector,
const SubnetID& subnet_id,
- const uint16_t code, const std::string& space);
+ const uint16_t code, const std::string& space,
+ ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes pool level option.
///
/// deleted option belongs.
/// @param code Code of the deleted option.
/// @param space Option space of the deleted option.
+ /// @param client_classes Optional client classes list of the option to be deleted.
+ /// Defaults to an empty pointer.
/// @return Number of deleted options.
virtual uint64_t
deleteOption4(const db::BackendSelector& backend_selector,
const asiolink::IOAddress& pool_start_address,
const asiolink::IOAddress& pool_end_address,
const uint16_t code,
- const std::string& space);
+ const std::string& space,
+ ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes global parameter.
///
}
}
+TEST_F(CfgOptionTest, optionsWithClientClasses) {
+ // Describes an option to create.
+ struct OptData {
+ uint16_t code_;
+ uint16_t value_;
+ std::string cclass_;
+ };
+
+ // List of options to create.
+ std::list<OptData> opts_to_make = {
+ { 777, 1, "cc-one" },
+ { 777, 3, "" },
+ { 777, 2, "cc-two" }
+ };
+
+ // Populate a CfgOption with test options.
+ CfgOption cfg;
+ OptionDescriptorList reference_options;
+ for (const auto& opt_to_make : opts_to_make) {
+ OptionUint16Ptr opt(new OptionUint16(Option::V6, opt_to_make.code_,
+ opt_to_make.value_));
+ OptionDescriptor desc(opt, false, false);
+ desc.space_name_ = DHCP6_OPTION_SPACE;
+ if (!opt_to_make.cclass_.empty()) {
+ desc.addClientClass(opt_to_make.cclass_);
+ }
+
+ ASSERT_NO_THROW(cfg.add(desc, desc.space_name_));
+ reference_options.push_back(desc);
+ }
+
+ // Verify we have the expected option counts.
+ auto options = cfg.getAll(DHCP6_OPTION_SPACE);
+ ASSERT_EQ(options->size(), reference_options.size());
+
+ // Access them by sequence index (i.e order of insertion).
+ auto reference_desc = reference_options.begin();
+ auto const& idx = options->get<0>();
+ for (auto& returned_desc : idx) {
+ ASSERT_EQ(*reference_desc, returned_desc);
+ ++reference_desc;
+ }
+
+ // Verify that CfgOption::get() (code only) returns the
+ // last one inserted.
+ OptionDescriptor found_desc = cfg.get(DHCP6_OPTION_SPACE, 777);
+ ASSERT_EQ(options->size(), reference_options.size());
+ ASSERT_TRUE(found_desc.option_);
+ ASSERT_EQ(found_desc, *(reference_options.rbegin()));
+
+ // Verify that CfgOption::getList() returns all three.
+ // Note this returns them in reverse sequence order.
+ OptionDescriptorList list_options = cfg.getList(DHCP6_OPTION_SPACE, 777);
+ ASSERT_EQ(options->size(), reference_options.size());
+ auto reference_rdesc = reference_options.rbegin();
+ for (auto &returned_desc : list_options ){
+ ASSERT_EQ(*reference_rdesc, returned_desc);
+ ++reference_rdesc;
+ }
+
+ // Verify that CfgOption::get() with client classes returns
+ // each one correctly.
+ for (auto &reference_desc : reference_options ){
+ OptionDescriptor found_desc = cfg.get(DHCP6_OPTION_SPACE, 777,
+ reference_desc.client_classes_);
+ ASSERT_TRUE(found_desc.option_);
+ ASSERT_EQ(found_desc, reference_desc);
+ }
+}
+
+TEST_F(CfgOptionTest, replaceWithClientClasses) {
+ // Describes an option to create.
+ struct OptData {
+ uint16_t code_;
+ uint16_t value_;
+ std::string cclass_;
+ };
+
+ // List of options to create.
+ std::list<OptData> opts_to_make = {
+ { 777, 1, "cc-one" },
+ { 777, 3, "" },
+ { 777, 2, "cc-two" }
+ };
+
+ // Populate a CfgOption with test options.
+ CfgOption cfg;
+ OptionDescriptorList reference_options;
+ for (const auto& opt_to_make : opts_to_make) {
+ OptionUint16Ptr opt(new OptionUint16(Option::V6, opt_to_make.code_,
+ opt_to_make.value_));
+
+ OptionDescriptor desc(opt, false, false);
+ desc.space_name_ = DHCP6_OPTION_SPACE;
+ if (!opt_to_make.cclass_.empty()) {
+ desc.addClientClass(opt_to_make.cclass_);
+ }
+
+ ASSERT_NO_THROW(cfg.add(desc, desc.space_name_));
+ reference_options.push_back(desc);
+ }
+
+ // Replace the first reference option.
+ auto& replacement = reference_options[0];
+ (boost::dynamic_pointer_cast<OptionUint16>(replacement.option_))->setValue(100);
+ ASSERT_NO_THROW(cfg.replace(replacement, DHCP6_OPTION_SPACE));
+
+ // Make sure we can the updated option.
+ OptionDescriptor found_desc = cfg.get(DHCP6_OPTION_SPACE, 777,
+ replacement.client_classes_);
+ ASSERT_TRUE(found_desc.option_);
+ ASSERT_EQ(found_desc, replacement);
+
+ // Replace the second reference option.
+ auto& replacement2 = reference_options[1];
+ (boost::dynamic_pointer_cast<OptionUint16>(replacement2.option_))->setValue(300);
+ ASSERT_NO_THROW(cfg.replace(replacement2, DHCP6_OPTION_SPACE));
+
+ // Make sure we can the updated option.
+ found_desc = cfg.get(DHCP6_OPTION_SPACE, 777, replacement2.client_classes_);
+ ASSERT_TRUE(found_desc.option_);
+ ASSERT_EQ(found_desc, replacement2);
+
+ // Verify we still have all the expected options in sequence order.
+ auto options = cfg.getAll(DHCP6_OPTION_SPACE);
+ ASSERT_EQ(options->size(), reference_options.size());
+
+ // Access them by sequence index (i.e order of insertion).
+ auto reference_desc = reference_options.begin();
+ auto const& idx = options->get<0>();
+ for (auto& returned_desc : idx) {
+ ASSERT_EQ(*reference_desc, returned_desc);
+ ++reference_desc;
+ }
+}
+
+TEST_F(CfgOptionTest, deleteWithClientClasses) {
+ // Describes an option to create.
+ struct OptData {
+ uint16_t code_;
+ uint16_t value_;
+ std::string cclass_;
+ };
+
+ // List of options to create.
+ std::list<OptData> opts_to_make = {
+ { 777, 1, "cc-one" },
+ { 777, 3, "" },
+ { 777, 2, "cc-two" }
+ };
+
+ // Populate a CfgOption with test options.
+ CfgOption cfg;
+ OptionDescriptorList reference_options;
+ for (const auto& opt_to_make : opts_to_make) {
+ OptionUint16Ptr opt(new OptionUint16(Option::V6, opt_to_make.code_,
+ opt_to_make.value_));
+
+ OptionDescriptor desc(opt, false, false);
+ desc.space_name_ = DHCP6_OPTION_SPACE;
+ if (!opt_to_make.cclass_.empty()) {
+ desc.addClientClass(opt_to_make.cclass_);
+ }
+
+ ASSERT_NO_THROW(cfg.add(desc, desc.space_name_));
+ reference_options.push_back(desc);
+ }
+
+ // Delete the third reference option.
+ ASSERT_NO_THROW(cfg.del(DHCP6_OPTION_SPACE, 777, reference_options[2].client_classes_));
+
+ // Make sure we can no longer find the deleted option.
+ OptionDescriptor found_desc = cfg.get(DHCP6_OPTION_SPACE, 777,
+ reference_options[2].client_classes_);
+ ASSERT_FALSE(found_desc.option_);
+ reference_options.pop_back();
+
+ // Delete the secon reference option.
+ ASSERT_NO_THROW(cfg.del(DHCP6_OPTION_SPACE, 777, reference_options[1].client_classes_));
+
+ // Make sure we can no longer find the deleted option.
+ found_desc = cfg.get(DHCP6_OPTION_SPACE, 777, reference_options[1].client_classes_);
+ ASSERT_FALSE(found_desc.option_);
+ reference_options.pop_back();
+
+ // Verify we still have one option.
+ auto options = cfg.getAll(DHCP6_OPTION_SPACE);
+ ASSERT_EQ(options->size(), reference_options.size());
+ auto reference_desc = reference_options.begin();
+ auto const& idx = options->get<0>();
+ for (auto& returned_desc : idx) {
+ ASSERT_EQ(*reference_desc, returned_desc);
+ ++reference_desc;
+ }
+}
+
+
} // end of anonymous namespace
-// Copyright (C) 2018-2024 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2018-2025 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
distance--;
}
}
+
+void
+GenericConfigBackendDHCPv4Test::subnetOption4WithClienClassesTest() {
+
+ Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 24,
+ 30, 40, 60, 1024));
+
+ // 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);
+
+ 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);
+
+ 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);
+
+ 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_);
+
+ auto found_opts = subnet->getCfgOption()->getList(DHCP4_OPTION_SPACE, DHO_BOOT_FILE_NAME);
+ ASSERT_EQ(3, found_opts.size());
+
+ // 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());
+ ASSERT_TRUE(returned_subnet);
+
+ {
+ SCOPED_TRACE("CREATE audit entry for a new subnet");
+ testNewAuditEntry("dhcp4_subnet",
+ AuditEntry::ModificationType::CREATE,
+ "subnet set");
+ }
+
+ // 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);
+
+ 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);
+ }
+
+ {
+ 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");
+ }
+
+ // We have added one option to the existing subnet. We should now have
+ // three options.
+ ASSERT_EQ(3, countRows("dhcp4_options"));
+
+ 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);
+
+ 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_);
+
+ {
+ SCOPED_TRACE("verify returned option with modified persistence");
+ testOptionsEquivalent(*opt_boot_file_name, returned_opt_boot_file_name);
+ }
+
+ {
+ SCOPED_TRACE("UPDATE audit entry for an updated subnet option");
+ testNewAuditEntry("dhcp4_subnet",
+ AuditEntry::ModificationType::UPDATE,
+ "subnet specific option set");
+ }
+
+ // 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"));
+
+ // 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_));
+
+ 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_);
+
+ {
+ SCOPED_TRACE("UPDATE audit entry for a deleted subnet option");
+ testNewAuditEntry("dhcp4_subnet",
+ AuditEntry::ModificationType::UPDATE,
+ "subnet specific option deleted");
+ }
+
+ // We should have only two options after deleting one of them.
+ ASSERT_EQ(2, countRows("dhcp4_options"));
+#endif
+}
/// event and it does not matter).
void multipleAuditEntriesTest();
+ void subnetOption4WithClienClassesTest();
+
/// @brief Holds pointers to subnets used in tests.
std::vector<Subnet4Ptr> test_subnets_;
uint64_t
TestConfigBackendDHCPv4::deleteOption4(const db::ServerSelector& server_selector,
const uint16_t code,
- const std::string& space) {
+ const std::string& space,
+ ClientClassesPtr client_classes) {
auto tag = getServerTag(server_selector);
uint64_t erased = 0;
for (auto option_it = options_.begin(); option_it != options_.end(); ) {
if ((option_it->option_->getType() == code) &&
(option_it->space_name_ == space) &&
- (option_it->hasServerTag(ServerTag(tag)))) {
+ (option_it->hasServerTag(ServerTag(tag))) &&
+ (!client_classes || (option_it->client_classes_ == *client_classes))) {
option_it = options_.erase(option_it);
++erased;
} else {
TestConfigBackendDHCPv4::deleteOption4(const db::ServerSelector& server_selector,
const std::string& shared_network_name,
const uint16_t code,
- const std::string& space) {
+ const std::string& space,
+ ClientClassesPtr client_classes) {
auto& index = shared_networks_.get<SharedNetworkNameIndexTag>();
auto network_it = index.find(shared_network_name);
}
}
}
+
+
if (!found) {
isc_throw(BadValue, "attempted to delete option in a "
"shared network " << shared_network_name
<< " not present in a selected server");
}
+ if (client_classes) {
+ return (shared_network->getCfgOption()->del(space, code, *client_classes));
+ }
+
return (shared_network->getCfgOption()->del(space, code));
}
TestConfigBackendDHCPv4::deleteOption4(const db::ServerSelector& server_selector,
const SubnetID& subnet_id,
const uint16_t code,
- const std::string& space) {
+ const std::string& space,
+ ClientClassesPtr client_classes) {
auto& index = subnets_.get<SubnetSubnetIdIndexTag>();
auto subnet_it = index.find(subnet_id);
<< " not present in a selected server");
}
+ if (client_classes) {
+ return (subnet->getCfgOption()->del(space, code, *client_classes));
+ }
+
return (subnet->getCfgOption()->del(space, code));
}
const asiolink::IOAddress& pool_start_address,
const asiolink::IOAddress& pool_end_address,
const uint16_t code,
- const std::string& space) {
+ const std::string& space,
+ ClientClassesPtr client_classes) {
auto not_in_selected_servers = false;
for (auto const& subnet : subnets_) {
// Get the pool: if it is not here we can directly go to the next subnet.
}
}
+ if (client_classes) {
+ return (pool->getCfgOption()->del(space, code, *client_classes));
+ }
+
return (pool->getCfgOption()->del(space, code));
}
/// @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 Number of deleted options.
virtual uint64_t
- deleteOption4(const db::ServerSelector& server_selector, const uint16_t code,
- const std::string& space);
+ deleteOption4(const db::ServerSelector& server_selector,
+ const uint16_t code,
+ const std::string& space,
+ ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes shared network level option.
///
/// belongs to.
/// @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.
virtual uint64_t
deleteOption4(const db::ServerSelector& server_selector,
const std::string& shared_network_name,
const uint16_t code,
- const std::string& space);
+ const std::string& space,
+ ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes subnet level option.
///
/// belongs.
/// @param code Code of the deleted option.
/// @param space Option space of the deleted option.
+ /// @param client_classes Optional client classes list of the option to be deleted.
+ /// Defaults to an empty pointer.
/// @return Number of deleted options.
virtual uint64_t
deleteOption4(const db::ServerSelector& server_selector, const SubnetID& subnet_id,
- const uint16_t code, const std::string& space);
+ const uint16_t code, const std::string& space,
+ ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes pool level option.
///
/// deleted option belongs.
/// @param code Code of the deleted option.
/// @param space Option space of the deleted option.
+ /// @param client_classes Optional client classes list of the option to be deleted.
+ /// Defaults to an empty pointer.
/// @return Number of deleted options.
virtual uint64_t
deleteOption4(const db::ServerSelector& server_selector,
const asiolink::IOAddress& pool_start_address,
const asiolink::IOAddress& pool_end_address,
const uint16_t code,
- const std::string& space);
+ const std::string& space,
+ ClientClassesPtr client_classes = ClientClassesPtr());
/// @brief Deletes global parameter.
///