2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-31 22:15:23 +00:00

[#549] implement reservation-update

This commit is contained in:
Andrei Pavel
2023-04-05 14:48:26 +03:00
parent d1a6a0bd32
commit f9a24c2f60
10 changed files with 149 additions and 3 deletions

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2015-2022 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2015-2023 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
@@ -85,6 +85,14 @@ public:
isc::Exception(file, line, what) {}
};
/// @brief Thrown when it is expected that some rows are affected,
/// usually during a DELETE or an UPDATE, but none are.
class NoRowsAffected : public Exception {
public:
NoRowsAffected(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) {}
};
} // namespace isc
} // namespace db

View File

@@ -25,6 +25,13 @@ public:
isc::Exception(file, line, what) { };
};
/// @brief Exception thrown when a @c Host object is expected, but none are found.
class HostNotFound : public Exception {
public:
HostNotFound(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
/// @brief Exception thrown when an address is already reserved by a @c Host
/// object (DuplicateHost is same identity, ReservedAddress same address).
class ReservedAddress : public Exception {
@@ -452,6 +459,13 @@ public:
const Host::IdentifierType& identifier_type,
const uint8_t* identifier_begin, const size_t identifier_len) = 0;
/// @brief Attempts to update an existing host entry.
///
/// @param host the host up to date with the requested changes
///
/// @return true if deletion was successful, false if the host was not there.
virtual void update(HostPtr const& host) = 0;
/// @brief Return backend type
///
/// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
@@ -516,7 +530,7 @@ typedef boost::shared_ptr<BaseHostDataSource> HostDataSourcePtr;
/// @brief HostDataSource list
typedef std::vector<HostDataSourcePtr> HostDataSourceList;
}
}
} // namespace dhcp
} // namespace isc
#endif // BASE_HOST_DATA_SOURCE_H

View File

@@ -19,6 +19,7 @@
using namespace isc::asiolink;
using namespace isc::data;
using namespace std;
namespace isc {
namespace dhcp {
@@ -1141,6 +1142,24 @@ CfgHosts::del6(const SubnetID& /*subnet_id*/,
return (false);
}
void
CfgHosts::update(HostPtr const& host) {
bool deleted(false);
if (CfgMgr::instance().getFamily() == AF_INET) {
vector<uint8_t> const& identifier(host->getIdentifier());
deleted = del4(host->getIPv4SubnetID(), host->getIdentifierType(), identifier.data(),
identifier.size());
} else {
vector<uint8_t> const& identifier(host->getIdentifier());
deleted = del6(host->getIPv6SubnetID(), host->getIdentifierType(), identifier.data(),
identifier.size());
}
if (!deleted) {
isc_throw(HostNotFound, "Host not updated (not found).");
}
add(host);
}
bool
CfgHosts::setIPReservationsUnique(const bool unique) {
ip_reservations_unique_ = unique;

View File

@@ -589,6 +589,9 @@ public:
const Host::IdentifierType& identifier_type,
const uint8_t* identifier_begin, const size_t identifier_len);
/// @brief Implements @ref BaseHostDataSource::update() for config hosts.
void update(HostPtr const& host);
/// @brief Return backend type
///
/// Returns the type of the backend (e.g. "mysql", "memfile" etc.)

View File

@@ -617,6 +617,21 @@ HostMgr::del6(const SubnetID& subnet_id, const Host::IdentifierType& identifier_
return (false);
}
void
HostMgr::update(HostPtr const& host) {
if (alternate_sources_.empty()) {
isc_throw(NoHostDataSourceManager,
"Unable to update existing host because there is no hosts-database configured.");
}
for (HostDataSourcePtr const& source : alternate_sources_) {
source->update(host);
}
// If no backend throws the host should be cached.
if (cache_ptr_) {
cache(host);
}
}
void
HostMgr::cache(ConstHostPtr host) const {
if (cache_ptr_) {

View File

@@ -555,6 +555,9 @@ public:
del6(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type,
const uint8_t* identifier_begin, const size_t identifier_len);
/// @brief Implements @ref BaseHostDataSource::update() for alternate sources.
void update(HostPtr const& host);
/// @brief Return backend type
///
/// Returns the type of the backend (e.g. "mysql", "memfile" etc.)

View File

@@ -3921,6 +3921,45 @@ MySqlHostDataSource::getAll6(const SubnetID& subnet_id,
return (collection);
}
void
MySqlHostDataSource::update(HostPtr const& host) {
// Get a context.
MySqlHostContextAlloc const context(*impl_);
MySqlHostContextPtr ctx(context.ctx_);
// If operating in read-only mode, throw exception.
impl_->checkReadOnly(ctx);
// Initiate MySQL transaction as we will have to make multiple queries
// to update host information into multiple tables. If that fails on
// any stage, the transaction will be rolled back by the destructor of
// the MySqlTransaction class.
MySqlTransaction transaction(ctx->conn_);
// As much as having dedicated prepared statements for updating tables would be consistent with
// the implementation of other commands, it's difficult if not impossible to cover all cases for
// updating the host to exactly as is described in the command, which may involve inserts and
// deletes alongside updates. So let's delete and add. The delete cascades into all tables. The
// add explicitly adds into all tables.
bool deleted(false);
if (CfgMgr::instance().getFamily() == AF_INET) {
vector<uint8_t> const& identifier(host->getIdentifier());
deleted = del4(host->getIPv4SubnetID(), host->getIdentifierType(), identifier.data(),
identifier.size());
} else {
vector<uint8_t> const& identifier(host->getIdentifier());
deleted = del6(host->getIPv6SubnetID(), host->getIdentifierType(), identifier.data(),
identifier.size());
}
if (!deleted) {
isc_throw(NoRowsAffected, "Host not updated (not found).");
}
add(host);
// Everything went fine, so explicitly commit the transaction.
transaction.commit();
}
// Miscellaneous database methods.
std::string

View File

@@ -403,6 +403,9 @@ public:
getAll6(const SubnetID& subnet_id,
const asiolink::IOAddress& address) const;
/// @brief Implements @ref BaseHostDataSource::update() for MySQL.
void update(HostPtr const& host);
/// @brief Return backend type
///
/// Returns the type of the backend (e.g. "mysql", "memfile" etc.)

View File

@@ -3191,6 +3191,45 @@ PgSqlHostDataSource::getAll6(const SubnetID& subnet_id,
return (collection);
}
void
PgSqlHostDataSource::update(HostPtr const& host) {
// Get a context.
PgSqlHostContextAlloc const context(*impl_);
PgSqlHostContextPtr ctx(context.ctx_);
// If operating in read-only mode, throw exception.
impl_->checkReadOnly(ctx);
// Initiate PostgreSQL transaction as we will have to make multiple queries
// to update host information into multiple tables. If that fails on
// any stage, the transaction will be rolled back by the destructor of
// the PgSqlTransaction class.
PgSqlTransaction transaction(ctx->conn_);
// As much as having dedicated prepared statements for updating tables would be consistent with
// the implementation of other commands, it's difficult if not impossible to cover all cases for
// updating the host to exactly as is described in the command, which may involve inserts and
// deletes alongside updates. So let's delete and add. The delete cascades into all tables. The
// add explicitly adds into all tables.
bool deleted(false);
if (CfgMgr::instance().getFamily() == AF_INET) {
vector<uint8_t> const& identifier(host->getIdentifier());
deleted = del4(host->getIPv4SubnetID(), host->getIdentifierType(), identifier.data(),
identifier.size());
} else {
vector<uint8_t> const& identifier(host->getIdentifier());
deleted = del6(host->getIPv6SubnetID(), host->getIdentifierType(), identifier.data(),
identifier.size());
}
if (!deleted) {
isc_throw(NoRowsAffected, "Host not updated (not found).");
}
add(host);
// Everything went fine, so explicitly commit the transaction.
transaction.commit();
}
// Miscellaneous database methods.
std::string

View File

@@ -451,6 +451,9 @@ public:
getAll6(const SubnetID& subnet_id,
const asiolink::IOAddress& address) const;
/// @brief Implements @ref BaseHostDataSource::update() for PostgreSQL.
void update(HostPtr const& host);
/// @brief Return backend type
///
/// Returns the type of database as the string "postgresql". This is