5#include <asiolink/io_address.h>
6#include <dhcp/hwaddr.h>
16#include "log_admindb_host_reservation_importer.h"
21STMT_NAME_T STMT_INSERT_HOST_RESERVATION{
"insert_host_reservation"};
22STMT_NAME_T STMT_DELETE_HOST_RESERVATION{
"delete_host_reservation"};
23STMT_NAME_T STMT_QUERY_HOST_RESERVATION_BY_MAC{
"query_host_reservation_by_mac"};
24STMT_NAME_T STMT_QUERY_HOST_RESERVATION_BY_IP{
"query_host_reservation_by_ip"};
25STMT_NAME_T STMT_INSERT_TMP_HOST_RESERVATION{
"insert_tmp_host_reservation"};
26STMT_NAME_T STMT_CREATE_TMP_HOST_TABLE{
"create_tmp_host_table"};
27STMT_NAME_T STMT_FLUSH_TMP_HOST_TABLE{
"flush_tmp_host_table"};
36 std::ostringstream stmt_insert_host_reservation;
37 stmt_insert_host_reservation <<
"INSERT INTO hosts (dhcp_identifier, dhcp_identifier_type, "
38 "ipv4_address, dhcp4_subnet_id) "
41 std::string stmt_insert_host_reservation_str = stmt_insert_host_reservation.str();
42 psql.prepare(STMT_INSERT_HOST_RESERVATION, stmt_insert_host_reservation_str);
44 std::string stmt_delete_host_reservation{
45 "DELETE FROM hosts WHERE dhcp_identifier = $1 AND ipv4_address = $2 AND dhcp4_subnet_id "
47 psql.prepare(STMT_DELETE_HOST_RESERVATION, stmt_delete_host_reservation);
49 const std::string stmt_query_host_reservation_by_ip{
50 "SELECT dhcp_identifier, dhcp_identifier_type, ipv4_address, dhcp4_subnet_id FROM hosts "
51 "WHERE ipv4_address = $1;"};
52 psql.prepare(STMT_QUERY_HOST_RESERVATION_BY_IP, stmt_query_host_reservation_by_ip);
53 const std::string stmt_query_host_reservation_by_mac{
54 "SELECT dhcp_identifier, dhcp_identifier_type, ipv4_address, dhcp4_subnet_id FROM hosts "
55 "WHERE dhcp_identifier = $1;"};
56 psql.prepare(STMT_QUERY_HOST_RESERVATION_BY_MAC, stmt_query_host_reservation_by_mac);
58 std::string stmt_create_tmp_host_table{
59 "CREATE TEMPORARY TABLE IF NOT EXISTS tmp_full_host_reservations("
60 "id SERIAL PRIMARY KEY NOT NULL,"
61 "mac_address BYTEA NOT NULL,"
62 "ipv4_address BIGINT NOT NULL,"
63 "subnet_id INT NOT NULL);"};
64 psql.prepare(STMT_CREATE_TMP_HOST_TABLE, stmt_create_tmp_host_table);
66 std::string stmt_flush_tmp_host_table{
"TRUNCATE tmp_full_host_reservations;"};
67 psql.prepare(STMT_FLUSH_TMP_HOST_TABLE, stmt_flush_tmp_host_table);
74#if KCH_PQXX_MAJOR_VERSION < 7
79 assert(host_update.
ipv4.isV4());
80 const uint32_t ipv4_address{host_update.
ipv4.toUint32()};
83 transaction.exec_prepared(STMT_INSERT_HOST_RESERVATION, dhcp_identifier, ipv4_address,
86 transaction.exec_prepared(STMT_DELETE_HOST_RESERVATION, dhcp_identifier, ipv4_address,
92 pqxx::work table_transaction{
psql};
93 table_transaction.exec_prepared0(STMT_CREATE_TMP_HOST_TABLE);
94 table_transaction.exec_prepared0(STMT_FLUSH_TMP_HOST_TABLE);
95 table_transaction.commit();
102 std::string stmt_insert_tmp_host_reservation{
103 "INSERT INTO tmp_full_host_reservations "
104 " (mac_address, ipv4_address, subnet_id) "
107 psql.prepare(STMT_INSERT_TMP_HOST_RESERVATION, stmt_insert_tmp_host_reservation);
111 const std::vector<AdminDBClient::HostUpdate>& full_sync)
113 for (
const auto& sync_item : full_sync) {
114#if KCH_PQXX_MAJOR_VERSION < 7
119 transaction.exec_prepared(STMT_INSERT_TMP_HOST_RESERVATION, mac_bin,
120 sync_item.ipv4.toUint32(), sync_item.subnet_id);
123 psql.unprepare(STMT_INSERT_TMP_HOST_RESERVATION);
130 std::string query_compare_host_tables{
132 " hosts.host_id AS l_id,"
133 " hosts.dhcp_identifier AS l_mac,"
134 " hosts.ipv4_address AS l_ip,"
135 " hosts.dhcp4_subnet_id AS l_subnet,"
136 " tmp_full_host_reservations.id AS r_id,"
137 " tmp_full_host_reservations.mac_address AS r_mac,"
138 " tmp_full_host_reservations.ipv4_address AS r_ip, "
139 " tmp_full_host_reservations.subnet_id AS r_subnet "
141 "FULL OUTER JOIN tmp_full_host_reservations "
142 "ON hosts.dhcp_identifier = tmp_full_host_reservations.mac_address "
143 "AND hosts.ipv4_address = tmp_full_host_reservations.ipv4_address "
144 "AND hosts.dhcp4_subnet_id = tmp_full_host_reservations.subnet_id "
145 "WHERE hosts.host_id IS NULL "
146 "OR tmp_full_host_reservations.id IS NULL "
149 return transaction.exec(query_compare_host_tables);
154 if (row.at(
"r_id").is_null()) {
155#if KCH_PQXX_MAJOR_VERSION < 7
156 const bytestr_t mac_raw{row.at(
"l_mac")};
158 const auto mac_raw = row.at(
"l_mac").as<
bytestr_t>();
161 isc::asiolink::IOAddress{row.at(
"l_ip").as<uint32_t>()},
162 isc::dhcp::HWAddr{
util::to_bytea(mac_raw), isc::dhcp::HTYPE_ETHER},
163 row.at(
"l_subnet").as<uint32_t>()};
166 if (row.at(
"l_id").is_null()) {
167#if KCH_PQXX_MAJOR_VERSION < 7
168 const bytestr_t mac_raw{row.at(
"r_mac")};
170 const auto mac_raw = row.at(
"r_mac").as<
bytestr_t>();
173 isc::asiolink::IOAddress{row.at(
"r_ip").as<uint32_t>()},
174 isc::dhcp::HWAddr{
util::to_bytea(mac_raw), isc::dhcp::HTYPE_ETHER},
175 row.at(
"r_subnet").as<uint32_t>()};
178 throw std::runtime_error(__FILE__
": compare_full_sync(): lines in JOIN without NULL id");
183#if KCH_PQXX_MAJOR_VERSION < 7
184 const bytestr_t mac_raw{row.at(
"dhcp_identifier")};
186 const auto mac_raw = row.at(
"dhcp_identifier").as<
bytestr_t>();
189 const auto ip_raw = row.at(
"ipv4_address").as<uint32_t>();
190 const auto subnet_id = row.at(
"dhcp4_subnet_id").as<uint32_t>();
193 isc::dhcp::HWAddr{mac, isc::dhcp::HTYPE_ETHER}, subnet_id};
198 transaction.exec0(
"DROP TABLE tmp_full_host_reservations;");
205 std::string_view username, std::string_view password,
206 std::string_view database)
208 std::ostringstream connstr;
209 connstr <<
"postgresql://" << username <<
':' << password <<
'@' << host <<
':' << port <<
'/'
211 p_impl = std::make_unique<LocalDBClientPrivate>(connstr.str());
218 pqxx::work transaction{p_impl->psql};
219 p_impl->apply_host_update(transaction, host_update);
220 transaction.commit();
225 pqxx::work transaction{p_impl->psql};
226 for (
const auto& u : host_updates) {
228 .arg(u.mac.toText(
false))
229 .arg(u.ipv4.toText())
230 .arg(u.incremental_update_id)
233 p_impl->apply_host_update(transaction, u);
235 transaction.commit();
238std::vector<AdminDBClient::HostUpdate>
241 p_impl->setup_tmp_host_table();
242 p_impl->prepare_insert_tmp_host_reservations();
243 pqxx::work transaction{p_impl->psql};
244 p_impl->insert_tmp_host_reservations(transaction, full_sync);
246 const pqxx::result differing_rows =
247 p_impl->compare_current_and_tmp_host_reservations(transaction);
248 std::vector<AdminDBClient::HostUpdate> diff;
249 diff.reserve(differing_rows.size());
250 for (
const pqxx::row& row : differing_rows) {
255 transaction.commit();
259std::vector<AdminDBClient::HostUpdate>
262 pqxx::work txn{p_impl->psql};
264#if KCH_PQXX_MAJOR_VERSION < 7
270 const pqxx::result res = txn.exec_prepared(STMT_QUERY_HOST_RESERVATION_BY_MAC, mac_bin);
271 std::vector<AdminDBClient::HostUpdate> hosts;
273 for (
const pqxx::row& row : res) {
274 hosts.push_back(p_impl->make_host_update_from_hosts_row(row));
280std::vector<AdminDBClient::HostUpdate>
283 pqxx::work txn{p_impl->psql};
285 const pqxx::result res =
286 txn.exec_prepared(STMT_QUERY_HOST_RESERVATION_BY_IP, ip_addr.toUint32());
287 std::vector<AdminDBClient::HostUpdate> hosts;
289 for (
const pqxx::row& row : res) {
Byte array conversion utilities.
LocalDBClient(std::string_view host, std::string_view port, std::string_view username, std::string_view password, std::string_view database)
void apply_host_updates(const std::vector< AdminDBClient::HostUpdate > &host_updates)
Apply a bunch of host updates to the local Kea database.
std::vector< AdminDBClient::HostUpdate > compare_full_sync(const std::vector< AdminDBClient::HostUpdate > &full_sync)
Compare the current database state to the result of a full sync from AdminDB.
static constexpr int DHCP_IDENTIFIER_TYPE_MAC
Kea DHCP identifier type MAC-adresss.
void apply_host_update(const AdminDBClient::HostUpdate &host_update)
Apply a host update to the local Kea database.
std::vector< AdminDBClient::HostUpdate > query_host_reservation(const isc::dhcp::HWAddr &mac)
Query the database for host reservations with the given MAC.
Collection of common variables used throughout the library.
std::basic_string< std::byte > byte_string
std::vector< uint8_t > to_bytea(byte_string_view str)
Convert a byte string to a byte array.
byte_string to_bytestr(const std::vector< uint8_t > &bytea)
Convert a byte array to a byte string.
pqxx::binarystring to_binstr(const std::vector< std::uint8_t > &bytea)
Convert a byte array to a binary string.
Compatibility macros and utilities for libpqxx < 7.
pqxx::binarystring bytestr_t
Type type
Whether the given host reservation shall be added or removed.
isc::asiolink::IOAddress ipv4
static AdminDBClient::HostUpdate make_host_update_from_hosts_row(const pqxx::row &row)
static AdminDBClient::HostUpdate make_host_update_from_diff_row(const pqxx::row &row)
LocalDBClientPrivate(const LocalDBClientPrivate &)=delete
~LocalDBClientPrivate()=default
auto compare_current_and_tmp_host_reservations(pqxx::work &transaction)
void insert_tmp_host_reservations(pqxx::work &transaction, const std::vector< AdminDBClient::HostUpdate > &full_sync)
static void remove_tmp_host_table(pqxx::work &transaction)
void setup_tmp_host_table()
void apply_host_update(pqxx::work &transaction, const AdminDBClient::HostUpdate &host_update)
void prepare_insert_tmp_host_reservations()
LocalDBClientPrivate(const std::string &connstr)