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:
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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.)
|
||||
|
@@ -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_) {
|
||||
|
@@ -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.)
|
||||
|
@@ -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
|
||||
|
@@ -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.)
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user