Kea 2.6.2
mysql_host_data_source.cc
Go to the documentation of this file.
1// Copyright (C) 2015-2025 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
11#include <dhcp/libdhcp++.h>
12#include <dhcp/option.h>
14#include <dhcp/option_space.h>
16#include <dhcpsrv/cfg_option.h>
17#include <dhcpsrv/cfgmgr.h>
18#include <dhcpsrv/dhcpsrv_log.h>
19#include <dhcpsrv/host_mgr.h>
22#include <dhcpsrv/timer_mgr.h>
23#include <util/buffer.h>
25#include <util/optional.h>
26
27#include <boost/algorithm/string/split.hpp>
28#include <boost/algorithm/string/classification.hpp>
29#include <boost/array.hpp>
30#include <boost/foreach.hpp>
31#include <boost/pointer_cast.hpp>
32#include <boost/static_assert.hpp>
33
34#include <mysql.h>
35#include <mysqld_error.h>
36#include <stdint.h>
37
38#include <mutex>
39#include <string>
40
41using namespace isc;
42using namespace isc::asiolink;
43using namespace isc::db;
44using namespace isc::dhcp;
45using namespace isc::util;
46using namespace isc::data;
47using namespace std;
48
49namespace {
50
55const uint8_t MAX_IDENTIFIER_TYPE = static_cast<uint8_t>(Host::LAST_IDENTIFIER_TYPE);
56
85class MySqlHostExchange {
86private:
91
92 static const size_t HOST_ID_COL = 0;
93 static const size_t DHCP_IDENTIFIER_COL = 1;
94 static const size_t DHCP_IDENTIFIER_TYPE_COL = 2;
95 static const size_t DHCP4_SUBNET_ID_COL = 3;
96 static const size_t DHCP6_SUBNET_ID_COL = 4;
97 static const size_t IPV4_ADDRESS_COL = 5;
98 static const size_t HOSTNAME_COL = 6;
99 static const size_t DHCP4_CLIENT_CLASSES_COL = 7;
100 static const size_t DHCP6_CLIENT_CLASSES_COL = 8;
101 static const size_t USER_CONTEXT_COL = 9;
102 static const size_t DHCP4_NEXT_SERVER_COL = 10;
103 static const size_t DHCP4_SERVER_HOSTNAME_COL = 11;
104 static const size_t DHCP4_BOOT_FILE_NAME_COL = 12;
105 static const size_t AUTH_KEY_COL = 13;
107
108 static const size_t HOST_COLUMNS = 14;
109
110public:
111
118 MySqlHostExchange(const size_t additional_columns_num = 0)
119 : columns_num_(HOST_COLUMNS + additional_columns_num),
120 bind_(columns_num_), columns_(columns_num_),
121 error_(columns_num_, MLM_FALSE), host_id_(0),
122 dhcp_identifier_length_(0), dhcp_identifier_type_(0),
123 dhcp4_subnet_id_(SUBNET_ID_UNUSED),
124 dhcp6_subnet_id_(SUBNET_ID_UNUSED), ipv4_address_(0),
125 hostname_length_(0), dhcp4_client_classes_length_(0),
126 dhcp6_client_classes_length_(0),
127 user_context_length_(0),
128 dhcp4_next_server_(0),
129 dhcp4_server_hostname_length_(0),
130 dhcp4_boot_file_name_length_(0),
131 auth_key_length_(0),
132 dhcp4_subnet_id_null_(MLM_FALSE),
133 dhcp6_subnet_id_null_(MLM_FALSE),
134 ipv4_address_null_(MLM_FALSE), hostname_null_(MLM_FALSE),
135 dhcp4_client_classes_null_(MLM_FALSE),
136 dhcp6_client_classes_null_(MLM_FALSE),
137 user_context_null_(MLM_FALSE),
138 dhcp4_next_server_null_(MLM_FALSE),
139 dhcp4_server_hostname_null_(MLM_FALSE),
140 dhcp4_boot_file_name_null_(MLM_FALSE),
141 auth_key_null_(MLM_FALSE) {
142
143 // Fill arrays with 0 so as they don't include any garbage.
144 memset(dhcp_identifier_buffer_, 0, sizeof(dhcp_identifier_buffer_));
145 memset(hostname_, 0, sizeof(hostname_));
146 memset(dhcp4_client_classes_, 0, sizeof(dhcp4_client_classes_));
147 memset(dhcp6_client_classes_, 0, sizeof(dhcp6_client_classes_));
148 memset(user_context_, 0, sizeof(user_context_));
149 memset(dhcp4_server_hostname_, 0, sizeof(dhcp4_server_hostname_));
150 memset(dhcp4_boot_file_name_, 0, sizeof(dhcp4_boot_file_name_));
151 memset(auth_key_, 0, sizeof(auth_key_));
152
153 // Set the column names for use by this class. This only comprises
154 // names used by the MySqlHostExchange class. Derived classes will
155 // need to set names for the columns they use.
156 columns_[HOST_ID_COL] = "host_id";
157 columns_[DHCP_IDENTIFIER_COL] = "dhcp_identifier";
158 columns_[DHCP_IDENTIFIER_TYPE_COL] = "dhcp_identifier_type";
159 columns_[DHCP4_SUBNET_ID_COL] = "dhcp4_subnet_id";
160 columns_[DHCP6_SUBNET_ID_COL] = "dhcp6_subnet_id";
161 columns_[IPV4_ADDRESS_COL] = "ipv4_address";
162 columns_[HOSTNAME_COL] = "hostname";
163 columns_[DHCP4_CLIENT_CLASSES_COL] = "dhcp4_client_classes";
164 columns_[DHCP6_CLIENT_CLASSES_COL] = "dhcp6_client_classes";
165 columns_[USER_CONTEXT_COL] = "user_context";
166 columns_[DHCP4_NEXT_SERVER_COL] = "dhcp4_next_server";
167 columns_[DHCP4_SERVER_HOSTNAME_COL] = "dhcp4_server_hostname";
168 columns_[DHCP4_BOOT_FILE_NAME_COL] = "dhcp4_boot_file_name";
169 columns_[AUTH_KEY_COL] = "auth_key";
170
171 BOOST_STATIC_ASSERT(13 < HOST_COLUMNS);
172 }
173
175 virtual ~MySqlHostExchange() {
176 }
177
192 size_t findAvailColumn() const {
193 std::vector<std::string>::const_iterator empty_column =
194 std::find(columns_.begin(), columns_.end(), std::string());
195 return (std::distance(columns_.begin(), empty_column));
196 }
197
201 uint64_t getHostId() const {
202 return (host_id_);
203 }
204
215 static void setErrorIndicators(std::vector<MYSQL_BIND>& bind,
216 std::vector<my_bools>& error) {
217 for (size_t i = 0; i < error.size(); ++i) {
218 error[i] = MLM_FALSE;
219 bind[i].error = reinterpret_cast<my_bool*>(&error[i]);
220 }
221 }
222
236 static std::string getColumnsInError(std::vector<my_bools>& error,
237 const std::vector<std::string>& names) {
238 std::string result = "";
239
240 // Accumulate list of column names
241 for (size_t i = 0; i < names.size(); ++i) {
242 if (error[i] == MLM_TRUE) {
243 if (!result.empty()) {
244 result += ", ";
245 }
246 result += names[i];
247 }
248 }
249
250 if (result.empty()) {
251 result = "(None)";
252 }
253
254 return (result);
255 }
256
269 std::vector<MYSQL_BIND> createBindForSend(const HostPtr& host, const bool unique_ip) {
270 // Store host object to ensure it remains valid.
271 host_ = host;
272
273 // Initialize prior to constructing the array of MYSQL_BIND structures.
274 // It sets all fields, including is_null, to zero, so we need to set
275 // is_null only if it should be true. This gives up minor performance
276 // benefit while being safe approach.
277 memset(&bind_[0], 0, sizeof(MYSQL_BIND) * bind_.size());
278
279 // Set up the structures for the various components of the host structure.
280
281 try {
282 // host_id : INT UNSIGNED NOT NULL
283 // The host_id is auto_incremented by MySQL database,
284 // so we need to pass the NULL value
285 host_id_ = 0;
286 bind_[0].buffer_type = MYSQL_TYPE_LONG;
287 bind_[0].buffer = reinterpret_cast<char*>(&host_id_);
288 bind_[0].is_unsigned = MLM_TRUE;
289
290 // dhcp_identifier : VARBINARY(255) NOT NULL
291 dhcp_identifier_length_ = host->getIdentifier().size();
292 memcpy(static_cast<void*>(dhcp_identifier_buffer_),
293 &(host->getIdentifier())[0],
294 host->getIdentifier().size());
295
296 bind_[1].buffer_type = MYSQL_TYPE_BLOB;
297 bind_[1].buffer = dhcp_identifier_buffer_;
298 bind_[1].buffer_length = dhcp_identifier_length_;
299 bind_[1].length = &dhcp_identifier_length_;
300
301 // dhcp_identifier_type : TINYINT NOT NULL
302 dhcp_identifier_type_ = static_cast<uint8_t>(host->getIdentifierType());
303 bind_[2].buffer_type = MYSQL_TYPE_TINY;
304 bind_[2].buffer = reinterpret_cast<char*>(&dhcp_identifier_type_);
305 bind_[2].is_unsigned = MLM_TRUE;
306
307 // dhcp4_subnet_id : INT UNSIGNED NULL
308 // Can't take an address of intermediate object, so let's store it
309 // in dhcp4_subnet_id_
310 dhcp4_subnet_id_ = host->getIPv4SubnetID();
311 dhcp4_subnet_id_null_ = host->getIPv4SubnetID() == SUBNET_ID_UNUSED ? MLM_TRUE : MLM_FALSE;
312 bind_[3].buffer_type = MYSQL_TYPE_LONG;
313 bind_[3].buffer = reinterpret_cast<char*>(&dhcp4_subnet_id_);
314 bind_[3].is_unsigned = MLM_TRUE;
315 bind_[3].is_null = &dhcp4_subnet_id_null_;
316
317 // dhcp6_subnet_id : INT UNSIGNED NULL
318 // Can't take an address of intermediate object, so let's store it
319 // in dhcp6_subnet_id_
320 dhcp6_subnet_id_ = host->getIPv6SubnetID();
321 dhcp6_subnet_id_null_ = host->getIPv6SubnetID() == SUBNET_ID_UNUSED ? MLM_TRUE : MLM_FALSE;
322 bind_[4].buffer_type = MYSQL_TYPE_LONG;
323 bind_[4].buffer = reinterpret_cast<char*>(&dhcp6_subnet_id_);
324 bind_[4].is_unsigned = MLM_TRUE;
325 bind_[4].is_null = &dhcp6_subnet_id_null_;
326
327 // ipv4_address : INT UNSIGNED NULL
328 // The address in the Host structure is an IOAddress object. Convert
329 // this to an integer for storage.
330 ipv4_address_ = host->getIPv4Reservation().toUint32();
331 ipv4_address_null_ = ipv4_address_ == 0 ? MLM_TRUE : MLM_FALSE;
332 bind_[5].buffer_type = MYSQL_TYPE_LONG;
333 bind_[5].buffer = reinterpret_cast<char*>(&ipv4_address_);
334 bind_[5].is_unsigned = MLM_TRUE;
335 bind_[5].is_null = &ipv4_address_null_;
336
337 // hostname : VARCHAR(255) NULL
338 strncpy(hostname_, host->getHostname().c_str(), HOSTNAME_MAX_LEN - 1);
339 hostname_length_ = host->getHostname().length();
340 bind_[6].buffer_type = MYSQL_TYPE_STRING;
341 bind_[6].buffer = reinterpret_cast<char*>(hostname_);
342 bind_[6].buffer_length = hostname_length_;
343
344 // dhcp4_client_classes : VARCHAR(255) NULL
345 bind_[7].buffer_type = MYSQL_TYPE_STRING;
346 // Override default separator to not include space after comma.
347 string classes4_txt = host->getClientClasses4().toText(",");
348 strncpy(dhcp4_client_classes_, classes4_txt.c_str(), CLIENT_CLASSES_MAX_LEN - 1);
349 bind_[7].buffer = dhcp4_client_classes_;
350 bind_[7].buffer_length = classes4_txt.length();
351
352 // dhcp6_client_classes : VARCHAR(255) NULL
353 bind_[8].buffer_type = MYSQL_TYPE_STRING;
354 // Override default separator to not include space after comma.
355 string classes6_txt = host->getClientClasses6().toText(",");
356 strncpy(dhcp6_client_classes_, classes6_txt.c_str(), CLIENT_CLASSES_MAX_LEN - 1);
357 bind_[8].buffer = dhcp6_client_classes_;
358 bind_[8].buffer_length = classes6_txt.length();
359
360 // user_context : TEXT NULL
361 ConstElementPtr ctx = host->getContext();
362 if (ctx) {
363 bind_[9].buffer_type = MYSQL_TYPE_STRING;
364 string ctx_txt = ctx->str();
365 strncpy(user_context_, ctx_txt.c_str(), USER_CONTEXT_MAX_LEN - 1);
366 bind_[9].buffer = user_context_;
367 bind_[9].buffer_length = ctx_txt.length();
368 } else {
369 bind_[9].buffer_type = MYSQL_TYPE_NULL;
370 }
371
372 // ipv4_address : INT UNSIGNED NULL
373 // The address in the Host structure is an IOAddress object. Convert
374 // this to an integer for storage.
375 dhcp4_next_server_ = host->getNextServer().toUint32();
376 bind_[10].buffer_type = MYSQL_TYPE_LONG;
377 bind_[10].buffer = reinterpret_cast<char*>(&dhcp4_next_server_);
378 bind_[10].is_unsigned = MLM_TRUE;
379 // bind_[10].is_null = &MLM_FALSE; // commented out for performance
380 // reasons, see memset() above
381
382 // dhcp4_server_hostname
383 bind_[11].buffer_type = MYSQL_TYPE_STRING;
384 std::string server_hostname = host->getServerHostname();
385 strncpy(dhcp4_server_hostname_, server_hostname.c_str(),
387 bind_[11].buffer = dhcp4_server_hostname_;
388 bind_[11].buffer_length = server_hostname.length();
389
390 // dhcp4_boot_file_name
391 bind_[12].buffer_type = MYSQL_TYPE_STRING;
392 std::string boot_file_name = host->getBootFileName();
393 strncpy(dhcp4_boot_file_name_, boot_file_name.c_str(),
395 bind_[12].buffer = dhcp4_boot_file_name_;
396 bind_[12].buffer_length = boot_file_name.length();
397
398 // auth key
399 bind_[13].buffer_type = MYSQL_TYPE_STRING;
400 std::string auth_key = host->getKey().toText();
401 std::strncpy(auth_key_, auth_key.c_str(), TEXT_AUTH_KEY_LEN - 1);
402 auth_key_null_ = auth_key.empty() ? MLM_TRUE : MLM_FALSE;
403 bind_[13].buffer = auth_key_;
404 bind_[13].buffer_length = auth_key.length();
405
406 } catch (const std::exception& ex) {
407 isc_throw(DbOperationError,
408 "Could not create bind array from Host: "
409 << host->getHostname() << ", reason: " << ex.what());
410 }
411
412 // Add the data to the vector.
413 std::vector<MYSQL_BIND> vec(bind_.begin(), bind_.begin() + HOST_COLUMNS);
414
415 // When checking whether the IP is unique we need to bind the IPv4 address
416 // at the end of the query as it has additional binding for the IPv4
417 // address.
418 if (unique_ip) {
419 vec.push_back(bind_[5]); // ipv4_address
420 vec.push_back(bind_[3]); // subnet_id
421 }
422 return (vec);
423 }
424
432 virtual std::vector<MYSQL_BIND> createBindForReceive() {
433 // Initialize MYSQL_BIND array.
434 // It sets all fields, including is_null, to zero, so we need to set
435 // is_null only if it should be true. This gives up minor performance
436 // benefit while being safe approach. For improved readability, the
437 // code that explicitly sets is_null is there, but is commented out.
438 // This also takes care of setting bind_[X].is_null to MLM_FALSE.
439 memset(&bind_[0], 0, sizeof(MYSQL_BIND) * bind_.size());
440
441 // host_id : INT UNSIGNED NOT NULL
442 bind_[0].buffer_type = MYSQL_TYPE_LONG;
443 bind_[0].buffer = reinterpret_cast<char*>(&host_id_);
444 bind_[0].is_unsigned = MLM_TRUE;
445
446 // dhcp_identifier : VARBINARY(255) NOT NULL
447 dhcp_identifier_length_ = sizeof(dhcp_identifier_buffer_);
448 bind_[1].buffer_type = MYSQL_TYPE_BLOB;
449 bind_[1].buffer = reinterpret_cast<char*>(dhcp_identifier_buffer_);
450 bind_[1].buffer_length = dhcp_identifier_length_;
451 bind_[1].length = &dhcp_identifier_length_;
452
453 // dhcp_identifier_type : TINYINT NOT NULL
454 bind_[2].buffer_type = MYSQL_TYPE_TINY;
455 bind_[2].buffer = reinterpret_cast<char*>(&dhcp_identifier_type_);
456 bind_[2].is_unsigned = MLM_TRUE;
457
458 // dhcp4_subnet_id : INT UNSIGNED NULL
459 dhcp4_subnet_id_null_ = MLM_FALSE;
460 bind_[3].buffer_type = MYSQL_TYPE_LONG;
461 bind_[3].buffer = reinterpret_cast<char*>(&dhcp4_subnet_id_);
462 bind_[3].is_unsigned = MLM_TRUE;
463 bind_[3].is_null = &dhcp4_subnet_id_null_;
464
465 // dhcp6_subnet_id : INT UNSIGNED NULL
466 dhcp6_subnet_id_null_ = MLM_FALSE;
467 bind_[4].buffer_type = MYSQL_TYPE_LONG;
468 bind_[4].buffer = reinterpret_cast<char*>(&dhcp6_subnet_id_);
469 bind_[4].is_unsigned = MLM_TRUE;
470 bind_[4].is_null = &dhcp6_subnet_id_null_;
471
472 // ipv4_address : INT UNSIGNED NULL
473 ipv4_address_null_ = MLM_FALSE;
474 bind_[5].buffer_type = MYSQL_TYPE_LONG;
475 bind_[5].buffer = reinterpret_cast<char*>(&ipv4_address_);
476 bind_[5].is_unsigned = MLM_TRUE;
477 bind_[5].is_null = &ipv4_address_null_;
478
479 // hostname : VARCHAR(255) NULL
480 hostname_null_ = MLM_FALSE;
481 hostname_length_ = sizeof(hostname_);
482 bind_[6].buffer_type = MYSQL_TYPE_STRING;
483 bind_[6].buffer = reinterpret_cast<char*>(hostname_);
484 bind_[6].buffer_length = hostname_length_;
485 bind_[6].length = &hostname_length_;
486 bind_[6].is_null = &hostname_null_;
487
488 // dhcp4_client_classes : VARCHAR(255) NULL
489 dhcp4_client_classes_null_ = MLM_FALSE;
490 dhcp4_client_classes_length_ = sizeof(dhcp4_client_classes_);
491 bind_[7].buffer_type = MYSQL_TYPE_STRING;
492 bind_[7].buffer = reinterpret_cast<char*>(dhcp4_client_classes_);
493 bind_[7].buffer_length = dhcp4_client_classes_length_;
494 bind_[7].length = &dhcp4_client_classes_length_;
495 bind_[7].is_null = &dhcp4_client_classes_null_;
496
497 // dhcp6_client_classes : VARCHAR(255) NULL
498 dhcp6_client_classes_null_ = MLM_FALSE;
499 dhcp6_client_classes_length_ = sizeof(dhcp6_client_classes_);
500 bind_[8].buffer_type = MYSQL_TYPE_STRING;
501 bind_[8].buffer = reinterpret_cast<char*>(dhcp6_client_classes_);
502 bind_[8].buffer_length = dhcp6_client_classes_length_;
503 bind_[8].length = &dhcp6_client_classes_length_;
504 bind_[8].is_null = &dhcp6_client_classes_null_;
505
506 // user_context : TEXT NULL
507 user_context_null_ = MLM_FALSE;
508 user_context_length_ = sizeof(user_context_);
509 bind_[9].buffer_type = MYSQL_TYPE_STRING;
510 bind_[9].buffer = reinterpret_cast<char*>(user_context_);
511 bind_[9].buffer_length = user_context_length_;
512 bind_[9].length = &user_context_length_;
513 bind_[9].is_null = &user_context_null_;
514
515 // dhcp4_next_server
516 dhcp4_next_server_null_ = MLM_FALSE;
517 bind_[10].buffer_type = MYSQL_TYPE_LONG;
518 bind_[10].buffer = reinterpret_cast<char*>(&dhcp4_next_server_);
519 bind_[10].is_unsigned = MLM_TRUE;
520 bind_[10].is_null = &dhcp4_next_server_null_;
521
522 // dhcp4_server_hostname
523 dhcp4_server_hostname_null_ = MLM_FALSE;
524 dhcp4_server_hostname_length_ = sizeof(dhcp4_server_hostname_);
525 bind_[11].buffer_type = MYSQL_TYPE_STRING;
526 bind_[11].buffer = reinterpret_cast<char*>(dhcp4_server_hostname_);
527 bind_[11].buffer_length = dhcp4_server_hostname_length_;
528 bind_[11].length = &dhcp4_server_hostname_length_;
529 bind_[11].is_null = &dhcp4_server_hostname_null_;
530
531 // dhcp4_boot_file_name
532 dhcp4_boot_file_name_null_ = MLM_FALSE;
533 dhcp4_boot_file_name_length_ = sizeof(dhcp4_boot_file_name_);
534 bind_[12].buffer_type = MYSQL_TYPE_STRING;
535 bind_[12].buffer = reinterpret_cast<char*>(dhcp4_boot_file_name_);
536 bind_[12].buffer_length = dhcp4_boot_file_name_length_;
537 bind_[12].length = &dhcp4_boot_file_name_length_;
538 bind_[12].is_null = &dhcp4_boot_file_name_null_;
539
540 // auth_key_
541 auth_key_null_ = MLM_FALSE;
542 auth_key_length_ = sizeof(auth_key_);
543 bind_[13].buffer_type = MYSQL_TYPE_STRING;
544 bind_[13].buffer = reinterpret_cast<char*>(auth_key_);
545 bind_[13].buffer_length = auth_key_length_;
546 bind_[13].length = &auth_key_length_;
547 bind_[13].is_null = &auth_key_null_;
548
549 // Add the error flags
550 setErrorIndicators(bind_, error_);
551
552 // Add the data to the vector. Note the end element is one after the
553 // end of the array.
554 return (bind_);
555 }
556
565 HostPtr retrieveHost() {
566 // Check if the identifier stored in the database is correct.
567 if (dhcp_identifier_type_ > MAX_IDENTIFIER_TYPE) {
568 isc_throw(BadValue, "invalid dhcp identifier type returned: "
569 << static_cast<int>(dhcp_identifier_type_));
570 }
571 // Set the dhcp identifier type in a variable of the appropriate
572 // data type.
574 static_cast<Host::IdentifierType>(dhcp_identifier_type_);
575
576 // Set DHCPv4 subnet ID to the value returned. If NULL returned,
577 // set to 0.
578 SubnetID ipv4_subnet_id(SUBNET_ID_UNUSED);
579 if (dhcp4_subnet_id_null_ == MLM_FALSE) {
580 ipv4_subnet_id = dhcp4_subnet_id_;
581 }
582
583 // Set DHCPv6 subnet ID to the value returned. If NULL returned,
584 // set to 0.
585 SubnetID ipv6_subnet_id(SUBNET_ID_UNUSED);
586 if (dhcp6_subnet_id_null_ == MLM_FALSE) {
587 ipv6_subnet_id = dhcp6_subnet_id_;
588 }
589
590 // Set IPv4 address reservation if it was given, if not, set IPv4 zero
591 // address
592 asiolink::IOAddress ipv4_reservation = asiolink::IOAddress::IPV4_ZERO_ADDRESS();
593 if (ipv4_address_null_ == MLM_FALSE) {
594 ipv4_reservation = asiolink::IOAddress(ipv4_address_);
595 }
596
597 // Set hostname if non NULL value returned. Otherwise, leave an
598 // empty string.
599 std::string hostname;
600 if (hostname_null_ == MLM_FALSE) {
601 hostname = std::string(hostname_, hostname_length_);
602 }
603
604 // Set DHCPv4 client classes if non NULL value returned.
605 std::string dhcp4_client_classes;
606 if (dhcp4_client_classes_null_ == MLM_FALSE) {
607 dhcp4_client_classes = std::string(dhcp4_client_classes_,
608 dhcp4_client_classes_length_);
609 }
610
611 // Set DHCPv6 client classes if non NULL value returned.
612 std::string dhcp6_client_classes;
613 if (dhcp6_client_classes_null_ == MLM_FALSE) {
614 dhcp6_client_classes = std::string(dhcp6_client_classes_,
615 dhcp6_client_classes_length_);
616 }
617
618 // Convert user_context to string as well.
619 std::string user_context;
620 if (user_context_null_ == MLM_FALSE) {
621 user_context_[user_context_length_] = '\0';
622 user_context.assign(user_context_);
623 }
624
625 // Set next server value (siaddr) if non NULL value returned.
626 asiolink::IOAddress next_server = asiolink::IOAddress::IPV4_ZERO_ADDRESS();
627 if (dhcp4_next_server_null_ == MLM_FALSE) {
628 next_server = asiolink::IOAddress(dhcp4_next_server_);
629 }
630
631 // Set server hostname (sname) if non NULL value returned.
632 std::string dhcp4_server_hostname;
633 if (dhcp4_server_hostname_null_ == MLM_FALSE) {
634 dhcp4_server_hostname = std::string(dhcp4_server_hostname_,
635 dhcp4_server_hostname_length_);
636 }
637
638 // Set boot file name (file) if non NULL value returned.
639 std::string dhcp4_boot_file_name;
640 if (dhcp4_boot_file_name_null_ == MLM_FALSE) {
641 dhcp4_boot_file_name = std::string(dhcp4_boot_file_name_,
642 dhcp4_boot_file_name_length_);
643 }
644
645 // Set the auth key if a non empty array is retrieved
646 std::string auth_key;
647 if (auth_key_null_ == MLM_FALSE) {
648 auth_key = std::string(auth_key_, auth_key_length_);
649 }
650
651 // Create and return Host object from the data gathered.
652 HostPtr h(new Host(dhcp_identifier_buffer_, dhcp_identifier_length_,
653 type, ipv4_subnet_id, ipv6_subnet_id, ipv4_reservation,
654 hostname, dhcp4_client_classes, dhcp6_client_classes,
655 next_server, dhcp4_server_hostname,
656 dhcp4_boot_file_name, AuthKey(auth_key)));
657 h->setHostId(host_id_);
658
659 // Set the user context if there is one.
660 if (!user_context.empty()) {
661 try {
662 ConstElementPtr ctx = Element::fromJSON(user_context);
663 if (!ctx || (ctx->getType() != Element::map)) {
664 isc_throw(BadValue, "user context '" << user_context
665 << "' is not a JSON map");
666 }
667 h->setContext(ctx);
668 } catch (const isc::data::JSONError& ex) {
669 isc_throw(BadValue, "user context '" << user_context
670 << "' is invalid JSON: " << ex.what());
671 }
672 }
673
674 return (h);
675 }
676
690 virtual void processFetchedData(ConstHostCollection& hosts) {
691 HostPtr host;
692 // Add new host only if there are no hosts yet or the host id of the
693 // most recently added host is different than the host id of the
694 // currently processed host.
695 if (hosts.empty() || (hosts.back()->getHostId() != getHostId())) {
696 // Create Host object from the fetched data and append it to the
697 // collection.
698 host = retrieveHost();
699 hosts.push_back(host);
700 }
701 }
702
713 std::string getErrorColumns() {
714 return (getColumnsInError(error_, columns_));
715 }
716
717protected:
718
720 size_t columns_num_;
721
723 std::vector<MYSQL_BIND> bind_;
724
726 std::vector<std::string> columns_;
727
729 std::vector<my_bools> error_;
730
733 HostPtr host_;
734
735private:
736
738 uint64_t host_id_;
739
742 uint8_t dhcp_identifier_buffer_[ClientId::MAX_CLIENT_ID_LEN];
743
745 unsigned long dhcp_identifier_length_;
746
749 uint8_t dhcp_identifier_type_;
750
752 uint32_t dhcp4_subnet_id_;
753
755 uint32_t dhcp6_subnet_id_;
756
758 uint32_t ipv4_address_;
759
761 char hostname_[HOSTNAME_MAX_LEN];
762
764 unsigned long hostname_length_;
765
767 char dhcp4_client_classes_[CLIENT_CLASSES_MAX_LEN];
768
771 unsigned long dhcp4_client_classes_length_;
772
774 char dhcp6_client_classes_[CLIENT_CLASSES_MAX_LEN];
775
778 unsigned long dhcp6_client_classes_length_;
779
781 char user_context_[USER_CONTEXT_MAX_LEN];
782
784 unsigned long user_context_length_;
785
787 uint32_t dhcp4_next_server_;
788
790 char dhcp4_server_hostname_[SERVER_HOSTNAME_MAX_LEN];
791
793 unsigned long dhcp4_server_hostname_length_;
794
796 char dhcp4_boot_file_name_[BOOT_FILE_NAME_MAX_LEN];
797
799 unsigned long dhcp4_boot_file_name_length_;
800
802 char auth_key_[TEXT_AUTH_KEY_LEN];
803
805 unsigned long auth_key_length_;
806
809
810
811 my_bool dhcp4_subnet_id_null_;
812
814 my_bool dhcp6_subnet_id_null_;
815
817 my_bool ipv4_address_null_;
818
820 my_bool hostname_null_;
821
824 my_bool dhcp4_client_classes_null_;
825
828 my_bool dhcp6_client_classes_null_;
829
831 my_bool user_context_null_;
832
834 my_bool dhcp4_next_server_null_;
835
837 my_bool dhcp4_server_hostname_null_;
838
840 my_bool dhcp4_boot_file_name_null_;
841
843 my_bool auth_key_null_;
844
846};
847
857class MySqlHostWithOptionsExchange : public MySqlHostExchange {
858private:
859
861 static const size_t OPTION_COLUMNS = 8;
862
877 class OptionProcessor {
878 public:
879
886 OptionProcessor(const Option::Universe& universe,
887 const size_t start_column)
888 : universe_(universe), start_column_(start_column), option_id_(0),
889 code_(0), value_length_(0), formatted_value_length_(0),
890 space_length_(0), persistent_(false), cancelled_(false),
891 user_context_length_(0),
892 option_id_null_(MLM_FALSE), code_null_(MLM_FALSE),
893 value_null_(MLM_FALSE), formatted_value_null_(MLM_FALSE),
894 space_null_(MLM_FALSE), user_context_null_(MLM_FALSE),
895 option_id_index_(start_column), code_index_(start_column_ + 1),
896 value_index_(start_column_ + 2),
897 formatted_value_index_(start_column_ + 3),
898 space_index_(start_column_ + 4),
899 persistent_index_(start_column_ + 5),
900 cancelled_index_(start_column_ + 6),
901 user_context_index_(start_column_ + 7),
902 most_recent_option_id_(0) {
903
904 memset(value_, 0, sizeof(value_));
905 memset(formatted_value_, 0, sizeof(formatted_value_));
906 memset(space_, 0, sizeof(space_));
907 memset(user_context_, 0, sizeof(user_context_));
908 }
909
911 uint64_t getOptionId() const {
912 if (option_id_null_ == MLM_FALSE) {
913 return (option_id_);
914 }
915 return (0);
916 }
917
930 void retrieveOption(const CfgOptionPtr& cfg) {
931 // option_id may be NULL if dhcp4_options or dhcp6_options table
932 // doesn't contain any options for the particular host. Also, the
933 // current option id must be greater than id if the most recent
934 // option because options are ordered by option id. Otherwise
935 // we assume that this is already processed option.
936 if ((option_id_null_ == MLM_TRUE) ||
937 (most_recent_option_id_ >= option_id_)) {
938 return;
939 }
940
941 // Remember current option id as the most recent processed one. We
942 // will be comparing it with option ids in subsequent rows.
943 most_recent_option_id_ = option_id_;
944
945 // Convert it to string object for easier comparison.
946 std::string space;
947 if (space_null_ == MLM_FALSE) {
948 // Typically, the string values returned by the database are not
949 // NULL terminated.
950 space_[space_length_] = '\0';
951 space.assign(space_);
952 }
953
954 // If empty or null space provided, use a default top level space.
955 if (space.empty()) {
956 space = (universe_ == Option::V4 ?
958 }
959
960 // Convert formatted_value to string as well.
961 std::string formatted_value;
962 if (formatted_value_null_ == MLM_FALSE) {
963 formatted_value_[formatted_value_length_] = '\0';
964 formatted_value.assign(formatted_value_);
965 }
966
967 // Convert user_context to string as well.
968 std::string user_context;
969 if (user_context_null_ == MLM_FALSE) {
970 user_context_[user_context_length_] = '\0';
971 user_context.assign(user_context_);
972 }
973
974 // Options are held in a binary or textual format in the database.
975 // This is similar to having an option specified in a server
976 // configuration file. Such option is converted to appropriate C++
977 // class, using option definition. Thus, we need to find the
978 // option definition for this option code and option space.
979
980 // Check if this is a standard option.
981 OptionDefinitionPtr def = LibDHCP::getOptionDef(space, code_);
982
983 // Otherwise, we may check if this an option encapsulated within the
984 // vendor space.
985 if (!def && (space != DHCP4_OPTION_SPACE) &&
986 (space != DHCP6_OPTION_SPACE)) {
987 uint32_t vendor_id = LibDHCP::optionSpaceToVendorId(space);
988 if (vendor_id > 0) {
989 def = LibDHCP::getVendorOptionDef(universe_, vendor_id, code_);
990 }
991 }
992
993 // In all other cases, we use runtime option definitions, which
994 // should be also registered within the libdhcp++.
995 if (!def) {
996 def = LibDHCP::getRuntimeOptionDef(space, code_);
997 }
998
999 // Finish with a last resort option definition.
1000 if (!def) {
1001 def = LibDHCP::getLastResortOptionDef(space, code_);
1002 }
1003
1004 OptionPtr option;
1005
1006 // If no definition found, we use generic option type.
1007 if (!def) {
1008 // We have to pay attention if the value is NULL. If it is,
1009 // we must create an empty option instance. We can't rely on
1010 // the value_length_ because it may contain garbage for the
1011 // null values. Thus we check explicitly whether or not the
1012 // NULL flag is set.
1013 if (value_null_ == MLM_FALSE) {
1014 OptionBuffer buf(value_, value_ + value_length_);
1015 option.reset(new Option(universe_, code_, buf.begin(),
1016 buf.end()));
1017 } else {
1018 option.reset(new Option(universe_, code_));
1019 }
1020 } else {
1021 // The option value may be specified in textual or binary format
1022 // in the database. If formatted_value is empty, the binary
1023 // format is used. Depending on the format we use a different
1024 // variant of the optionFactory function.
1025 if (formatted_value.empty()) {
1026 OptionBuffer buf(value_, value_ + value_length_);
1027 // Again, check if the value is null before submitting the
1028 // buffer to the factory function.
1029 option = def->optionFactory(universe_, code_, buf.begin(),
1030 value_null_ == MLM_FALSE ? buf.end() :
1031 buf.begin());
1032 } else {
1033 // Spit the value specified in comma separated values
1034 // format.
1035 std::vector<std::string> split_vec;
1036 boost::split(split_vec, formatted_value, boost::is_any_of(","));
1037 option = def->optionFactory(universe_, code_, split_vec);
1038 }
1039 }
1040
1041 OptionDescriptor desc(option, persistent_, cancelled_,
1042 formatted_value);
1043
1044 // Set the user context if there is one into the option descriptor.
1045 if (!user_context.empty()) {
1046 try {
1047 ConstElementPtr ctx = Element::fromJSON(user_context);
1048 if (!ctx || (ctx->getType() != Element::map)) {
1049 isc_throw(BadValue, "user context '" << user_context
1050 << "' is no a JSON map");
1051 }
1052 desc.setContext(ctx);
1053 } catch (const isc::data::JSONError& ex) {
1054 isc_throw(BadValue, "user context '" << user_context
1055 << "' is invalid JSON: " << ex.what());
1056 }
1057 }
1058 cfg->add(desc, space);
1059 }
1060
1065 void setColumnNames(std::vector<std::string>& columns) {
1066 columns[option_id_index_] = "option_id";
1067 columns[code_index_] = "code";
1068 columns[value_index_] = "value";
1069 columns[formatted_value_index_] = "formatted_value";
1070 columns[space_index_] = "space";
1071 columns[persistent_index_] = "persistent";
1072 columns[cancelled_index_] = "cancelled";
1073 columns[user_context_index_] = "user_context";
1074 }
1075
1082 void setBindFields(std::vector<MYSQL_BIND>& bind) {
1083 // This method is called just before making a new query, so we
1084 // reset the most_recent_option_id_ and other exchange members to
1085 // start over with options processing.
1086 most_recent_option_id_ = 0;
1087
1088 option_id_ = 0;
1089 code_ = 0;
1090 persistent_ = false;
1091 cancelled_ = false;
1092 option_id_null_ = MLM_FALSE;
1093 code_null_ = MLM_FALSE;
1094 value_null_ = MLM_FALSE;
1095 formatted_value_null_ = MLM_FALSE;
1096 space_null_ = MLM_FALSE;
1097 user_context_null_ = MLM_FALSE;
1098
1099 memset(value_, 0, sizeof(value_));
1100 memset(formatted_value_, 0, sizeof(formatted_value_));
1101 memset(space_, 0, sizeof(space_));
1102 memset(user_context_, 0, sizeof(user_context_));
1103
1104 // option_id : INT UNSIGNED NOT NULL AUTO_INCREMENT,
1105 bind[option_id_index_].buffer_type = MYSQL_TYPE_LONG;
1106 bind[option_id_index_].buffer = reinterpret_cast<char*>(&option_id_);
1107 bind[option_id_index_].is_unsigned = MLM_TRUE;
1108 bind[option_id_index_].is_null = &option_id_null_;
1109
1110 // code : TINYINT OR SHORT UNSIGNED NOT NULL
1111 bind[code_index_].buffer_type = MYSQL_TYPE_SHORT;
1112 bind[code_index_].buffer = reinterpret_cast<char*>(&code_);
1113 bind[code_index_].is_unsigned = MLM_TRUE;
1114 bind[code_index_].is_null = &code_null_;
1115
1116 // value : BLOB NULL
1117 value_length_ = sizeof(value_);
1118 bind[value_index_].buffer_type = MYSQL_TYPE_BLOB;
1119 bind[value_index_].buffer = reinterpret_cast<char*>(value_);
1120 bind[value_index_].buffer_length = value_length_;
1121 bind[value_index_].length = &value_length_;
1122 bind[value_index_].is_null = &value_null_;
1123
1124 // formatted_value : TEXT NULL
1125 formatted_value_length_ = sizeof(formatted_value_);
1126 bind[formatted_value_index_].buffer_type = MYSQL_TYPE_STRING;
1127 bind[formatted_value_index_].buffer = reinterpret_cast<char*>(formatted_value_);
1128 bind[formatted_value_index_].buffer_length = formatted_value_length_;
1129 bind[formatted_value_index_].length = &formatted_value_length_;
1130 bind[formatted_value_index_].is_null = &formatted_value_null_;
1131
1132 // space : VARCHAR(128) NULL
1133 space_length_ = sizeof(space_);
1134 bind[space_index_].buffer_type = MYSQL_TYPE_STRING;
1135 bind[space_index_].buffer = reinterpret_cast<char*>(space_);
1136 bind[space_index_].buffer_length = space_length_;
1137 bind[space_index_].length = &space_length_;
1138 bind[space_index_].is_null = &space_null_;
1139
1140 // persistent : TINYINT(1) NOT NULL DEFAULT 0
1141 bind[persistent_index_].buffer_type = MYSQL_TYPE_TINY;
1142 bind[persistent_index_].buffer = reinterpret_cast<char*>(&persistent_);
1143 bind[persistent_index_].is_unsigned = MLM_TRUE;
1144
1145 // cancelled : TINYINT(1) NOT NULL DEFAULT 0
1146 bind[cancelled_index_].buffer_type = MYSQL_TYPE_TINY;
1147 bind[cancelled_index_].buffer = reinterpret_cast<char*>(&cancelled_);
1148 bind[cancelled_index_].is_unsigned = MLM_TRUE;
1149
1150 // user_context : TEXT NULL
1151 user_context_length_ = sizeof(user_context_);
1152 bind[user_context_index_].buffer_type = MYSQL_TYPE_STRING;
1153 bind[user_context_index_].buffer = reinterpret_cast<char*>(user_context_);
1154 bind[user_context_index_].buffer_length = user_context_length_;
1155 bind[user_context_index_].length = &user_context_length_;
1156 bind[user_context_index_].is_null = &user_context_null_;
1157 }
1158
1159 private:
1160
1162 Option::Universe universe_;
1163
1165 size_t start_column_;
1166
1168 uint32_t option_id_;
1169
1171 uint16_t code_;
1172
1174 uint8_t value_[OPTION_VALUE_MAX_LEN];
1175
1177 unsigned long value_length_;
1178
1180 char formatted_value_[OPTION_FORMATTED_VALUE_MAX_LEN];
1181
1183 unsigned long formatted_value_length_;
1184
1186 char space_[OPTION_SPACE_MAX_LEN];
1187
1189 unsigned long space_length_;
1190
1193 bool persistent_;
1194
1196 bool cancelled_;
1197
1199 char user_context_[USER_CONTEXT_MAX_LEN];
1200
1202 unsigned long user_context_length_;
1203
1206
1207
1208 my_bool option_id_null_;
1209
1211 my_bool code_null_;
1212
1214 my_bool value_null_;
1215
1218 my_bool formatted_value_null_;
1219
1221 my_bool space_null_;
1222
1224 my_bool user_context_null_;
1226
1228
1229
1230 size_t option_id_index_;
1231
1233 size_t code_index_;
1234
1236 size_t value_index_;
1237
1239 size_t formatted_value_index_;
1240
1242 size_t space_index_;
1243
1245 size_t persistent_index_;
1246
1248 size_t cancelled_index_;
1250
1252 size_t user_context_index_;
1253
1255 uint32_t most_recent_option_id_;
1256 };
1257
1259 typedef boost::shared_ptr<OptionProcessor> OptionProcessorPtr;
1260
1261public:
1262
1269 enum FetchedOptions {
1270 DHCP4_ONLY,
1271 DHCP6_ONLY,
1272 DHCP4_AND_DHCP6
1273 };
1274
1283 MySqlHostWithOptionsExchange(const FetchedOptions& fetched_options,
1284 const size_t additional_columns_num = 0)
1285 : MySqlHostExchange(getRequiredColumnsNum(fetched_options)
1286 + additional_columns_num),
1287 opt_proc4_(), opt_proc6_() {
1288
1289 // Create option processor for DHCPv4 options, if required.
1290 if ((fetched_options == DHCP4_ONLY) ||
1291 (fetched_options == DHCP4_AND_DHCP6)) {
1292 opt_proc4_.reset(new OptionProcessor(Option::V4,
1293 findAvailColumn()));
1294 opt_proc4_->setColumnNames(columns_);
1295 }
1296
1297 // Create option processor for DHCPv6 options, if required.
1298 if ((fetched_options == DHCP6_ONLY) ||
1299 (fetched_options == DHCP4_AND_DHCP6)) {
1300 opt_proc6_.reset(new OptionProcessor(Option::V6,
1301 findAvailColumn()));
1302 opt_proc6_->setColumnNames(columns_);
1303 }
1304 }
1305
1314 virtual void processFetchedData(ConstHostCollection& hosts) override {
1315 // Holds pointer to the previously parsed host.
1316 HostPtr most_recent_host;
1317 if (!hosts.empty()) {
1318 // Const cast is not very elegant way to deal with it, but
1319 // there is a good reason to use it here. This method is called
1320 // to build a collection of const hosts to be returned to the
1321 // caller. If we wanted to use non-const collection we'd need
1322 // to copy the whole collection before returning it, which has
1323 // performance implications. Alternatively, we could store the
1324 // most recently added host in a class member but this would
1325 // make the code less readable.
1326 most_recent_host = boost::const_pointer_cast<Host>(hosts.back());
1327 }
1328
1329 // If no host has been parsed yet or we're at the row holding next
1330 // host, we create a new host object and put it at the end of the
1331 // list.
1332 if (!most_recent_host || (most_recent_host->getHostId() < getHostId())) {
1333 HostPtr host = retrieveHost();
1334 hosts.push_back(host);
1335 most_recent_host = host;
1336 }
1337
1338 // Parse DHCPv4 options if required to do so.
1339 if (opt_proc4_) {
1340 CfgOptionPtr cfg = most_recent_host->getCfgOption4();
1341 opt_proc4_->retrieveOption(cfg);
1342 }
1343
1344 // Parse DHCPv6 options if required to do so.
1345 if (opt_proc6_) {
1346 CfgOptionPtr cfg = most_recent_host->getCfgOption6();
1347 opt_proc6_->retrieveOption(cfg);
1348 }
1349 }
1350
1354 virtual std::vector<MYSQL_BIND> createBindForReceive() override {
1355 // The following call sets bind_ values between 0 and 8.
1356 static_cast<void>(MySqlHostExchange::createBindForReceive());
1357
1358 // Bind variables for DHCPv4 options.
1359 if (opt_proc4_) {
1360 opt_proc4_->setBindFields(bind_);
1361 }
1362
1363 // Bind variables for DHCPv6 options.
1364 if (opt_proc6_) {
1365 opt_proc6_->setBindFields(bind_);
1366 }
1367
1368 // Add the error flags
1369 setErrorIndicators(bind_, error_);
1370
1371 return (bind_);
1372 }
1373
1374private:
1375
1387 static size_t getRequiredColumnsNum(const FetchedOptions& fetched_options) {
1388 return (fetched_options == DHCP4_AND_DHCP6 ? 2 * OPTION_COLUMNS :
1389 OPTION_COLUMNS);
1390 }
1391
1395 OptionProcessorPtr opt_proc4_;
1396
1400 OptionProcessorPtr opt_proc6_;
1401};
1402
1415class MySqlHostIPv6Exchange : public MySqlHostWithOptionsExchange {
1416private:
1417
1419 static const size_t RESERVATION_COLUMNS = 5;
1420
1421public:
1422
1427 MySqlHostIPv6Exchange(const FetchedOptions& fetched_options)
1428 : MySqlHostWithOptionsExchange(fetched_options, RESERVATION_COLUMNS),
1429 reservation_id_(0),
1430 reserv_type_(0), reserv_type_null_(MLM_FALSE),
1431 ipv6_address_buffer_len_(0), prefix_len_(0), iaid_(0),
1432 reservation_id_index_(findAvailColumn()),
1433 address_index_(reservation_id_index_ + 1),
1434 prefix_len_index_(reservation_id_index_ + 2),
1435 type_index_(reservation_id_index_ + 3),
1436 iaid_index_(reservation_id_index_ + 4),
1437 most_recent_reservation_id_(0) {
1438
1439 memset(ipv6_address_buffer_, 0, sizeof(ipv6_address_buffer_));
1440
1441 // Provide names of additional columns returned by the queries.
1442 columns_[reservation_id_index_] = "reservation_id";
1443 columns_[address_index_] = "address";
1444 columns_[prefix_len_index_] = "prefix_len";
1445 columns_[type_index_] = "type";
1446 columns_[iaid_index_] = "dhcp6_iaid";
1447 }
1448
1452 uint32_t getReservationId() const {
1453 if (reserv_type_null_ == MLM_FALSE) {
1454 return (reservation_id_);
1455 }
1456 return (0);
1457 }
1458
1465 IPv6Resrv retrieveReservation() {
1466 // Set the IPv6 Reservation type (0 = IA_NA, 2 = IA_PD)
1468
1469 switch (reserv_type_) {
1470 case 0:
1471 type = IPv6Resrv::TYPE_NA;
1472 break;
1473
1474 case 2:
1475 type = IPv6Resrv::TYPE_PD;
1476 break;
1477
1478 default:
1479 isc_throw(BadValue,
1480 "invalid IPv6 reservation type returned: "
1481 << static_cast<int>(reserv_type_)
1482 << ". Only 0 or 2 are allowed.");
1483 }
1484
1485 IOAddress addr6 = IOAddress::fromBytes(AF_INET6, ipv6_address_buffer_);
1486 IPv6Resrv r(type, addr6, prefix_len_);
1487 return (r);
1488 }
1489
1509 virtual void processFetchedData(ConstHostCollection& hosts) override {
1510
1511 // Call parent class to fetch host information and options.
1512 MySqlHostWithOptionsExchange::processFetchedData(hosts);
1513
1514 if (getReservationId() == 0) {
1515 return;
1516 }
1517
1518 if (hosts.empty()) {
1519 isc_throw(Unexpected, "no host information while retrieving"
1520 " IPv6 reservation");
1521 }
1522 HostPtr host = boost::const_pointer_cast<Host>(hosts.back());
1523
1524 // If we're dealing with a new reservation, let's add it to the
1525 // host.
1526 if (getReservationId() > most_recent_reservation_id_) {
1527 most_recent_reservation_id_ = getReservationId();
1528
1529 if (most_recent_reservation_id_ > 0) {
1530 host->addReservation(retrieveReservation());
1531 }
1532 }
1533 }
1534
1543 virtual std::vector<MYSQL_BIND> createBindForReceive() override {
1544 // Reset most recent reservation id value because we're now making
1545 // a new SELECT query.
1546 most_recent_reservation_id_ = 0;
1547
1548 // Bind values supported by parent classes.
1549 static_cast<void>(MySqlHostWithOptionsExchange::createBindForReceive());
1550
1551 // reservation_id : INT UNSIGNED NOT NULL AUTO_INCREMENT
1552 bind_[reservation_id_index_].buffer_type = MYSQL_TYPE_LONG;
1553 bind_[reservation_id_index_].buffer = reinterpret_cast<char*>(&reservation_id_);
1554 bind_[reservation_id_index_].is_unsigned = MLM_TRUE;
1555
1556 // IPv6 address/prefix BINARY(16)
1557 ipv6_address_buffer_len_ = isc::asiolink::V6ADDRESS_LEN;
1558 bind_[address_index_].buffer_type = MYSQL_TYPE_BLOB;
1559 bind_[address_index_].buffer = reinterpret_cast<char*>(ipv6_address_buffer_);
1560 bind_[address_index_].buffer_length = ipv6_address_buffer_len_;
1561 bind_[address_index_].length = &ipv6_address_buffer_len_;
1562
1563 // prefix_len : TINYINT
1564 bind_[prefix_len_index_].buffer_type = MYSQL_TYPE_TINY;
1565 bind_[prefix_len_index_].buffer = reinterpret_cast<char*>(&prefix_len_);
1566 bind_[prefix_len_index_].is_unsigned = MLM_TRUE;
1567
1568 // (reservation) type : TINYINT
1569 reserv_type_null_ = MLM_FALSE;
1570 bind_[type_index_].buffer_type = MYSQL_TYPE_TINY;
1571 bind_[type_index_].buffer = reinterpret_cast<char*>(&reserv_type_);
1572 bind_[type_index_].is_unsigned = MLM_TRUE;
1573 bind_[type_index_].is_null = &reserv_type_null_;
1574
1575 // dhcp6_iaid INT UNSIGNED
1576 bind_[iaid_index_].buffer_type = MYSQL_TYPE_LONG;
1577 bind_[iaid_index_].buffer = reinterpret_cast<char*>(&iaid_);
1578 bind_[iaid_index_].is_unsigned = MLM_TRUE;
1579
1580 // Add the error flags
1581 setErrorIndicators(bind_, error_);
1582
1583 return (bind_);
1584 }
1585
1586private:
1587
1589 uint32_t reservation_id_;
1590
1592 uint8_t reserv_type_;
1593
1598 my_bool reserv_type_null_;
1599
1601 uint8_t ipv6_address_buffer_[isc::asiolink::V6ADDRESS_LEN];
1602
1604 unsigned long ipv6_address_buffer_len_;
1605
1607 uint8_t prefix_len_;
1608
1610 uint32_t iaid_;
1611
1613
1614
1615 size_t reservation_id_index_;
1616
1618 size_t address_index_;
1619
1621 size_t prefix_len_index_;
1622
1624 size_t type_index_;
1625
1627 size_t iaid_index_;
1628
1630
1632 uint32_t most_recent_reservation_id_;
1633};
1634
1645class MySqlIPv6ReservationExchange {
1646private:
1647
1649 static const size_t RESRV_COLUMNS = 6;
1650
1651public:
1652
1656 MySqlIPv6ReservationExchange()
1657 : host_id_(0), prefix_len_(0), type_(0),
1658 iaid_(0), resv_(IPv6Resrv::TYPE_NA, asiolink::IOAddress("::"), 128) {
1659
1660 // Reset error table.
1661 std::fill(&error_[0], &error_[RESRV_COLUMNS], MLM_FALSE);
1662
1663 // Set the column names (for error messages)
1664 columns_[0] = "host_id";
1665 columns_[1] = "address";
1666 columns_[2] = "prefix_len";
1667 columns_[3] = "type";
1668 columns_[4] = "dhcp6_iaid";
1669
1670 BOOST_STATIC_ASSERT(4 < RESRV_COLUMNS);
1671 }
1672
1687 std::vector<MYSQL_BIND> createBindForSend(const IPv6Resrv& resv,
1688 const HostID& id,
1689 const bool unique_ip) {
1690
1691 // Store the values to ensure they remain valid.
1692 resv_ = resv;
1693 host_id_ = id;
1694
1695 // Initialize prior to constructing the array of MYSQL_BIND structures.
1696 // It sets all fields, including is_null, to zero, so we need to set
1697 // is_null only if it should be true. This gives up minor performance
1698 // benefit while being safe approach. For improved readability, the
1699 // code that explicitly sets is_null is there, but is commented out.
1700 memset(bind_, 0, sizeof(bind_));
1701
1702 // Set up the structures for the various components of the host structure.
1703
1704 try {
1705 addr6_ = resv.getPrefix().toBytes();
1706 if (addr6_.size() != isc::asiolink::V6ADDRESS_LEN) {
1707 isc_throw(DbOperationError, "createBindForSend() - prefix is not "
1708 << isc::asiolink::V6ADDRESS_LEN << " bytes long");
1709 }
1710
1711 addr6_length_ = isc::asiolink::V6ADDRESS_LEN;
1712 bind_[0].buffer_type = MYSQL_TYPE_BLOB;
1713 bind_[0].buffer = reinterpret_cast<char*>(&addr6_[0]);
1714 bind_[0].buffer_length = isc::asiolink::V6ADDRESS_LEN;
1715 bind_[0].length = &addr6_length_;
1716
1717 // prefix_len tinyint
1718 prefix_len_ = resv.getPrefixLen();
1719 bind_[1].buffer_type = MYSQL_TYPE_TINY;
1720 bind_[1].buffer = reinterpret_cast<char*>(&prefix_len_);
1721 bind_[1].is_unsigned = MLM_TRUE;
1722
1723 // type tinyint
1724 // See lease6_types for values (0 = IA_NA, 1 = IA_TA, 2 = IA_PD)
1725 type_ = resv.getType() == IPv6Resrv::TYPE_NA ? 0 : 2;
1726 bind_[2].buffer_type = MYSQL_TYPE_TINY;
1727 bind_[2].buffer = reinterpret_cast<char*>(&type_);
1728 bind_[2].is_unsigned = MLM_TRUE;
1729
1730 // dhcp6_iaid INT UNSIGNED
1732 iaid_ = 0;
1733 bind_[3].buffer_type = MYSQL_TYPE_LONG;
1734 bind_[3].buffer = reinterpret_cast<char*>(&iaid_);
1735 bind_[3].is_unsigned = MLM_TRUE;
1736
1737 // host_id INT UNSIGNED NOT NULL
1738 bind_[4].buffer_type = MYSQL_TYPE_LONG;
1739 bind_[4].buffer = reinterpret_cast<char*>(&host_id_);
1740 bind_[4].is_unsigned = MLM_TRUE;
1741
1742 } catch (const std::exception& ex) {
1743 isc_throw(DbOperationError,
1744 "Could not create bind array from IPv6 Reservation: "
1745 << resv_.toText() << ", reason: " << ex.what());
1746 }
1747
1748 // Add the data to the vector. Note the end element is one after the
1749 // end of the array.
1750 // RESRV_COLUMNS -1 as we do not set reservation_id.
1751 std::vector<MYSQL_BIND> vec(&bind_[0], &bind_[RESRV_COLUMNS-1]);
1752
1753 // When checking whether the IP is unique we need to bind the IPv6 address
1754 // and prefix length at the end of the query as it has additional binding
1755 // for the IPv6 address and prefix length.
1756 if (unique_ip) {
1757 vec.push_back(bind_[0]); // address
1758 vec.push_back(bind_[1]); // prefix_len
1759 }
1760
1761 return (vec);
1762 }
1763
1764private:
1765
1767 uint64_t host_id_;
1768
1770 uint8_t prefix_len_;
1771
1773 uint8_t type_;
1774
1776 uint8_t iaid_;
1777
1779 IPv6Resrv resv_;
1780
1782 MYSQL_BIND bind_[RESRV_COLUMNS];
1783
1785 std::string columns_[RESRV_COLUMNS];
1786
1789 my_bool error_[RESRV_COLUMNS];
1790
1792 std::vector<uint8_t> addr6_;
1793
1795 unsigned long addr6_length_;
1796};
1797
1801class MySqlOptionExchange {
1803 static const size_t OPTION_COLUMNS = 10;
1804
1805public:
1806
1808 MySqlOptionExchange()
1809 : type_(0), value_len_(0), formatted_value_len_(0), space_(),
1810 space_len_(0), persistent_(false), cancelled_(false),
1811 user_context_(), user_context_len_(0), subnet_id_(SUBNET_ID_UNUSED),
1812 host_id_(0), option_() {
1813
1814 BOOST_STATIC_ASSERT(10 <= OPTION_COLUMNS);
1815 }
1816
1820 std::vector<MYSQL_BIND> createBindForSend(const OptionDescriptor& opt_desc,
1821 const std::string& opt_space,
1822 const Optional<SubnetID>& subnet_id,
1823 const HostID& host_id) {
1824
1825 // Hold pointer to the option to make sure it remains valid until
1826 // we complete a query.
1827 option_ = opt_desc.option_;
1828
1829 memset(bind_, 0, sizeof(bind_));
1830
1831 try {
1832 // option_id: INT UNSIGNED NOT NULL
1833 // The option_id is auto_incremented, so we need to pass the NULL
1834 // value.
1835 bind_[0].buffer_type = MYSQL_TYPE_NULL;
1836
1837 // code: SMALLINT UNSIGNED NOT NULL
1838 type_ = option_->getType();
1839 bind_[1].buffer_type = MYSQL_TYPE_SHORT;
1840 bind_[1].buffer = reinterpret_cast<char*>(&type_);
1841 bind_[1].is_unsigned = MLM_TRUE;
1842
1843 // value: BLOB NULL
1844 if (opt_desc.formatted_value_.empty() &&
1845 (opt_desc.option_->len() > opt_desc.option_->getHeaderLen())) {
1846 // The formatted_value is empty and the option value is
1847 // non-empty so we need to prepare on-wire format for the
1848 // option and store it in the database as a blob.
1849 OutputBuffer buf(opt_desc.option_->len());
1850 opt_desc.option_->pack(buf);
1851 const uint8_t* buf_ptr = buf.getData();
1852 value_.assign(buf_ptr + opt_desc.option_->getHeaderLen(),
1853 buf_ptr + buf.getLength());
1854 value_len_ = value_.size();
1855 bind_[2].buffer_type = MYSQL_TYPE_BLOB;
1856 bind_[2].buffer = &value_[0];
1857 bind_[2].buffer_length = value_len_;
1858 bind_[2].length = &value_len_;
1859
1860 } else {
1861 // No value or formatted_value specified. In this case, the
1862 // value blob is NULL.
1863 value_.clear();
1864 bind_[2].buffer_type = MYSQL_TYPE_NULL;
1865 }
1866
1867 // formatted_value: TEXT NULL,
1868 if (!opt_desc.formatted_value_.empty()) {
1869 formatted_value_len_ = opt_desc.formatted_value_.size();
1870 bind_[3].buffer_type = MYSQL_TYPE_STRING;
1871 bind_[3].buffer = const_cast<char*>(opt_desc.formatted_value_.c_str());
1872 bind_[3].buffer_length = formatted_value_len_;
1873 bind_[3].length = &formatted_value_len_;
1874
1875 } else {
1876 bind_[3].buffer_type = MYSQL_TYPE_NULL;
1877 }
1878
1879 // space: VARCHAR(128) NULL
1880 space_ = opt_space;
1881 space_len_ = space_.size();
1882 bind_[4].buffer_type = MYSQL_TYPE_STRING;
1883 bind_[4].buffer = const_cast<char*>(space_.c_str());
1884 bind_[4].buffer_length = space_len_;
1885 bind_[4].length = &space_len_;
1886
1887 // persistent: TINYINT(1) NOT NULL DEFAULT 0
1888 persistent_ = opt_desc.persistent_;
1889 bind_[5].buffer_type = MYSQL_TYPE_TINY;
1890 bind_[5].buffer = reinterpret_cast<char*>(&persistent_);
1891 bind_[5].is_unsigned = MLM_TRUE;
1892
1893 // cancelled: TINYINT(1) NOT NULL DEFAULT 0
1894 cancelled_ = opt_desc.cancelled_;
1895 bind_[6].buffer_type = MYSQL_TYPE_TINY;
1896 bind_[6].buffer = reinterpret_cast<char*>(&cancelled_);
1897 bind_[6].is_unsigned = MLM_TRUE;
1898
1899 // user_context: TEST NULL,
1900 ConstElementPtr ctx = opt_desc.getContext();
1901 if (ctx) {
1902 user_context_ = ctx->str();
1903 user_context_len_ = user_context_.size();
1904 bind_[7].buffer_type = MYSQL_TYPE_STRING;
1905 bind_[7].buffer = const_cast<char*>(user_context_.c_str());
1906 bind_[7].buffer_length = user_context_len_;
1907 bind_[7].length = &user_context_len_;
1908 } else {
1909 bind_[7].buffer_type = MYSQL_TYPE_NULL;
1910 }
1911
1912 // dhcp4_subnet_id: INT UNSIGNED NULL
1913 if (!subnet_id.unspecified()) {
1914 subnet_id_ = subnet_id;
1915 bind_[8].buffer_type = MYSQL_TYPE_LONG;
1916 bind_[8].buffer = reinterpret_cast<char*>(subnet_id_);
1917 bind_[8].is_unsigned = MLM_TRUE;
1918
1919 } else {
1920 bind_[8].buffer_type = MYSQL_TYPE_NULL;
1921 }
1922
1923 // host_id: INT UNSIGNED NOT NULL
1924 host_id_ = host_id;
1925 bind_[9].buffer_type = MYSQL_TYPE_LONG;
1926 bind_[9].buffer = reinterpret_cast<char*>(&host_id_);
1927 bind_[9].is_unsigned = MLM_TRUE;
1928
1929 } catch (const std::exception& ex) {
1930 isc_throw(DbOperationError,
1931 "Could not create bind array for inserting DHCP "
1932 "option: " << option_->toText() << ", reason: "
1933 << ex.what());
1934 }
1935
1936 return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[OPTION_COLUMNS]));
1937 }
1938
1939private:
1940
1942 uint16_t type_;
1943
1945 std::vector<uint8_t> value_;
1946
1948 unsigned long value_len_;
1949
1951 unsigned long formatted_value_len_;
1952
1954 std::string space_;
1955
1957 unsigned long space_len_;
1958
1961 bool persistent_;
1962
1965 bool cancelled_;
1966
1968 std::string user_context_;
1969
1971 unsigned long user_context_len_;
1972
1974 uint32_t subnet_id_;
1975
1977 uint32_t host_id_;
1978
1980 OptionPtr option_;
1981
1983 MYSQL_BIND bind_[OPTION_COLUMNS];
1984};
1985
1986} // namespace
1987
1988namespace isc {
1989namespace dhcp {
1990
2001public:
2002
2009 IOServiceAccessorPtr io_service_accessor,
2010 db::DbCallback db_reconnect_callback);
2011
2016
2019 boost::shared_ptr<MySqlHostWithOptionsExchange> host_ipv4_exchange_;
2020
2023 boost::shared_ptr<MySqlHostIPv6Exchange> host_ipv6_exchange_;
2024
2028 boost::shared_ptr<MySqlHostIPv6Exchange> host_ipv46_exchange_;
2029
2032 boost::shared_ptr<MySqlIPv6ReservationExchange> host_ipv6_reservation_exchange_;
2033
2037 boost::shared_ptr<MySqlOptionExchange> host_option_exchange_;
2038
2041
2044};
2045
2053public:
2054
2056 std::vector<MySqlHostContextPtr> pool_;
2057
2059 std::mutex mutex_;
2060};
2061
2063typedef boost::shared_ptr<MySqlHostContextPool> MySqlHostContextPoolPtr;
2064
2067public:
2068
2078 GET_HOST_DHCPID, // Gets hosts by host identifier
2079 GET_HOST_ADDR, // Gets hosts by IPv4 address
2080 GET_HOST_SUBID4_DHCPID, // Gets host by IPv4 SubnetID, HW address/DUID
2081 GET_HOST_SUBID6_DHCPID, // Gets host by IPv6 SubnetID, HW address/DUID
2082 GET_HOST_SUBID_ADDR, // Gets host by IPv4 SubnetID and IPv4 address
2083 GET_HOST_PREFIX, // Gets host by IPv6 prefix
2084 GET_HOST_SUBID6_ADDR, // Gets host by IPv6 SubnetID and IPv6 prefix
2085 GET_HOST_ADDR6, // Gets hosts by IPv6 address/prefix
2086 GET_HOST_SUBID4, // Gets hosts by IPv4 SubnetID
2087 GET_HOST_SUBID6, // Gets hosts by IPv6 SubnetID
2088 GET_HOST_HOSTNAME, // Gets hosts by hostname
2089 GET_HOST_HOSTNAME_SUBID4, // Gets hosts by hostname and IPv4 SubnetID
2090 GET_HOST_HOSTNAME_SUBID6, // Gets hosts by hostname and IPv6 SubnetID
2091 GET_HOST_SUBID4_PAGE, // Gets hosts by IPv4 SubnetID beginning by HID
2092 GET_HOST_SUBID6_PAGE, // Gets hosts by IPv6 SubnetID beginning by HID
2093 GET_HOST_PAGE4, // Gets v4 hosts beginning by HID
2094 GET_HOST_PAGE6, // Gets v6 hosts beginning by HID
2095 INSERT_HOST_NON_UNIQUE_IP, // Insert new host to collection with allowing IP duplicates
2096 INSERT_HOST_UNIQUE_IP, // Insert new host to collection with checking for IP duplicates
2097 INSERT_V6_RESRV_NON_UNIQUE,// Insert v6 reservation without checking that it is unique
2098 INSERT_V6_RESRV_UNIQUE, // Insert v6 reservation with checking that it is unique
2099 INSERT_V4_HOST_OPTION, // Insert DHCPv4 option
2100 INSERT_V6_HOST_OPTION, // Insert DHCPv6 option
2101 DEL_HOST_ADDR4, // Delete v4 host (subnet-id, addr4)
2102 DEL_HOST_ADDR6, // Delete v6 host (subnet-id, addr6)
2103 DEL_HOST_SUBID4_ID, // Delete v4 host (subnet-id, ident.type, identifier)
2104 DEL_HOST_SUBID6_ID, // Delete v6 host (subnet-id, ident.type, identifier)
2105 NUM_STATEMENTS // Number of statements
2106 };
2107
2114
2120
2123
2146 static bool dbReconnect(ReconnectCtlPtr db_reconnect_ctl);
2147
2158
2171 std::pair<uint32_t, uint32_t> getVersion(const std::string& timer_name = std::string()) const;
2172
2183 std::vector<MYSQL_BIND>& bind);
2184
2194 StatementIndex stindex,
2195 MYSQL_BIND* bind);
2196
2202 void addResv(MySqlHostContextPtr& ctx,
2203 const IPv6Resrv& resv,
2204 const HostID& id);
2205
2217 const OptionDescriptor& opt_desc,
2218 const std::string& opt_space,
2219 const Optional<SubnetID>& subnet_id,
2220 const HostID& host_id);
2221
2230 const StatementIndex& stindex,
2231 const ConstCfgOptionPtr& options_cfg,
2232 const uint64_t host_id);
2233
2246 const int status,
2247 const StatementIndex index,
2248 const char* what) const;
2249
2269 StatementIndex stindex,
2270 MYSQL_BIND* bind,
2271 boost::shared_ptr<MySqlHostExchange> exchange,
2273 bool single) const;
2274
2293 const SubnetID& subnet_id,
2294 const Host::IdentifierType& identifier_type,
2295 const uint8_t* identifier_begin,
2296 const size_t identifier_len,
2297 StatementIndex stindex,
2298 boost::shared_ptr<MySqlHostExchange> exchange) const;
2299
2309 void checkReadOnly(MySqlHostContextPtr& ctx) const;
2310
2313
2317
2320
2324
2326 std::string timer_name_;
2327};
2328
2329namespace {
2330
2332typedef boost::array<TaggedStatement, MySqlHostDataSourceImpl::NUM_STATEMENTS>
2333TaggedStatementArray;
2334
2337TaggedStatementArray tagged_statements = { {
2338 // Retrieves host information, IPv6 reservations and both DHCPv4 and
2339 // DHCPv6 options associated with the host. The LEFT JOIN clause is used
2340 // to retrieve information from 4 different tables using a single query.
2341 // Hence, this query returns multiple rows for a single host.
2343 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2344 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, "
2345 "h.hostname, h.dhcp4_client_classes, h.dhcp6_client_classes, "
2346 "h.user_context, "
2347 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2348 "h.dhcp4_boot_file_name, h.auth_key, "
2349 "o4.option_id, o4.code, o4.value, o4.formatted_value, o4.space, "
2350 "o4.persistent, o4.cancelled, o4.user_context, "
2351 "o6.option_id, o6.code, o6.value, o6.formatted_value, o6.space, "
2352 "o6.persistent, o6.cancelled, o6.user_context, "
2353 "r.reservation_id, r.address, r.prefix_len, r.type, "
2354 "r.dhcp6_iaid "
2355 "FROM hosts AS h "
2356 "LEFT JOIN dhcp4_options AS o4 "
2357 "ON h.host_id = o4.host_id "
2358 "LEFT JOIN dhcp6_options AS o6 "
2359 "ON h.host_id = o6.host_id "
2360 "LEFT JOIN ipv6_reservations AS r "
2361 "ON h.host_id = r.host_id "
2362 "WHERE dhcp_identifier = ? AND dhcp_identifier_type = ? "
2363 "ORDER BY h.host_id, o4.option_id, o6.option_id, r.reservation_id"},
2364
2365 // Retrieves host information along with the DHCPv4 options associated with
2366 // it. Left joining the dhcp4_options table results in multiple rows being
2367 // returned for the same host. The host is retrieved by IPv4 address.
2369 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2370 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2371 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2372 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2373 "h.dhcp4_boot_file_name, h.auth_key, "
2374 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2375 "o.persistent, o.cancelled, o.user_context "
2376 "FROM hosts AS h "
2377 "LEFT JOIN dhcp4_options AS o "
2378 "ON h.host_id = o.host_id "
2379 "WHERE ipv4_address = ? "
2380 "ORDER BY h.host_id, o.option_id"},
2381
2382 // Retrieves host information and DHCPv4 options using subnet identifier
2383 // and client's identifier. Left joining the dhcp4_options table results in
2384 // multiple rows being returned for the same host.
2386 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2387 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2388 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2389 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2390 "h.dhcp4_boot_file_name, h.auth_key, "
2391 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2392 "o.persistent, o.cancelled, o.user_context "
2393 "FROM hosts AS h "
2394 "LEFT JOIN dhcp4_options AS o "
2395 "ON h.host_id = o.host_id "
2396 "WHERE h.dhcp4_subnet_id = ? AND h.dhcp_identifier_type = ? "
2397 "AND h.dhcp_identifier = ? "
2398 "ORDER BY h.host_id, o.option_id"},
2399
2400 // Retrieves host information, IPv6 reservations and DHCPv6 options
2401 // associated with a host. The number of rows returned is a multiplication
2402 // of number of IPv6 reservations and DHCPv6 options.
2404 "SELECT h.host_id, h.dhcp_identifier, "
2405 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2406 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2407 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2408 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2409 "h.dhcp4_boot_file_name, h.auth_key, "
2410 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2411 "o.persistent, o.cancelled, o.user_context, "
2412 "r.reservation_id, r.address, r.prefix_len, r.type, "
2413 "r.dhcp6_iaid "
2414 "FROM hosts AS h "
2415 "LEFT JOIN dhcp6_options AS o "
2416 "ON h.host_id = o.host_id "
2417 "LEFT JOIN ipv6_reservations AS r "
2418 "ON h.host_id = r.host_id "
2419 "WHERE h.dhcp6_subnet_id = ? AND h.dhcp_identifier_type = ? "
2420 "AND h.dhcp_identifier = ? "
2421 "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2422
2423 // Retrieves host information and DHCPv4 options for the host using subnet
2424 // identifier and IPv4 reservation. Left joining the dhcp4_options table
2425 // results in multiple rows being returned for the host. The number of
2426 // rows depends on the number of options defined for the host.
2428 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2429 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2430 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2431 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2432 "h.dhcp4_boot_file_name, h.auth_key, "
2433 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2434 "o.persistent, o.cancelled, o.user_context "
2435 "FROM hosts AS h "
2436 "LEFT JOIN dhcp4_options AS o "
2437 "ON h.host_id = o.host_id "
2438 "WHERE h.dhcp4_subnet_id = ? AND h.ipv4_address = ? "
2439 "ORDER BY h.host_id, o.option_id"},
2440
2441 // Retrieves host information, IPv6 reservations and DHCPv6 options
2442 // associated with a host using prefix and prefix length. This query
2443 // returns host information for a single host. However, multiple rows
2444 // are returned due to left joining IPv6 reservations and DHCPv6 options.
2445 // The number of rows returned is multiplication of number of existing
2446 // IPv6 reservations and DHCPv6 options.
2448 "SELECT h.host_id, h.dhcp_identifier, "
2449 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2450 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2451 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2452 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2453 "h.dhcp4_boot_file_name, h.auth_key, "
2454 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2455 "o.persistent, o.cancelled, o.user_context,"
2456 "r.reservation_id, r.address, r.prefix_len, r.type, "
2457 "r.dhcp6_iaid "
2458 "FROM hosts AS h "
2459 "LEFT JOIN dhcp6_options AS o "
2460 "ON h.host_id = o.host_id "
2461 "LEFT JOIN ipv6_reservations AS r "
2462 "ON h.host_id = r.host_id "
2463 "WHERE h.host_id = "
2464 "( SELECT host_id FROM ipv6_reservations "
2465 "WHERE address = ? AND prefix_len = ? ) "
2466 "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2467
2468 // Retrieves host information, IPv6 reservations and DHCPv6 options
2469 // associated with a host using IPv6 subnet id and prefix. This query
2470 // returns host information for a single host. However, multiple rows
2471 // are returned due to left joining IPv6 reservations and DHCPv6 options.
2472 // The number of rows returned is multiplication of number of existing
2473 // IPv6 reservations and DHCPv6 options.
2475 "SELECT h.host_id, h.dhcp_identifier, "
2476 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2477 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2478 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2479 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2480 "h.dhcp4_boot_file_name, h.auth_key, "
2481 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2482 "o.persistent, o.cancelled, o.user_context, "
2483 "r.reservation_id, r.address, r.prefix_len, r.type, "
2484 "r.dhcp6_iaid "
2485 "FROM hosts AS h "
2486 "LEFT JOIN dhcp6_options AS o "
2487 "ON h.host_id = o.host_id "
2488 "LEFT JOIN ipv6_reservations AS r "
2489 "ON h.host_id = r.host_id "
2490 "WHERE h.dhcp6_subnet_id = ? AND h.host_id IN "
2491 "(SELECT host_id FROM ipv6_reservations "
2492 "WHERE address = ?) "
2493 "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2494
2495 // Retrieves host information, IPv6 reservations and DHCPv6 options
2496 // associated with a host using IPv6 address/prefix. This query
2497 // may return host information for one or more host reservations. Even
2498 // if only one host is found, multiple rows
2499 // are returned due to left joining IPv6 reservations and DHCPv6 options.
2500 // The number of rows returned is multiplication of number of existing
2501 // IPv6 reservations and DHCPv6 options.
2503 "SELECT h.host_id, h.dhcp_identifier, "
2504 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2505 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2506 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2507 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2508 "h.dhcp4_boot_file_name, h.auth_key, "
2509 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2510 "o.persistent, o.cancelled, o.user_context, "
2511 "r.reservation_id, r.address, r.prefix_len, r.type, "
2512 "r.dhcp6_iaid "
2513 "FROM hosts AS h "
2514 "LEFT JOIN dhcp6_options AS o "
2515 "ON h.host_id = o.host_id "
2516 "LEFT JOIN ipv6_reservations AS r "
2517 "ON h.host_id = r.host_id "
2518 "WHERE h.host_id IN "
2519 "(SELECT host_id FROM ipv6_reservations "
2520 "WHERE address = ?) "
2521 "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2522
2523 // Retrieves host information along with the DHCPv4 options associated with
2524 // it. Left joining the dhcp4_options table results in multiple rows being
2525 // returned for the same host. Hosts are retrieved by IPv4 subnet id.
2527 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2528 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2529 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2530 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2531 "h.dhcp4_boot_file_name, h.auth_key, "
2532 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2533 "o.persistent, o.cancelled, o.user_context "
2534 "FROM hosts AS h "
2535 "LEFT JOIN dhcp4_options AS o "
2536 "ON h.host_id = o.host_id "
2537 "WHERE h.dhcp4_subnet_id = ? "
2538 "ORDER BY h.host_id, o.option_id"},
2539
2540 // Retrieves host information, IPv6 reservations and DHCPv6 options
2541 // associated with a host. The number of rows returned is a multiplication
2542 // of number of IPv6 reservations and DHCPv6 options. Hosts are retrieved
2543 // by IPv6 subnet id.
2545 "SELECT h.host_id, h.dhcp_identifier, "
2546 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2547 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2548 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2549 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2550 "h.dhcp4_boot_file_name, h.auth_key, "
2551 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2552 "o.persistent, o.cancelled, o.user_context, "
2553 "r.reservation_id, r.address, r.prefix_len, r.type, "
2554 "r.dhcp6_iaid "
2555 "FROM hosts AS h "
2556 "LEFT JOIN dhcp6_options AS o "
2557 "ON h.host_id = o.host_id "
2558 "LEFT JOIN ipv6_reservations AS r "
2559 "ON h.host_id = r.host_id "
2560 "WHERE h.dhcp6_subnet_id = ? "
2561 "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2562
2563 // Retrieves host information, IPv6 reservations and both DHCPv4 and
2564 // DHCPv6 options associated with the host. The LEFT JOIN clause is used
2565 // to retrieve information from 4 different tables using a single query.
2566 // Hence, this query returns multiple rows for a single host.
2568 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2569 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, "
2570 "h.hostname, h.dhcp4_client_classes, h.dhcp6_client_classes, "
2571 "h.user_context, "
2572 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2573 "h.dhcp4_boot_file_name, h.auth_key, "
2574 "o4.option_id, o4.code, o4.value, o4.formatted_value, o4.space, "
2575 "o4.persistent, o4.cancelled, o4.user_context, "
2576 "o6.option_id, o6.code, o6.value, o6.formatted_value, o6.space, "
2577 "o6.persistent, o6.cancelled, o6.user_context, "
2578 "r.reservation_id, r.address, r.prefix_len, r.type, "
2579 "r.dhcp6_iaid "
2580 "FROM hosts AS h "
2581 "LEFT JOIN dhcp4_options AS o4 "
2582 "ON h.host_id = o4.host_id "
2583 "LEFT JOIN dhcp6_options AS o6 "
2584 "ON h.host_id = o6.host_id "
2585 "LEFT JOIN ipv6_reservations AS r "
2586 "ON h.host_id = r.host_id "
2587 "WHERE h.hostname = ? "
2588 "ORDER BY h.host_id, o4.option_id, o6.option_id, r.reservation_id"},
2589
2590 // Retrieves host information and DHCPv4 options using hostname and
2591 // subnet identifier. Left joining the dhcp4_options table results in
2592 // multiple rows being returned for the same host.
2594 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2595 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2596 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2597 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2598 "h.dhcp4_boot_file_name, h.auth_key, "
2599 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2600 "o.persistent, o.cancelled, o.user_context "
2601 "FROM hosts AS h "
2602 "LEFT JOIN dhcp4_options AS o "
2603 "ON h.host_id = o.host_id "
2604 "WHERE h.hostname = ? AND h.dhcp4_subnet_id = ? "
2605 "ORDER BY h.host_id, o.option_id"},
2606
2607 // Retrieves host information, IPv6 reservations and DHCPv6 options
2608 // using hostname and subnet identifier. The number of rows returned
2609 // is a multiplication of number of IPv6 reservations and DHCPv6 options.
2611 "SELECT h.host_id, h.dhcp_identifier, "
2612 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2613 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2614 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2615 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2616 "h.dhcp4_boot_file_name, h.auth_key, "
2617 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2618 "o.persistent, o.cancelled, o.user_context, "
2619 "r.reservation_id, r.address, r.prefix_len, r.type, "
2620 "r.dhcp6_iaid "
2621 "FROM hosts AS h "
2622 "LEFT JOIN dhcp6_options AS o "
2623 "ON h.host_id = o.host_id "
2624 "LEFT JOIN ipv6_reservations AS r "
2625 "ON h.host_id = r.host_id "
2626 "WHERE h.hostname = ? AND h.dhcp6_subnet_id = ? "
2627 "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2628
2629 // Retrieves host information along with the DHCPv4 options associated with
2630 // it. Left joining the dhcp4_options table results in multiple rows being
2631 // returned for the same host. Hosts are retrieved by IPv4 subnet id
2632 // and with a host id greater than the start one.
2633 // The number of hosts returned is lower or equal to the limit.
2635 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2636 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2637 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2638 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2639 "h.dhcp4_boot_file_name, h.auth_key, "
2640 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2641 "o.persistent, o.cancelled, o.user_context "
2642 "FROM ( SELECT * FROM hosts AS h "
2643 "WHERE h.dhcp4_subnet_id = ? AND h.host_id > ? "
2644 "ORDER BY h.host_id "
2645 "LIMIT ? ) AS h "
2646 "LEFT JOIN dhcp4_options AS o "
2647 "ON h.host_id = o.host_id "
2648 "ORDER BY h.host_id, o.option_id"},
2649
2650 // Retrieves host information, IPv6 reservations and DHCPv6 options
2651 // associated with a host. The number of rows returned is a multiplication
2652 // of number of IPv6 reservations and DHCPv6 options. Hosts are retrieved
2653 // by IPv6 subnet id and with a host id greater than the start one.
2654 // The number of hosts returned is lower or equal to the limit.
2656 "SELECT h.host_id, h.dhcp_identifier, "
2657 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2658 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2659 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2660 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2661 "h.dhcp4_boot_file_name, h.auth_key, "
2662 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2663 "o.persistent, o.cancelled, o.user_context, "
2664 "r.reservation_id, r.address, r.prefix_len, r.type, "
2665 "r.dhcp6_iaid "
2666 "FROM ( SELECT * FROM hosts AS h "
2667 "WHERE h.dhcp6_subnet_id = ? AND h.host_id > ? "
2668 "ORDER BY h.host_id "
2669 "LIMIT ? ) AS h "
2670 "LEFT JOIN dhcp6_options AS o "
2671 "ON h.host_id = o.host_id "
2672 "LEFT JOIN ipv6_reservations AS r "
2673 "ON h.host_id = r.host_id "
2674 "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2675
2676 // Retrieves host information along with the DHCPv4 options associated with
2677 // it. Left joining the dhcp4_options table results in multiple rows being
2678 // returned for the same host. Hosts are retrieved with a host id greater
2679 // than the start one.
2680 // The number of hosts returned is lower or equal to the limit.
2682 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2683 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2684 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2685 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2686 "h.dhcp4_boot_file_name, h.auth_key, "
2687 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2688 "o.persistent, o.cancelled, o.user_context "
2689 "FROM ( SELECT * FROM hosts AS h "
2690 "WHERE h.host_id > ? "
2691 "ORDER BY h.host_id "
2692 "LIMIT ? ) AS h "
2693 "LEFT JOIN dhcp4_options AS o "
2694 "ON h.host_id = o.host_id "
2695 "ORDER BY h.host_id, o.option_id"},
2696
2697 // Retrieves host information, IPv6 reservations and DHCPv6 options
2698 // associated with a host. The number of rows returned is a multiplication
2699 // of number of IPv6 reservations and DHCPv6 options. Hosts are retrieved
2700 // with a host id greater than the start one.
2701 // The number of hosts returned is lower or equal to the limit.
2703 "SELECT h.host_id, h.dhcp_identifier, "
2704 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2705 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2706 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2707 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2708 "h.dhcp4_boot_file_name, h.auth_key, "
2709 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2710 "o.persistent, o.cancelled, o.user_context, "
2711 "r.reservation_id, r.address, r.prefix_len, r.type, "
2712 "r.dhcp6_iaid "
2713 "FROM ( SELECT * FROM hosts AS h "
2714 "WHERE h.host_id > ? "
2715 "ORDER BY h.host_id "
2716 "LIMIT ? ) AS h "
2717 "LEFT JOIN dhcp6_options AS o "
2718 "ON h.host_id = o.host_id "
2719 "LEFT JOIN ipv6_reservations AS r "
2720 "ON h.host_id = r.host_id "
2721 "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2722
2723 // Inserts a host into the 'hosts' table without checking that there is
2724 // a reservation for the IP address.
2726 "INSERT INTO hosts(host_id, dhcp_identifier, dhcp_identifier_type, "
2727 "dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
2728 "dhcp4_client_classes, dhcp6_client_classes, "
2729 "user_context, dhcp4_next_server, "
2730 "dhcp4_server_hostname, dhcp4_boot_file_name, auth_key) "
2731 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"},
2732
2733 // Inserts a host into the 'hosts' table with checking that reserved IP
2734 // address is unique. The innermost query checks if there is at least
2735 // one host for the given IP/subnet combination. For checking whether
2736 // hosts exists or not it doesn't matter if we select actual columns,
2737 // thus SELECT 1 was used as an optimization to avoid selecting data
2738 // that will be ignored anyway. If the host does not exist the new
2739 // host is inserted. DUAL is a special MySQL table from which we can
2740 // select the values to be inserted. If the host with the given IP
2741 // address already exists the new host won't be inserted. The caller
2742 // can check the number of affected rows to detect that there was
2743 // a duplicate host in the database.
2745 "INSERT INTO hosts(host_id, dhcp_identifier, dhcp_identifier_type, "
2746 "dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
2747 "dhcp4_client_classes, dhcp6_client_classes, "
2748 "user_context, dhcp4_next_server, "
2749 "dhcp4_server_hostname, dhcp4_boot_file_name, auth_key) "
2750 "SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? FROM DUAL "
2751 "WHERE NOT EXISTS ("
2752 "SELECT 1 FROM hosts "
2753 "WHERE ipv4_address = ? AND dhcp4_subnet_id = ? "
2754 "LIMIT 1"
2755 ")"},
2756
2757 // Inserts a single IPv6 reservation into 'reservations' table without
2758 // checking that the inserted reservation is unique.
2760 "INSERT INTO ipv6_reservations(address, prefix_len, type, "
2761 "dhcp6_iaid, host_id) "
2762 "VALUES (?, ?, ?, ?, ?)"},
2763
2764 // Inserts a single IPv6 reservation into 'reservations' table with
2765 // checking that the inserted reservation is unique.
2767 "INSERT INTO ipv6_reservations(address, prefix_len, type, "
2768 "dhcp6_iaid, host_id) "
2769 "SELECT ?, ?, ?, ?, ? FROM DUAL "
2770 "WHERE NOT EXISTS ("
2771 "SELECT 1 FROM ipv6_reservations "
2772 "WHERE address = ? AND prefix_len = ? "
2773 "LIMIT 1"
2774 ")"},
2775
2776 // Inserts a single DHCPv4 option into 'dhcp4_options' table.
2777 // Using fixed scope_id = 3, which associates an option with host.
2779 "INSERT INTO dhcp4_options(option_id, code, value, formatted_value, space, "
2780 "persistent, cancelled, user_context, dhcp4_subnet_id, host_id, scope_id) "
2781 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 3)"},
2782
2783 // Inserts a single DHCPv6 option into 'dhcp6_options' table.
2784 // Using fixed scope_id = 3, which associates an option with host.
2786 "INSERT INTO dhcp6_options(option_id, code, value, formatted_value, space, "
2787 "persistent, cancelled, user_context, dhcp6_subnet_id, host_id, scope_id) "
2788 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 3)"},
2789
2790 // Delete IPv4 reservations by subnet id and reserved address.
2792 "DELETE FROM hosts WHERE dhcp4_subnet_id = ? AND ipv4_address = ?"},
2793
2794 // Delete IPv6 reservations by subnet id and reserved address/prefix.
2796 "DELETE h FROM hosts AS h "
2797 "INNER JOIN ipv6_reservations AS r "
2798 "ON h.host_id = r.host_id "
2799 "WHERE h.dhcp6_subnet_id = ? AND r.address = ?"},
2800
2801 // Delete a single IPv4 reservation by subnet id and identifier.
2803 "DELETE FROM hosts WHERE dhcp4_subnet_id = ? AND dhcp_identifier_type=? "
2804 "AND dhcp_identifier = ?"},
2805
2806 // Delete a single IPv6 reservation by subnet id and identifier.
2808 "DELETE FROM hosts WHERE dhcp6_subnet_id = ? AND dhcp_identifier_type=? "
2809 "AND dhcp_identifier = ?"}
2810 }
2811};
2812
2813} // namespace
2814
2815// MySqlHostContext Constructor
2816
2818 IOServiceAccessorPtr io_service_accessor,
2819 db::DbCallback db_reconnect_callback)
2820 : conn_(parameters, io_service_accessor, db_reconnect_callback),
2821 is_readonly_(true) {
2822}
2823
2824// MySqlHostContextAlloc Constructor and Destructor
2825
2827 MySqlHostDataSourceImpl& mgr) : ctx_(), mgr_(mgr) {
2828
2829 if (MultiThreadingMgr::instance().getMode()) {
2830 // multi-threaded
2831 {
2832 // we need to protect the whole pool_ operation, hence extra scope {}
2833 lock_guard<mutex> lock(mgr_.pool_->mutex_);
2834 if (!mgr_.pool_->pool_.empty()) {
2835 ctx_ = mgr_.pool_->pool_.back();
2836 mgr_.pool_->pool_.pop_back();
2837 }
2838 }
2839 if (!ctx_) {
2840 ctx_ = mgr_.createContext();
2841 }
2842 } else {
2843 // single-threaded
2844 if (mgr_.pool_->pool_.empty()) {
2845 isc_throw(Unexpected, "No available MySQL host context?!");
2846 }
2847 ctx_ = mgr_.pool_->pool_.back();
2848 }
2849}
2850
2852 if (MultiThreadingMgr::instance().getMode()) {
2853 // multi-threaded
2854 lock_guard<mutex> lock(mgr_.pool_->mutex_);
2855 mgr_.pool_->pool_.push_back(ctx_);
2856 if (ctx_->conn_.isUnusable()) {
2857 mgr_.unusable_ = true;
2858 }
2859 } else if (ctx_->conn_.isUnusable()) {
2860 mgr_.unusable_ = true;
2861 }
2862}
2863
2865 : parameters_(parameters), ip_reservations_unique_(true), unusable_(false) {
2866
2867 // Create unique timer name per instance.
2868 timer_name_ = "MySqlHostMgr[";
2869 timer_name_ += boost::lexical_cast<std::string>(reinterpret_cast<uint64_t>(this));
2870 timer_name_ += "]DbReconnectTimer";
2871
2874 timer_name_);
2875
2876 // Create an initial context.
2877 pool_.reset(new MySqlHostContextPool());
2878 pool_->pool_.push_back(createContext());
2879}
2880
2881// Create context.
2882
2888
2889 // Open the database.
2890 ctx->conn_.openDatabase();
2891
2892 // Check if we have TLS when we required it.
2893 if (ctx->conn_.getTls()) {
2894 std::string cipher = ctx->conn_.getTlsCipher();
2895 if (cipher.empty()) {
2897 } else {
2900 .arg(cipher);
2901 }
2902 }
2903
2904 // Prepare query statements. Those are will be only used to retrieve
2905 // information from the database, so they can be used even if the
2906 // database is read only for the current user.
2907 ctx->conn_.prepareStatements(tagged_statements.begin(),
2908 tagged_statements.begin() + WRITE_STMTS_BEGIN);
2909
2910 // Check if the backend is explicitly configured to operate with
2911 // read only access to the database.
2912 ctx->is_readonly_ = ctx->conn_.configuredReadOnly();
2913
2914 // If we are using read-write mode for the database we also prepare
2915 // statements for INSERTS etc.
2916 if (!ctx->is_readonly_) {
2917 // Prepare statements for writing to the database, e.g. INSERT.
2918 ctx->conn_.prepareStatements(tagged_statements.begin() + WRITE_STMTS_BEGIN,
2919 tagged_statements.end());
2920 } else {
2922 }
2923
2924 // Create the exchange objects for use in exchanging data between the
2925 // program and the database.
2926 ctx->host_ipv4_exchange_.reset(new MySqlHostWithOptionsExchange(MySqlHostWithOptionsExchange::DHCP4_ONLY));
2927 ctx->host_ipv6_exchange_.reset(new MySqlHostIPv6Exchange(MySqlHostWithOptionsExchange::DHCP6_ONLY));
2928 ctx->host_ipv46_exchange_.reset(new MySqlHostIPv6Exchange(MySqlHostWithOptionsExchange::DHCP4_AND_DHCP6));
2929 ctx->host_ipv6_reservation_exchange_.reset(new MySqlIPv6ReservationExchange());
2930 ctx->host_option_exchange_.reset(new MySqlOptionExchange());
2931
2932 // Create ReconnectCtl for this connection.
2933 ctx->conn_.makeReconnectCtl(timer_name_, NetworkState::DB_CONNECTION + 11);
2934
2935 return (ctx);
2936}
2937
2940
2941bool
2944
2945 // Invoke application layer connection lost callback.
2946 if (!DatabaseConnection::invokeDbLostCallback(db_reconnect_ctl)) {
2947 return (false);
2948 }
2949
2950 bool reopened = false;
2951
2952 const std::string timer_name = db_reconnect_ctl->timerName();
2953 bool check = db_reconnect_ctl->checkRetries();
2954
2955 // At least one connection was lost.
2956 try {
2957 CfgDbAccessPtr cfg_db = CfgMgr::instance().getCurrentCfg()->getCfgDbAccess();
2958 std::list<std::string> host_db_access_list = cfg_db->getHostDbAccessStringList();
2959 for (std::string& hds : host_db_access_list) {
2960 auto parameters = DatabaseConnection::parse(hds);
2961 if (HostMgr::delBackend("mysql", hds, true)) {
2963 }
2964 }
2965 reopened = true;
2966 } catch (const std::exception& ex) {
2968 .arg(ex.what());
2969 }
2970
2971 if (reopened) {
2972 // Cancel the timer.
2973 if (TimerMgr::instance()->isTimerRegistered(timer_name)) {
2974 TimerMgr::instance()->unregisterTimer(timer_name);
2975 }
2976
2977 // Invoke application layer connection recovered callback.
2978 if (!DatabaseConnection::invokeDbRecoveredCallback(db_reconnect_ctl)) {
2979 return (false);
2980 }
2981 } else {
2982 if (!check) {
2983 // We're out of retries, log it and initiate shutdown.
2985 .arg(db_reconnect_ctl->maxRetries());
2986
2987 // Cancel the timer.
2988 if (TimerMgr::instance()->isTimerRegistered(timer_name)) {
2989 TimerMgr::instance()->unregisterTimer(timer_name);
2990 }
2991
2992 // Invoke application layer connection failed callback.
2994 return (false);
2995 }
2996
2998 .arg(db_reconnect_ctl->maxRetries() - db_reconnect_ctl->retriesLeft() + 1)
2999 .arg(db_reconnect_ctl->maxRetries())
3000 .arg(db_reconnect_ctl->retryInterval());
3001
3002 // Start the timer.
3003 if (!TimerMgr::instance()->isTimerRegistered(timer_name)) {
3004 TimerMgr::instance()->registerTimer(timer_name,
3005 std::bind(&MySqlHostDataSourceImpl::dbReconnect, db_reconnect_ctl),
3006 db_reconnect_ctl->retryInterval(),
3008 }
3009 TimerMgr::instance()->setup(timer_name);
3010 }
3011
3012 return (true);
3013}
3014
3015std::pair<uint32_t, uint32_t>
3024
3025void
3027 StatementIndex stindex,
3028 std::vector<MYSQL_BIND>& bind) {
3029 // Bind the parameters to the statement
3030 int status = mysql_stmt_bind_param(ctx->conn_.getStatement(stindex), &bind[0]);
3031 checkError(ctx, status, stindex, "unable to bind parameters");
3032
3033 // Execute the statement
3034 status = MysqlExecuteStatement(ctx->conn_.getStatement(stindex));
3035
3036 if (status != 0) {
3037 // Failure: check for the special case of duplicate entry.
3038 if (mysql_errno(ctx->conn_.mysql_) == ER_DUP_ENTRY) {
3039 isc_throw(DuplicateEntry, "Database duplicate entry error");
3040 }
3041 checkError(ctx, status, stindex, "unable to execute");
3042 }
3043
3044 // If the number of rows inserted is 0 it means that the query detected
3045 // an attempt to insert duplicated data for which there is no unique
3046 // index in the database. Unique indexes are not created in the database
3047 // when it may be sometimes allowed to insert duplicated records per
3048 // server's configuration.
3049 my_ulonglong numrows = mysql_stmt_affected_rows(ctx->conn_.getStatement(stindex));
3050 if (numrows == 0) {
3051 isc_throw(DuplicateEntry, "Database duplicate entry error");
3052 }
3053}
3054
3055bool
3057 StatementIndex stindex,
3058 MYSQL_BIND* bind) {
3059 // Bind the parameters to the statement
3060 int status = mysql_stmt_bind_param(ctx->conn_.getStatement(stindex), &bind[0]);
3061 checkError(ctx, status, stindex, "unable to bind parameters");
3062
3063 // Execute the statement
3064 status = MysqlExecuteStatement(ctx->conn_.getStatement(stindex));
3065
3066 if (status != 0) {
3067 checkError(ctx, status, stindex, "unable to execute");
3068 }
3069
3070 // Let's check how many hosts were deleted.
3071 my_ulonglong numrows = mysql_stmt_affected_rows(ctx->conn_.getStatement(stindex));
3072
3073 return (numrows != 0);
3074}
3075
3076void
3078 const IPv6Resrv& resv,
3079 const HostID& id) {
3080 std::vector<MYSQL_BIND> bind = ctx->host_ipv6_reservation_exchange_->
3081 createBindForSend(resv, id, ip_reservations_unique_);
3082
3084}
3085
3086void
3088 const StatementIndex& stindex,
3089 const OptionDescriptor& opt_desc,
3090 const std::string& opt_space,
3091 const Optional<SubnetID>& subnet_id,
3092 const HostID& id) {
3093 std::vector<MYSQL_BIND> bind = ctx->host_option_exchange_->createBindForSend(opt_desc, opt_space, subnet_id, id);
3094
3095 addStatement(ctx, stindex, bind);
3096}
3097
3098void
3100 const StatementIndex& stindex,
3101 const ConstCfgOptionPtr& options_cfg,
3102 const uint64_t host_id) {
3103 // Get option space names and vendor space names and combine them within a
3104 // single list.
3105 std::list<std::string> option_spaces = options_cfg->getOptionSpaceNames();
3106 std::list<std::string> vendor_spaces = options_cfg->getVendorIdsSpaceNames();
3107 option_spaces.insert(option_spaces.end(), vendor_spaces.begin(),
3108 vendor_spaces.end());
3109
3110 // For each option space retrieve all options and insert them into the
3111 // database.
3112 for (auto const& space : option_spaces) {
3113 OptionContainerPtr options = options_cfg->getAllCombined(space);
3114 if (options && !options->empty()) {
3115 for (auto const& opt : *options) {
3116 addOption(ctx, stindex, opt, space, Optional<SubnetID>(), host_id);
3117 }
3118 }
3119 }
3120}
3121
3122void
3124 const int status,
3125 const StatementIndex index,
3126 const char* what) const {
3127 ctx->conn_.checkError(status, index, what);
3128}
3129
3130void
3132 StatementIndex stindex,
3133 MYSQL_BIND* bind,
3134 boost::shared_ptr<MySqlHostExchange> exchange,
3136 bool single) const {
3137
3138 // Bind the selection parameters to the statement
3139 int status = mysql_stmt_bind_param(ctx->conn_.getStatement(stindex), bind);
3140 checkError(ctx, status, stindex, "unable to bind WHERE clause parameter");
3141
3142 // Set up the MYSQL_BIND array for the data being returned and bind it to
3143 // the statement.
3144 std::vector<MYSQL_BIND> outbind = exchange->createBindForReceive();
3145 status = mysql_stmt_bind_result(ctx->conn_.getStatement(stindex), &outbind[0]);
3146 checkError(ctx, status, stindex, "unable to bind SELECT clause parameters");
3147
3148 // Execute the statement
3149 status = MysqlExecuteStatement(ctx->conn_.getStatement(stindex));
3150 checkError(ctx, status, stindex, "unable to execute");
3151
3152 // Ensure that all the lease information is retrieved in one go to avoid
3153 // overhead of going back and forth between client and server.
3154 status = mysql_stmt_store_result(ctx->conn_.getStatement(stindex));
3155 checkError(ctx, status, stindex, "unable to set up for storing all results");
3156
3157 // Set up the fetch "release" object to release resources associated
3158 // with the call to mysql_stmt_fetch when this method exits, then
3159 // retrieve the data. mysql_stmt_fetch return value equal to 0 represents
3160 // successful data fetch.
3161 MySqlFreeResult fetch_release(ctx->conn_.getStatement(stindex));
3162 while ((status = mysql_stmt_fetch(ctx->conn_.getStatement(stindex))) ==
3164 try {
3165 exchange->processFetchedData(result);
3166
3167 } catch (const isc::BadValue& ex) {
3168 // Rethrow the exception with a bit more data.
3169 isc_throw(BadValue, ex.what() << ". Statement is <" <<
3170 ctx->conn_.text_statements_[stindex] << ">");
3171 }
3172
3173 if (single && (result.size() > 1)) {
3174 isc_throw(MultipleRecords, "multiple records were found in the "
3175 "database where only one was expected for query "
3176 << ctx->conn_.text_statements_[stindex]);
3177 }
3178 }
3179
3180 // How did the fetch end?
3181 // If mysql_stmt_fetch return value is equal to 1 an error occurred.
3182 if (status == MLM_MYSQL_FETCH_FAILURE) {
3183 // Error - unable to fetch results
3184 checkError(ctx, status, stindex, "unable to fetch results");
3185
3186 } else if (status == MYSQL_DATA_TRUNCATED) {
3187 // Data truncated - throw an exception indicating what was at fault
3188 isc_throw(DataTruncated, ctx->conn_.text_statements_[stindex]
3189 << " returned truncated data: columns affected are "
3190 << exchange->getErrorColumns());
3191 }
3192}
3193
3196 const SubnetID& subnet_id,
3197 const Host::IdentifierType& identifier_type,
3198 const uint8_t* identifier_begin,
3199 const size_t identifier_len,
3200 StatementIndex stindex,
3201 boost::shared_ptr<MySqlHostExchange> exchange) const {
3202
3203 // Set up the WHERE clause value
3204 MYSQL_BIND inbind[3];
3205 memset(inbind, 0, sizeof(inbind));
3206
3207 uint32_t subnet_buffer = static_cast<uint32_t>(subnet_id);
3208 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3209 inbind[0].buffer = reinterpret_cast<char*>(&subnet_buffer);
3210 inbind[0].is_unsigned = MLM_TRUE;
3211
3212 // Identifier value.
3213 std::vector<char> identifier_vec(identifier_begin,
3214 identifier_begin + identifier_len);
3215 unsigned long length = identifier_vec.size();
3216 inbind[2].buffer_type = MYSQL_TYPE_BLOB;
3217 inbind[2].buffer = &identifier_vec[0];
3218 inbind[2].buffer_length = length;
3219 inbind[2].length = &length;
3220
3221 // Identifier type.
3222 char identifier_type_copy = static_cast<char>(identifier_type);
3223 inbind[1].buffer_type = MYSQL_TYPE_TINY;
3224 inbind[1].buffer = &identifier_type_copy;
3225 inbind[1].is_unsigned = MLM_TRUE;
3226
3227 ConstHostCollection collection;
3228 getHostCollection(ctx, stindex, inbind, exchange, collection, true);
3229
3230 // Return single record if present, else clear the host.
3232 if (!collection.empty()) {
3233 result = *collection.begin();
3234 }
3235
3236 return (result);
3237}
3238
3239void
3241 if (ctx->is_readonly_) {
3242 isc_throw(ReadOnlyDb, "MySQL host database backend is configured to"
3243 " operate in read only mode");
3244 }
3245}
3246
3250
3253
3256 return (impl_->parameters_);
3257}
3258
3259void
3261 // Get a context
3262 MySqlHostContextAlloc get_context(*impl_);
3263 MySqlHostContextPtr ctx = get_context.ctx_;
3264
3265 // If operating in read-only mode, throw exception.
3266 impl_->checkReadOnly(ctx);
3267
3268 // Initiate MySQL transaction as we will have to make multiple queries
3269 // to insert host information into multiple tables. If that fails on
3270 // any stage, the transaction will be rolled back by the destructor of
3271 // the MySqlTransaction class.
3272 MySqlTransaction transaction(ctx->conn_);
3273
3274 // If we're configured to check that an IP reservation within a given subnet
3275 // is unique, the IP reservation exists and the subnet is actually set
3276 // we will be using a special query that checks for uniqueness. Otherwise,
3277 // we will use a regular insert statement.
3278 bool unique_ip = impl_->ip_reservations_unique_ && !host->getIPv4Reservation().isV4Zero()
3279 && host->getIPv4SubnetID() != SUBNET_ID_UNUSED;
3280
3281 // Create the MYSQL_BIND array for the host
3282 std::vector<MYSQL_BIND> bind = ctx->host_ipv4_exchange_->createBindForSend(host, unique_ip);
3283
3284 // ... and insert the host.
3285 impl_->addStatement(ctx, unique_ip ? MySqlHostDataSourceImpl::INSERT_HOST_UNIQUE_IP :
3287
3288 // Gets the last inserted hosts id
3289 uint64_t host_id = mysql_insert_id(ctx->conn_.mysql_);
3290
3291 // Insert DHCPv4 options.
3292 ConstCfgOptionPtr cfg_option4 = host->getCfgOption4();
3293 if (cfg_option4) {
3295 cfg_option4, host_id);
3296 }
3297
3298 // Insert DHCPv6 options.
3299 ConstCfgOptionPtr cfg_option6 = host->getCfgOption6();
3300 if (cfg_option6) {
3302 cfg_option6, host_id);
3303 }
3304
3305 // Insert IPv6 reservations.
3306 IPv6ResrvRange v6resv = host->getIPv6Reservations();
3307 if (std::distance(v6resv.first, v6resv.second) > 0) {
3308 BOOST_FOREACH(auto const& resv, v6resv) {
3309 impl_->addResv(ctx, resv.second, host_id);
3310 }
3311 }
3312
3313 // Everything went fine, so explicitly commit the transaction.
3314 transaction.commit();
3315}
3316
3317bool
3319 const asiolink::IOAddress& addr) {
3320 // Get a context
3321 MySqlHostContextAlloc get_context(*impl_);
3322 MySqlHostContextPtr ctx = get_context.ctx_;
3323
3324 // If operating in read-only mode, throw exception.
3325 impl_->checkReadOnly(ctx);
3326
3327 // Set up the WHERE clause value
3328 MYSQL_BIND inbind[2];
3329
3330 uint32_t subnet = subnet_id;
3331 memset(inbind, 0, sizeof(inbind));
3332 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3333 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3334 inbind[0].is_unsigned = MLM_TRUE;
3335
3336 // v4
3337 if (addr.isV4()) {
3338 uint32_t addr4 = addr.toUint32();
3339 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3340 inbind[1].buffer = reinterpret_cast<char*>(&addr4);
3341 inbind[1].is_unsigned = MLM_TRUE;
3342
3343 return (impl_->delStatement(ctx, MySqlHostDataSourceImpl::DEL_HOST_ADDR4, inbind));
3344 }
3345
3346 // v6
3347 std::vector<uint8_t>addr6 = addr.toBytes();
3348 if (addr6.size() != isc::asiolink::V6ADDRESS_LEN) {
3349 isc_throw(DbOperationError, "del() - address is not "
3350 << isc::asiolink::V6ADDRESS_LEN << " bytes long");
3351 }
3352
3353 unsigned long addr6_length = isc::asiolink::V6ADDRESS_LEN;
3354 inbind[1].buffer_type = MYSQL_TYPE_BLOB;
3355 inbind[1].buffer = reinterpret_cast<char*>(&addr6[0]);
3356 inbind[1].buffer_length = isc::asiolink::V6ADDRESS_LEN;
3357 inbind[1].length = &addr6_length;
3358
3359 return (impl_->delStatement(ctx, MySqlHostDataSourceImpl::DEL_HOST_ADDR6, inbind));
3360}
3361
3362bool
3364 const Host::IdentifierType& identifier_type,
3365 const uint8_t* identifier_begin,
3366 const size_t identifier_len) {
3367 // Get a context
3368 MySqlHostContextAlloc get_context(*impl_);
3369 MySqlHostContextPtr ctx = get_context.ctx_;
3370
3371 // If operating in read-only mode, throw exception.
3372 impl_->checkReadOnly(ctx);
3373
3374 // Set up the WHERE clause value
3375 MYSQL_BIND inbind[3];
3376
3377 // subnet-id
3378 memset(inbind, 0, sizeof(inbind));
3379 uint32_t subnet = static_cast<uint32_t>(subnet_id);
3380 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3381 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3382 inbind[0].is_unsigned = MLM_TRUE;
3383
3384 // identifier type
3385 char identifier_type_copy = static_cast<char>(identifier_type);
3386 inbind[1].buffer_type = MYSQL_TYPE_TINY;
3387 inbind[1].buffer = &identifier_type_copy;
3388 inbind[1].is_unsigned = MLM_TRUE;
3389
3390 // identifier value
3391 std::vector<char> identifier_vec(identifier_begin,
3392 identifier_begin + identifier_len);
3393 unsigned long length = identifier_vec.size();
3394 inbind[2].buffer_type = MYSQL_TYPE_BLOB;
3395 inbind[2].buffer = &identifier_vec[0];
3396 inbind[2].buffer_length = length;
3397 inbind[2].length = &length;
3398
3399 ConstHostCollection collection;
3400 return (impl_->delStatement(ctx, MySqlHostDataSourceImpl::DEL_HOST_SUBID4_ID, inbind));
3401}
3402
3403bool
3405 const Host::IdentifierType& identifier_type,
3406 const uint8_t* identifier_begin,
3407 const size_t identifier_len) {
3408 // Get a context
3409 MySqlHostContextAlloc get_context(*impl_);
3410 MySqlHostContextPtr ctx = get_context.ctx_;
3411
3412 // If operating in read-only mode, throw exception.
3413 impl_->checkReadOnly(ctx);
3414
3415 // Set up the WHERE clause value
3416 MYSQL_BIND inbind[3];
3417
3418 // subnet-id
3419 memset(inbind, 0, sizeof(inbind));
3420 uint32_t subnet = static_cast<uint32_t>(subnet_id);
3421 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3422 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3423 inbind[0].is_unsigned = MLM_TRUE;
3424
3425 // identifier type
3426 char identifier_type_copy = static_cast<char>(identifier_type);
3427 inbind[1].buffer_type = MYSQL_TYPE_TINY;
3428 inbind[1].buffer = &identifier_type_copy;
3429 inbind[1].is_unsigned = MLM_TRUE;
3430
3431 // identifier value
3432 std::vector<char> identifier_vec(identifier_begin,
3433 identifier_begin + identifier_len);
3434 unsigned long length = identifier_vec.size();
3435 inbind[2].buffer_type = MYSQL_TYPE_BLOB;
3436 inbind[2].buffer = &identifier_vec[0];
3437 inbind[2].buffer_length = length;
3438 inbind[2].length = &length;
3439
3440 ConstHostCollection collection;
3441 return (impl_->delStatement(ctx, MySqlHostDataSourceImpl::DEL_HOST_SUBID6_ID, inbind));
3442}
3443
3446 const uint8_t* identifier_begin,
3447 const size_t identifier_len) const {
3448 // Get a context
3449 MySqlHostContextAlloc get_context(*impl_);
3450 MySqlHostContextPtr ctx = get_context.ctx_;
3451
3452 // Set up the WHERE clause value
3453 MYSQL_BIND inbind[2];
3454 memset(inbind, 0, sizeof(inbind));
3455
3456 // Identifier type.
3457 char identifier_type_copy = static_cast<char>(identifier_type);
3458 inbind[1].buffer = &identifier_type_copy;
3459 inbind[1].buffer_type = MYSQL_TYPE_TINY;
3460 inbind[1].is_unsigned = MLM_TRUE;
3461
3462 // Identifier value.
3463 std::vector<char> identifier_vec(identifier_begin,
3464 identifier_begin + identifier_len);
3465 unsigned long int length = identifier_vec.size();
3466 inbind[0].buffer_type = MYSQL_TYPE_BLOB;
3467 inbind[0].buffer = &identifier_vec[0];
3468 inbind[0].buffer_length = length;
3469 inbind[0].length = &length;
3470
3472 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_DHCPID, inbind,
3473 ctx->host_ipv46_exchange_, result, false);
3474
3475 return (result);
3476}
3477
3480 // Get a context
3481 MySqlHostContextAlloc get_context(*impl_);
3482 MySqlHostContextPtr ctx = get_context.ctx_;
3483
3484 // Set up the WHERE clause value
3485 MYSQL_BIND inbind[1];
3486 memset(inbind, 0, sizeof(inbind));
3487 uint32_t subnet = subnet_id;
3488 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3489 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3490 inbind[0].is_unsigned = MLM_TRUE;
3491
3493 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID4, inbind,
3494 ctx->host_ipv4_exchange_, result, false);
3495
3496 return (result);
3497}
3498
3501 // Get a context
3502 MySqlHostContextAlloc get_context(*impl_);
3503 MySqlHostContextPtr ctx = get_context.ctx_;
3504
3505 // Set up the WHERE clause value
3506 MYSQL_BIND inbind[1];
3507 memset(inbind, 0, sizeof(inbind));
3508 uint32_t subnet = subnet_id;
3509 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3510 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3511 inbind[0].is_unsigned = MLM_TRUE;
3512
3514 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID6, inbind,
3515 ctx->host_ipv6_exchange_, result, false);
3516
3517 return (result);
3518}
3519
3521MySqlHostDataSource::getAllbyHostname(const std::string& hostname) const {
3522 // Get a context
3523 MySqlHostContextAlloc get_context(*impl_);
3524 MySqlHostContextPtr ctx = get_context.ctx_;
3525
3526 // Set up the WHERE clause value
3527 MYSQL_BIND inbind[1];
3528 memset(inbind, 0, sizeof(inbind));
3529
3530 // Hostname
3531 char hostname_[HOSTNAME_MAX_LEN];
3532 strncpy(hostname_, hostname.c_str(), HOSTNAME_MAX_LEN - 1);
3533 unsigned long length = hostname.length();
3534 inbind[0].buffer_type = MYSQL_TYPE_STRING;
3535 inbind[0].buffer = reinterpret_cast<char*>(hostname_);
3536 inbind[0].buffer_length = length;
3537 inbind[0].length = &length;
3538
3540 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_HOSTNAME, inbind,
3541 ctx->host_ipv46_exchange_, result, false);
3542
3543 return (result);
3544}
3545
3547MySqlHostDataSource::getAllbyHostname4(const std::string& hostname,
3548 const SubnetID& subnet_id) const {
3549 // Get a context
3550 MySqlHostContextAlloc get_context(*impl_);
3551 MySqlHostContextPtr ctx = get_context.ctx_;
3552
3553 // Set up the WHERE clause value
3554 MYSQL_BIND inbind[2];
3555 memset(inbind, 0, sizeof(inbind));
3556
3557 // Hostname
3558 char hostname_[HOSTNAME_MAX_LEN];
3559 strncpy(hostname_, hostname.c_str(), HOSTNAME_MAX_LEN - 1);
3560 unsigned long length = hostname.length();
3561 inbind[0].buffer_type = MYSQL_TYPE_STRING;
3562 inbind[0].buffer = reinterpret_cast<char*>(hostname_);
3563 inbind[0].buffer_length = length;
3564 inbind[0].length = &length;
3565
3566 // Subnet ID
3567 uint32_t subnet = subnet_id;
3568 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3569 inbind[1].buffer = reinterpret_cast<char*>(&subnet);
3570 inbind[1].is_unsigned = MLM_TRUE;
3571
3573 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_HOSTNAME_SUBID4, inbind,
3574 ctx->host_ipv4_exchange_, result, false);
3575
3576 return (result);
3577}
3578
3580MySqlHostDataSource::getAllbyHostname6(const std::string& hostname,
3581 const SubnetID& subnet_id) const {
3582 // Get a context
3583 MySqlHostContextAlloc get_context(*impl_);
3584 MySqlHostContextPtr ctx = get_context.ctx_;
3585
3586 // Set up the WHERE clause value
3587 MYSQL_BIND inbind[2];
3588 memset(inbind, 0, sizeof(inbind));
3589
3590 // Hostname
3591 char hostname_[HOSTNAME_MAX_LEN];
3592 strncpy(hostname_, hostname.c_str(), HOSTNAME_MAX_LEN - 1);
3593 unsigned long length = hostname.length();
3594 inbind[0].buffer_type = MYSQL_TYPE_STRING;
3595 inbind[0].buffer = reinterpret_cast<char*>(hostname_);
3596 inbind[0].buffer_length = length;
3597 inbind[0].length = &length;
3598
3599 // Subnet ID
3600 uint32_t subnet = subnet_id;
3601 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3602 inbind[1].buffer = reinterpret_cast<char*>(&subnet);
3603 inbind[1].is_unsigned = MLM_TRUE;
3604
3606 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_HOSTNAME_SUBID6, inbind,
3607 ctx->host_ipv6_exchange_, result, false);
3608
3609 return (result);
3610}
3611
3614 size_t& /*source_index*/,
3615 uint64_t lower_host_id,
3616 const HostPageSize& page_size) const {
3617 // Get a context
3618 MySqlHostContextAlloc get_context(*impl_);
3619 MySqlHostContextPtr ctx = get_context.ctx_;
3620
3621 // Set up the WHERE clause value
3622 MYSQL_BIND inbind[3];
3623 memset(inbind, 0, sizeof(inbind));
3624
3625 // Bind subnet id
3626 uint32_t subnet = subnet_id;
3627 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3628 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3629 inbind[0].is_unsigned = MLM_TRUE;
3630
3631 // Bind lower host id
3632 uint32_t host_id = lower_host_id;
3633 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3634 inbind[1].buffer = reinterpret_cast<char*>(&host_id);
3635 inbind[1].is_unsigned = MLM_TRUE;
3636
3637 // Bind page size value
3638 uint32_t page_size_data = page_size.page_size_;
3639 inbind[2].buffer_type = MYSQL_TYPE_LONG;
3640 inbind[2].buffer = reinterpret_cast<char*>(&page_size_data);
3641 inbind[2].is_unsigned = MLM_TRUE;
3642
3644 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID4_PAGE, inbind,
3645 ctx->host_ipv4_exchange_, result, false);
3646
3647 return (result);
3648}
3649
3652 size_t& /*source_index*/,
3653 uint64_t lower_host_id,
3654 const HostPageSize& page_size) const {
3655 // Get a context
3656 MySqlHostContextAlloc get_context(*impl_);
3657 MySqlHostContextPtr ctx = get_context.ctx_;
3658
3659 // Set up the WHERE clause value
3660 MYSQL_BIND inbind[3];
3661 memset(inbind, 0, sizeof(inbind));
3662
3663 // Bind subnet id
3664 uint32_t subnet = subnet_id;
3665 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3666 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3667 inbind[0].is_unsigned = MLM_TRUE;
3668
3669 // Bind lower host id
3670 uint32_t host_id = lower_host_id;
3671 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3672 inbind[1].buffer = reinterpret_cast<char*>(&host_id);
3673 inbind[1].is_unsigned = MLM_TRUE;
3674
3675 // Bind page size value
3676 uint32_t page_size_data = page_size.page_size_;
3677 inbind[2].buffer_type = MYSQL_TYPE_LONG;
3678 inbind[2].buffer = reinterpret_cast<char*>(&page_size_data);
3679 inbind[2].is_unsigned = MLM_TRUE;
3680
3682 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID6_PAGE, inbind,
3683 ctx->host_ipv6_exchange_, result, false);
3684
3685 return (result);
3686}
3687
3689MySqlHostDataSource::getPage4(size_t& /*source_index*/,
3690 uint64_t lower_host_id,
3691 const HostPageSize& page_size) const {
3692 // Get a context
3693 MySqlHostContextAlloc get_context(*impl_);
3694 MySqlHostContextPtr ctx = get_context.ctx_;
3695
3696 // Set up the WHERE clause value
3697 MYSQL_BIND inbind[2];
3698 memset(inbind, 0, sizeof(inbind));
3699
3700 // Bind lower host id
3701 uint32_t host_id = lower_host_id;
3702 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3703 inbind[0].buffer = reinterpret_cast<char*>(&host_id);
3704 inbind[0].is_unsigned = MLM_TRUE;
3705
3706 // Bind page size value
3707 uint32_t page_size_data = page_size.page_size_;
3708 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3709 inbind[1].buffer = reinterpret_cast<char*>(&page_size_data);
3710 inbind[1].is_unsigned = MLM_TRUE;
3711
3713 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_PAGE4, inbind,
3714 ctx->host_ipv4_exchange_, result, false);
3715
3716 return (result);
3717}
3718
3720MySqlHostDataSource::getPage6(size_t& /*source_index*/,
3721 uint64_t lower_host_id,
3722 const HostPageSize& page_size) const {
3723 // Get a context
3724 MySqlHostContextAlloc get_context(*impl_);
3725 MySqlHostContextPtr ctx = get_context.ctx_;
3726
3727 // Set up the WHERE clause value
3728 MYSQL_BIND inbind[2];
3729 memset(inbind, 0, sizeof(inbind));
3730
3731 // Bind lower host id
3732 uint32_t host_id = lower_host_id;
3733 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3734 inbind[0].buffer = reinterpret_cast<char*>(&host_id);
3735 inbind[0].is_unsigned = MLM_TRUE;
3736
3737 // Bind page size value
3738 uint32_t page_size_data = page_size.page_size_;
3739 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3740 inbind[1].buffer = reinterpret_cast<char*>(&page_size_data);
3741 inbind[1].is_unsigned = MLM_TRUE;
3742
3744 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_PAGE6, inbind,
3745 ctx->host_ipv6_exchange_, result, false);
3746
3747 return (result);
3748}
3749
3752 // Get a context
3753 MySqlHostContextAlloc get_context(*impl_);
3754 MySqlHostContextPtr ctx = get_context.ctx_;
3755
3756 // Set up the WHERE clause value
3757 MYSQL_BIND inbind[1];
3758 memset(inbind, 0, sizeof(inbind));
3759
3760 uint32_t addr4 = address.toUint32();
3761 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3762 inbind[0].buffer = reinterpret_cast<char*>(&addr4);
3763 inbind[0].is_unsigned = MLM_TRUE;
3764
3766 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_ADDR, inbind,
3767 ctx->host_ipv4_exchange_, result, false);
3768
3769 return (result);
3770}
3771
3774 const Host::IdentifierType& identifier_type,
3775 const uint8_t* identifier_begin,
3776 const size_t identifier_len) const {
3777 // Get a context
3778 MySqlHostContextAlloc get_context(*impl_);
3779 MySqlHostContextPtr ctx = get_context.ctx_;
3780
3781 return (impl_->getHost(ctx, subnet_id, identifier_type, identifier_begin, identifier_len,
3783 ctx->host_ipv4_exchange_));
3784}
3785
3788 const asiolink::IOAddress& address) const {
3789 if (!address.isV4()) {
3790 isc_throw(BadValue, "MySqlHostDataSource::get4(id, address): "
3791 "wrong address type, address supplied is an IPv6 address");
3792 }
3793
3794 // Get a context
3795 MySqlHostContextAlloc get_context(*impl_);
3796 MySqlHostContextPtr ctx = get_context.ctx_;
3797
3798 // Set up the WHERE clause value
3799 MYSQL_BIND inbind[2];
3800 uint32_t subnet = subnet_id;
3801 memset(inbind, 0, sizeof(inbind));
3802 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3803 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3804 inbind[0].is_unsigned = MLM_TRUE;
3805
3806 uint32_t addr4 = address.toUint32();
3807 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3808 inbind[1].buffer = reinterpret_cast<char*>(&addr4);
3809 inbind[1].is_unsigned = MLM_TRUE;
3810
3811 ConstHostCollection collection;
3812 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID_ADDR, inbind,
3813 ctx->host_ipv4_exchange_, collection, true);
3814
3815 // Return single record if present, else clear the host.
3817 if (!collection.empty()) {
3818 result = *collection.begin();
3819 }
3820
3821 return (result);
3822}
3823
3826 const asiolink::IOAddress& address) const {
3827 if (!address.isV4()) {
3828 isc_throw(BadValue, "MySqlHostDataSource::getAll4(id, address): "
3829 "wrong address type, address supplied is an IPv6 address");
3830 }
3831
3832 // Get a context
3833 MySqlHostContextAlloc get_context(*impl_);
3834 MySqlHostContextPtr ctx = get_context.ctx_;
3835
3836 // Set up the WHERE clause value
3837 MYSQL_BIND inbind[2];
3838 uint32_t subnet = subnet_id;
3839 memset(inbind, 0, sizeof(inbind));
3840 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3841 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3842 inbind[0].is_unsigned = MLM_TRUE;
3843
3844 uint32_t addr4 = address.toUint32();
3845 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3846 inbind[1].buffer = reinterpret_cast<char*>(&addr4);
3847 inbind[1].is_unsigned = MLM_TRUE;
3848
3849 ConstHostCollection collection;
3850 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID_ADDR, inbind,
3851 ctx->host_ipv4_exchange_, collection, false);
3852 return (collection);
3853}
3854
3857 const Host::IdentifierType& identifier_type,
3858 const uint8_t* identifier_begin,
3859 const size_t identifier_len) const {
3860 // Get a context
3861 MySqlHostContextAlloc get_context(*impl_);
3862 MySqlHostContextPtr ctx = get_context.ctx_;
3863
3864 return (impl_->getHost(ctx, subnet_id, identifier_type, identifier_begin, identifier_len,
3866 ctx->host_ipv6_exchange_));
3867}
3868
3871 const uint8_t prefix_len) const {
3872 if (!prefix.isV6()) {
3873 isc_throw(BadValue, "MySqlHostDataSource::get6(prefix, prefix_len): "
3874 "wrong address type, address supplied is an IPv4 address");
3875 }
3876
3877 // Get a context
3878 MySqlHostContextAlloc get_context(*impl_);
3879 MySqlHostContextPtr ctx = get_context.ctx_;
3880
3881 // Set up the WHERE clause value
3882 MYSQL_BIND inbind[2];
3883 memset(inbind, 0, sizeof(inbind));
3884
3885 std::vector<uint8_t>addr6 = prefix.toBytes();
3886 if (addr6.size() != isc::asiolink::V6ADDRESS_LEN) {
3887 isc_throw(DbOperationError, "get6() - prefix is not "
3888 << isc::asiolink::V6ADDRESS_LEN << " bytes long");
3889 }
3890
3891 unsigned long addr6_length = isc::asiolink::V6ADDRESS_LEN;
3892 inbind[0].buffer_type = MYSQL_TYPE_BLOB;
3893 inbind[0].buffer = reinterpret_cast<char*>(&addr6[0]);
3894 inbind[0].buffer_length = isc::asiolink::V6ADDRESS_LEN;
3895 inbind[0].length = &addr6_length;
3896
3897 uint8_t tmp = prefix_len;
3898 inbind[1].buffer_type = MYSQL_TYPE_TINY;
3899 inbind[1].buffer = reinterpret_cast<char*>(&tmp);
3900 inbind[1].is_unsigned = MLM_TRUE;
3901
3902 ConstHostCollection collection;
3903 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_PREFIX, inbind,
3904 ctx->host_ipv6_exchange_, collection, true);
3905
3906 // Return single record if present, else clear the host.
3908 if (!collection.empty()) {
3909 result = *collection.begin();
3910 }
3911
3912 return (result);
3913}
3914
3917 const asiolink::IOAddress& address) const {
3918 if (!address.isV6()) {
3919 isc_throw(BadValue, "MySqlHostDataSource::get6(id, address): "
3920 "wrong address type, address supplied is an IPv4 address");
3921 }
3922
3923 // Get a context
3924 MySqlHostContextAlloc get_context(*impl_);
3925 MySqlHostContextPtr ctx = get_context.ctx_;
3926
3927 // Set up the WHERE clause value
3928 MYSQL_BIND inbind[2];
3929 memset(inbind, 0, sizeof(inbind));
3930
3931 uint32_t subnet_buffer = static_cast<uint32_t>(subnet_id);
3932 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3933 inbind[0].buffer = reinterpret_cast<char*>(&subnet_buffer);
3934 inbind[0].is_unsigned = MLM_TRUE;
3935
3936 std::vector<uint8_t>addr6 = address.toBytes();
3937 if (addr6.size() != isc::asiolink::V6ADDRESS_LEN) {
3938 isc_throw(DbOperationError, "get6() - address is not "
3939 << isc::asiolink::V6ADDRESS_LEN << " bytes long");
3940 }
3941
3942 unsigned long addr6_length = isc::asiolink::V6ADDRESS_LEN;
3943 inbind[1].buffer_type = MYSQL_TYPE_BLOB;
3944 inbind[1].buffer = reinterpret_cast<char*>(&addr6[0]);
3945 inbind[1].buffer_length = isc::asiolink::V6ADDRESS_LEN;
3946 inbind[1].length = &addr6_length;
3947
3948 ConstHostCollection collection;
3949 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR, inbind,
3950 ctx->host_ipv6_exchange_, collection, true);
3951
3952 // Return single record if present, else clear the host.
3954 if (!collection.empty()) {
3955 result = *collection.begin();
3956 }
3957
3958 return (result);
3959}
3960
3963 const asiolink::IOAddress& address) const {
3964 if (!address.isV6()) {
3965 isc_throw(BadValue, "MySqlHostDataSource::getAll6(id, address): "
3966 "wrong address type, address supplied is an IPv4 address");
3967 }
3968
3969 // Get a context
3970 MySqlHostContextAlloc get_context(*impl_);
3971 MySqlHostContextPtr ctx = get_context.ctx_;
3972
3973 // Set up the WHERE clause value
3974 MYSQL_BIND inbind[2];
3975 memset(inbind, 0, sizeof(inbind));
3976
3977 uint32_t subnet_buffer = static_cast<uint32_t>(subnet_id);
3978 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3979 inbind[0].buffer = reinterpret_cast<char*>(&subnet_buffer);
3980 inbind[0].is_unsigned = MLM_TRUE;
3981
3982 std::vector<uint8_t>addr6 = address.toBytes();
3983 if (addr6.size() != isc::asiolink::V6ADDRESS_LEN) {
3984 isc_throw(DbOperationError, "getAll6() - address is not "
3985 << isc::asiolink::V6ADDRESS_LEN << " bytes long");
3986 }
3987
3988 unsigned long addr6_length = isc::asiolink::V6ADDRESS_LEN;
3989 inbind[1].buffer_type = MYSQL_TYPE_BLOB;
3990 inbind[1].buffer = reinterpret_cast<char*>(&addr6[0]);
3991 inbind[1].buffer_length = isc::asiolink::V6ADDRESS_LEN;
3992 inbind[1].length = &addr6_length;
3993
3994 ConstHostCollection collection;
3995 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR, inbind,
3996 ctx->host_ipv6_exchange_, collection, false);
3997 return (collection);
3998}
3999
4002 if (!address.isV6()) {
4003 isc_throw(BadValue, "MySqlHostDataSource::getAll6(address): "
4004 "wrong address type, address supplied is an IPv4 address");
4005 }
4006
4007 // Get a context
4008 MySqlHostContextAlloc get_context(*impl_);
4009 MySqlHostContextPtr ctx = get_context.ctx_;
4010
4011 // Set up the WHERE clause value
4012 MYSQL_BIND inbind[1];
4013 memset(inbind, 0, sizeof(inbind));
4014
4015 std::vector<uint8_t>addr6 = address.toBytes();
4016 if (addr6.size() != isc::asiolink::V6ADDRESS_LEN) {
4017 isc_throw(DbOperationError, "getAll6() - address is not "
4018 << isc::asiolink::V6ADDRESS_LEN << " bytes long");
4019 }
4020
4021 unsigned long addr6_length = isc::asiolink::V6ADDRESS_LEN;
4022 inbind[0].buffer_type = MYSQL_TYPE_BLOB;
4023 inbind[0].buffer = reinterpret_cast<char*>(&addr6[0]);
4024 inbind[0].buffer_length = isc::asiolink::V6ADDRESS_LEN;
4025 inbind[0].length = &addr6_length;
4026
4027 ConstHostCollection collection;
4028 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_ADDR6, inbind,
4029 ctx->host_ipv6_exchange_, collection, false);
4030 return (collection);
4031}
4032
4033void
4035 // Get a context.
4036 MySqlHostContextAlloc const context(*impl_);
4037 MySqlHostContextPtr ctx(context.ctx_);
4038
4039 // If operating in read-only mode, throw exception.
4040 impl_->checkReadOnly(ctx);
4041
4042 // Initiate MySQL transaction as we will have to make multiple queries
4043 // to update host information into multiple tables. If that fails on
4044 // any stage, the transaction will be rolled back by the destructor of
4045 // the MySqlTransaction class.
4046 MySqlTransaction transaction(ctx->conn_);
4047
4048 // As much as having dedicated prepared statements for updating tables would be consistent with
4049 // the implementation of other commands, it's difficult if not impossible to cover all cases for
4050 // updating the host to exactly as is described in the command, which may involve inserts and
4051 // deletes alongside updates. So let's delete and add. The delete cascades into all tables. The
4052 // add explicitly adds into all tables.
4054
4055 // Everything went fine, so explicitly commit the transaction.
4056 transaction.commit();
4057}
4058
4059// Miscellaneous database methods.
4060
4061std::string
4063 std::string name = "";
4064 // Get a context
4065 MySqlHostContextAlloc get_context(*impl_);
4066 MySqlHostContextPtr ctx = get_context.ctx_;
4067
4068 try {
4069 name = ctx->conn_.getParameter("name");
4070 } catch (...) {
4071 // Return an empty name
4072 }
4073 return (name);
4074}
4075
4076std::string
4078 return (std::string("Host data source that stores host information"
4079 "in MySQL database"));
4080}
4081
4082std::pair<uint32_t, uint32_t>
4083MySqlHostDataSource::getVersion(const std::string& timer_name) const {
4084 return (impl_->getVersion(timer_name));
4085}
4086
4087void
4089 // Get a context
4090 MySqlHostContextAlloc get_context(*impl_);
4091 MySqlHostContextPtr ctx = get_context.ctx_;
4092
4093 // If operating in read-only mode, throw exception.
4094 impl_->checkReadOnly(ctx);
4095 if (ctx->conn_.isTransactionStarted()) {
4096 ctx->conn_.commit();
4097 }
4098}
4099
4100void
4102 // Get a context
4103 MySqlHostContextAlloc get_context(*impl_);
4104 MySqlHostContextPtr ctx = get_context.ctx_;
4105
4106 // If operating in read-only mode, throw exception.
4107 impl_->checkReadOnly(ctx);
4108 if (ctx->conn_.isTransactionStarted()) {
4109 ctx->conn_.rollback();
4110 }
4111}
4112
4113bool
4115 impl_->ip_reservations_unique_ = unique;
4116 return (true);
4117}
4118
4119bool
4121 return (impl_->unusable_);
4122}
4123
4124} // namespace dhcp
4125} // namespace isc
when the call the UDPServer carries on at the same position As a result
Definition asiodns.dox:16
@ map
Definition data.h:147
static ElementPtr fromJSON(const std::string &in, bool preproc=false)
These functions will parse the given string (JSON) representation of a compound element.
Definition data.cc:798
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown when an unexpected error condition occurs.
Data is truncated.
static bool invokeDbLostCallback(const util::ReconnectCtlPtr &db_reconnect_ctl)
Invokes the connection's lost connectivity callback.
static bool invokeDbFailedCallback(const util::ReconnectCtlPtr &db_reconnect_ctl)
Invokes the connection's restore failed connectivity callback.
static ParameterMap parse(const std::string &dbaccess)
Parse database access string.
static bool invokeDbRecoveredCallback(const util::ReconnectCtlPtr &db_reconnect_ctl)
Invokes the connection's restored connectivity callback.
static isc::asiolink::IOServicePtr & getIOService()
Returns pointer to the IO service.
std::map< std::string, std::string > ParameterMap
Database configuration parameter map.
Exception thrown on failure to execute a database function.
Database duplicate entry error.
Multiple lease records found where one expected.
Common MySQL Connector Pool.
static std::pair< uint32_t, uint32_t > getVersion(const ParameterMap &parameters, const IOServiceAccessorPtr &ac=IOServiceAccessorPtr(), const DbCallback &cb=DbCallback(), const std::string &timer_name=std::string(), unsigned int id=0)
Get the schema version.
static void ensureSchemaVersion(const ParameterMap &parameters, const DbCallback &cb=DbCallback(), const std::string &timer_name=std::string())
Retrieve schema version, validate it against the hardcoded version, and attempt to initialize the sch...
Fetch and Release MySQL Results.
RAII object representing MySQL transaction.
void commit()
Commits transaction.
Attempt to modify data in read-only database.
virtual void update(HostPtr const &host)
Attempts to update an existing host entry.
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition cfgmgr.cc:28
SrvConfigPtr getCurrentCfg()
Returns a pointer to the current configuration.
Definition cfgmgr.cc:115
static constexpr size_t MAX_CLIENT_ID_LEN
Maximum size of a client ID.
Definition duid.h:235
static bool delBackend(const std::string &db_type)
Delete an alternate host backend (aka host data source).
Definition host_mgr.cc:62
static void addBackend(const std::string &access)
Add an alternate host backend (aka host data source).
Definition host_mgr.cc:57
Wraps value holding size of the page with host reservations.
const size_t page_size_
Holds page size.
IdentifierType
Type of the host identifier.
Definition host.h:307
static const IdentifierType LAST_IDENTIFIER_TYPE
Constant pointing to the last identifier of the IdentifierType enumeration.
Definition host.h:317
IPv6 reservation for a host.
Definition host.h:161
const asiolink::IOAddress & getPrefix() const
Returns prefix for the reservation.
Definition host.h:190
Type getType() const
Returns reservation type.
Definition host.h:204
Type
Type of the reservation.
Definition host.h:167
uint8_t getPrefixLen() const
Returns prefix length.
Definition host.h:195
static OptionDefinitionPtr getOptionDef(const std::string &space, const uint16_t code)
Return the first option definition matching a particular option code.
Definition libdhcp++.cc:126
static OptionDefinitionPtr getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id, const uint16_t code)
Returns vendor option definition for a given vendor-id and code.
Definition libdhcp++.cc:168
static uint32_t optionSpaceToVendorId(const std::string &option_space)
Converts option space name to vendor id.
static OptionDefinitionPtr getRuntimeOptionDef(const std::string &space, const uint16_t code)
Returns runtime (non-standard) option definition by space and option code.
Definition libdhcp++.cc:189
static OptionDefinitionPtr getLastResortOptionDef(const std::string &space, const uint16_t code)
Returns last resort option definition by space and option code.
Definition libdhcp++.cc:247
std::vector< MySqlHostContextPtr > pool_
The vector of available contexts.
std::mutex mutex_
The mutex to protect pool access.
MySqlHostContext(const DatabaseConnection::ParameterMap &parameters, IOServiceAccessorPtr io_service_accessor, db::DbCallback db_reconnect_callback)
Constructor.
boost::shared_ptr< MySqlHostIPv6Exchange > host_ipv46_exchange_
Pointer to an object representing an exchange which can be used to retrieve hosts,...
boost::shared_ptr< MySqlHostIPv6Exchange > host_ipv6_exchange_
Pointer to an object representing an exchange which can be used to retrieve hosts,...
boost::shared_ptr< MySqlIPv6ReservationExchange > host_ipv6_reservation_exchange_
Pointer to an object representing an exchange which can be used to insert new IPv6 reservation.
boost::shared_ptr< MySqlHostWithOptionsExchange > host_ipv4_exchange_
The exchange objects are used for transfer of data to/from the database.
boost::shared_ptr< MySqlOptionExchange > host_option_exchange_
Pointer to an object representing an exchange which can be used to insert DHCPv4 or DHCPv6 option int...
MySqlConnection conn_
MySQL connection.
bool is_readonly_
Indicates if the database is opened in read only mode.
Implementation of the MySqlHostDataSource.
void checkReadOnly(MySqlHostContextPtr &ctx) const
Throws exception if database is read only.
void checkError(MySqlHostContextPtr &ctx, const int status, const StatementIndex index, const char *what) const
Check Error and Throw Exception.
std::string timer_name_
Timer name used to register database reconnect timer.
DatabaseConnection::ParameterMap parameters_
The parameters.
MySqlHostContextPtr createContext() const
Create a new context.
bool delStatement(MySqlHostContextPtr &ctx, StatementIndex stindex, MYSQL_BIND *bind)
Executes statements that delete records.
MySqlHostDataSourceImpl(const DatabaseConnection::ParameterMap &parameters)
Constructor.
ConstHostPtr getHost(MySqlHostContextPtr &ctx, const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len, StatementIndex stindex, boost::shared_ptr< MySqlHostExchange > exchange) const
Retrieves a host by subnet and client's unique identifier.
void addStatement(MySqlHostContextPtr &ctx, MySqlHostDataSourceImpl::StatementIndex stindex, std::vector< MYSQL_BIND > &bind)
Executes statements which inserts a row into one of the tables.
std::pair< uint32_t, uint32_t > getVersion(const std::string &timer_name=std::string()) const
Returns backend version.
void getHostCollection(MySqlHostContextPtr &ctx, StatementIndex stindex, MYSQL_BIND *bind, boost::shared_ptr< MySqlHostExchange > exchange, ConstHostCollection &result, bool single) const
Creates collection of Host objects with associated information such as IPv6 reservations and/or DHCP ...
bool ip_reservations_unique_
Holds the setting whether the IP reservations must be unique or may be non-unique.
void addOptions(MySqlHostContextPtr &ctx, const StatementIndex &stindex, const ConstCfgOptionPtr &options_cfg, const uint64_t host_id)
Inserts multiple options into the database.
MySqlHostContextPoolPtr pool_
The pool of contexts.
void addOption(MySqlHostContextPtr &ctx, const MySqlHostDataSourceImpl::StatementIndex &stindex, const OptionDescriptor &opt_desc, const std::string &opt_space, const Optional< SubnetID > &subnet_id, const HostID &host_id)
Inserts a single DHCP option into the database.
bool unusable_
Indicates if there is at least one connection that can no longer be used for normal operations.
void addResv(MySqlHostContextPtr &ctx, const IPv6Resrv &resv, const HostID &id)
Inserts IPv6 Reservation into ipv6_reservation table.
static bool dbReconnect(ReconnectCtlPtr db_reconnect_ctl)
Attempts to reconnect the server to the host DB backend manager.
static const StatementIndex WRITE_STMTS_BEGIN
Index of first statement performing write to the database.
MySqlHostContextAlloc(MySqlHostDataSourceImpl &mgr)
Constructor.
virtual ConstHostCollection getAll(const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len) const
Return all hosts connected to any subnet for which reservations have been made using a specified iden...
virtual ConstHostPtr get6(const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len) const
Returns a host connected to the IPv6 subnet.
virtual std::pair< uint32_t, uint32_t > getVersion(const std::string &timer_name=std::string()) const
Returns backend version.
virtual void commit()
Commit Transactions.
virtual bool del(const SubnetID &subnet_id, const asiolink::IOAddress &addr)
Attempts to delete hosts by (subnet-id, address)
virtual ConstHostCollection getAll4(const SubnetID &subnet_id) const
Return all hosts in a DHCPv4 subnet.
virtual ConstHostCollection getAll6(const SubnetID &subnet_id) const
Return all hosts in a DHCPv6 subnet.
virtual std::string getDescription() const
Returns description of the backend.
MySqlHostDataSource(const db::DatabaseConnection::ParameterMap &parameters)
Constructor.
virtual ~MySqlHostDataSource()
Virtual destructor.
virtual ConstHostCollection getAllbyHostname(const std::string &hostname) const
Return all hosts with a hostname.
virtual isc::db::DatabaseConnection::ParameterMap getParameters() const
Return backend parameters.
void update(HostPtr const &host)
Implements BaseHostDataSource::update() for MySQL.
virtual bool del6(const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len)
Attempts to delete a host by (subnet6-id, identifier type, identifier)
virtual ConstHostCollection getPage6(const SubnetID &subnet_id, size_t &source_index, uint64_t lower_host_id, const HostPageSize &page_size) const
Returns range of hosts in a DHCPv6 subnet.
virtual ConstHostCollection getAllbyHostname6(const std::string &hostname, const SubnetID &subnet_id) const
Return all hosts with a hostname in a DHCPv6 subnet.
virtual bool del4(const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len)
Attempts to delete a host by (subnet4-id, identifier type, identifier)
virtual ConstHostCollection getPage4(const SubnetID &subnet_id, size_t &source_index, uint64_t lower_host_id, const HostPageSize &page_size) const
Returns range of hosts in a DHCPv4 subnet.
virtual bool isUnusable()
Flag which indicates if the host manager has at least one unusable connection.
virtual ConstHostCollection getAllbyHostname4(const std::string &hostname, const SubnetID &subnet_id) const
Return all hosts with a hostname in a DHCPv4 subnet.
virtual void rollback()
Rollback Transactions.
virtual std::string getName() const
Returns backend name.
virtual void add(const HostPtr &host)
Adds a new host to the collection.
virtual ConstHostPtr get4(const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len) const
Returns a host connected to the IPv4 subnet.
virtual bool setIPReservationsUnique(const bool unique)
Controls whether IP reservations are unique or non-unique.
static const unsigned int DB_CONNECTION
The network state is being altered by the DB connection recovery mechanics.
Option descriptor.
Definition cfg_option.h:47
OptionPtr option_
Option instance.
Definition cfg_option.h:50
bool cancelled_
Cancelled flag.
Definition cfg_option.h:64
std::string formatted_value_
Option value in textual (CSV) format.
Definition cfg_option.h:79
bool persistent_
Persistence flag.
Definition cfg_option.h:56
Universe
defines option universe DHCPv4 or DHCPv6
Definition option.h:83
static const TimerMgrPtr & instance()
Returns pointer to the sole instance of the TimerMgr.
Definition timer_mgr.cc:446
RAII class creating a critical section.
static MultiThreadingMgr & instance()
Returns a single instance of Multi Threading Manager.
A template representing an optional value.
Definition optional.h:36
void unspecified(bool unspecified)
Modifies the flag that indicates whether the value is specified or unspecified.
Definition optional.h:136
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition macros.h:32
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition macros.h:20
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition macros.h:14
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:29
const my_bool MLM_FALSE
MySQL false value.
boost::shared_ptr< IOServiceAccessor > IOServiceAccessorPtr
Pointer to an instance of IOServiceAccessor.
@ error
Definition db_log.h:118
const my_bool MLM_TRUE
MySQL true value.
bool my_bool
my_bool type in MySQL 8.x.
const int MLM_MYSQL_FETCH_FAILURE
MySQL fetch failure code.
const int MLM_MYSQL_FETCH_SUCCESS
check for bool size
std::function< bool(util::ReconnectCtlPtr db_reconnect_ctl)> DbCallback
Defines a callback prototype for propagating events upward.
std::function< isc::asiolink::IOServicePtr()> IOServiceAccessor
Function which returns the IOService that can be used to recover the connection.
int MysqlExecuteStatement(MYSQL_STMT *stmt)
Execute a prepared statement.
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition dhcpsrv_log.h:56
const size_t OPTION_FORMATTED_VALUE_MAX_LEN
Maximum length of option value specified in textual format.
Definition host.h:48
const isc::log::MessageID DHCPSRV_MYSQL_HOST_DB_GET_VERSION
boost::shared_ptr< CfgOption > CfgOptionPtr
Non-const pointer.
Definition cfg_option.h:803
boost::shared_ptr< Host > HostPtr
Pointer to the Host object.
Definition host.h:807
std::vector< ConstHostPtr > ConstHostCollection
Collection of the const Host objects.
Definition host.h:813
const isc::log::MessageID DHCPSRV_MYSQL_NO_TLS
boost::shared_ptr< CfgDbAccess > CfgDbAccessPtr
A pointer to the CfgDbAccess.
const size_t CLIENT_CLASSES_MAX_LEN
Maximum length of classes stored in a dhcp4/6_client_classes columns.
Definition host.h:36
std::pair< IPv6ResrvIterator, IPv6ResrvIterator > IPv6ResrvRange
Definition host.h:243
const int DHCPSRV_DBG_TRACE_DETAIL
Additional information.
Definition dhcpsrv_log.h:38
const size_t TEXT_AUTH_KEY_LEN
Maximum length of authentication keys (coded in hexadecimal).
Definition host.h:66
const isc::log::MessageID DHCPSRV_MYSQL_HOST_DB_RECONNECT_FAILED
boost::shared_ptr< OptionDefinition > OptionDefinitionPtr
Pointer to option definition object.
uint32_t SubnetID
Defines unique IPv4 or IPv6 subnet identifier.
Definition subnet_id.h:25
uint64_t HostID
HostID (used only when storing in MySQL or PostgreSQL backends)
Definition host.h:69
boost::shared_ptr< OptionContainer > OptionContainerPtr
Pointer to the OptionContainer object.
Definition cfg_option.h:303
boost::shared_ptr< const Host > ConstHostPtr
Const pointer to the Host object.
Definition host.h:810
boost::shared_ptr< MySqlHostContextPool > MySqlHostContextPoolPtr
Type of pointers to context pools.
const size_t HOSTNAME_MAX_LEN
Maximum length of the hostname stored in DNS.
Definition host.h:42
boost::shared_ptr< MySqlHostContext > MySqlHostContextPtr
Type of pointers to contexts.
const size_t USER_CONTEXT_MAX_LEN
Maximum length of user context.
Definition host.h:54
const size_t OPTION_VALUE_MAX_LEN
Maximum length of option value.
Definition host.h:45
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition option.h:24
const size_t SERVER_HOSTNAME_MAX_LEN
Maximum length of the server hostname.
Definition host.h:57
const isc::log::MessageID DHCPSRV_MYSQL_HOST_DB_READONLY
const size_t BOOT_FILE_NAME_MAX_LEN
Maximum length of the boot file name.
Definition host.h:60
const int DHCPSRV_DBG_TRACE
DHCP server library logging levels.
Definition dhcpsrv_log.h:26
const isc::log::MessageID DHCPSRV_MYSQL_HOST_DB_RECONNECT_ATTEMPT_SCHEDULE
boost::shared_ptr< Option > OptionPtr
Definition option.h:37
const isc::log::MessageID DHCPSRV_MYSQL_HOST_DB_RECONNECT_ATTEMPT_FAILED
const isc::log::MessageID DHCPSRV_MYSQL_TLS_CIPHER
boost::shared_ptr< const CfgOption > ConstCfgOptionPtr
Const pointer.
Definition cfg_option.h:806
const size_t OPTION_SPACE_MAX_LEN
Maximum length of option space name.
Definition host.h:51
boost::shared_ptr< ReconnectCtl > ReconnectCtlPtr
Pointer to an instance of ReconnectCtl.
Defines the logger used by the top-level component of kea-lfc.
#define DHCP4_OPTION_SPACE
global std option spaces
#define DHCP6_OPTION_SPACE
data::ConstElementPtr getContext() const
Returns const pointer to the user context.