Kea 2.2.0
mysql_host_data_source.cc
Go to the documentation of this file.
1// Copyright (C) 2015-2022 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>
21#include <dhcpsrv/timer_mgr.h>
22#include <util/buffer.h>
24#include <util/optional.h>
25
26#include <boost/algorithm/string/split.hpp>
27#include <boost/algorithm/string/classification.hpp>
28#include <boost/array.hpp>
29#include <boost/pointer_cast.hpp>
30#include <boost/static_assert.hpp>
31
32#include <mysql.h>
33#include <mysqld_error.h>
34#include <stdint.h>
35
36#include <mutex>
37#include <string>
38
39using namespace isc;
40using namespace isc::asiolink;
41using namespace isc::db;
42using namespace isc::dhcp;
43using namespace isc::util;
44using namespace isc::data;
45using namespace std;
46
47namespace {
48
53const uint8_t MAX_IDENTIFIER_TYPE = static_cast<uint8_t>(Host::LAST_IDENTIFIER_TYPE);
54
83class MySqlHostExchange {
84private:
85
87 static const size_t HOST_COLUMNS = 14;
88
89public:
90
97 MySqlHostExchange(const size_t additional_columns_num = 0)
98 : columns_num_(HOST_COLUMNS + additional_columns_num),
99 bind_(columns_num_), columns_(columns_num_),
100 error_(columns_num_, MLM_FALSE), host_id_(0),
101 dhcp_identifier_length_(0), dhcp_identifier_type_(0),
102 dhcp4_subnet_id_(SUBNET_ID_UNUSED),
103 dhcp6_subnet_id_(SUBNET_ID_UNUSED), ipv4_address_(0),
104 hostname_length_(0), dhcp4_client_classes_length_(0),
105 dhcp6_client_classes_length_(0),
106 user_context_length_(0),
107 dhcp4_next_server_(0),
108 dhcp4_server_hostname_length_(0),
109 dhcp4_boot_file_name_length_(0),
110 auth_key_length_(0),
111 dhcp4_subnet_id_null_(MLM_FALSE),
112 dhcp6_subnet_id_null_(MLM_FALSE),
113 ipv4_address_null_(MLM_FALSE), hostname_null_(MLM_FALSE),
114 dhcp4_client_classes_null_(MLM_FALSE),
115 dhcp6_client_classes_null_(MLM_FALSE),
116 user_context_null_(MLM_FALSE),
117 dhcp4_next_server_null_(MLM_FALSE),
118 dhcp4_server_hostname_null_(MLM_FALSE),
119 dhcp4_boot_file_name_null_(MLM_FALSE),
120 auth_key_null_(MLM_FALSE) {
121
122 // Fill arrays with 0 so as they don't include any garbage.
123 memset(dhcp_identifier_buffer_, 0, sizeof(dhcp_identifier_buffer_));
124 memset(hostname_, 0, sizeof(hostname_));
125 memset(dhcp4_client_classes_, 0, sizeof(dhcp4_client_classes_));
126 memset(dhcp6_client_classes_, 0, sizeof(dhcp6_client_classes_));
127 memset(user_context_, 0, sizeof(user_context_));
128 memset(dhcp4_server_hostname_, 0, sizeof(dhcp4_server_hostname_));
129 memset(dhcp4_boot_file_name_, 0, sizeof(dhcp4_boot_file_name_));
130 memset(auth_key_, 0, sizeof(auth_key_));
131
132 // Set the column names for use by this class. This only comprises
133 // names used by the MySqlHostExchange class. Derived classes will
134 // need to set names for the columns they use.
135 columns_[0] = "host_id";
136 columns_[1] = "dhcp_identifier";
137 columns_[2] = "dhcp_identifier_type";
138 columns_[3] = "dhcp4_subnet_id";
139 columns_[4] = "dhcp6_subnet_id";
140 columns_[5] = "ipv4_address";
141 columns_[6] = "hostname";
142 columns_[7] = "dhcp4_client_classes";
143 columns_[8] = "dhcp6_client_classes";
144 columns_[9] = "user_context";
145 columns_[10] = "dhcp4_next_server";
146 columns_[11] = "dhcp4_server_hostname";
147 columns_[12] = "dhcp4_boot_file_name";
148 columns_[13] = "auth_key";
149
150 BOOST_STATIC_ASSERT(13 < HOST_COLUMNS);
151 };
152
154 virtual ~MySqlHostExchange() {
155 }
156
171 size_t findAvailColumn() const {
172 std::vector<std::string>::const_iterator empty_column =
173 std::find(columns_.begin(), columns_.end(), std::string());
174 return (std::distance(columns_.begin(), empty_column));
175 }
176
180 uint64_t getHostId() const {
181 return (host_id_);
182 };
183
194 static void setErrorIndicators(std::vector<MYSQL_BIND>& bind,
195 std::vector<my_bools>& error) {
196 for (size_t i = 0; i < error.size(); ++i) {
197 error[i] = MLM_FALSE;
198 bind[i].error = reinterpret_cast<my_bool*>(&error[i]);
199 }
200 };
201
215 static std::string getColumnsInError(std::vector<my_bools>& error,
216 const std::vector<std::string>& names) {
217 std::string result = "";
218
219 // Accumulate list of column names
220 for (size_t i = 0; i < names.size(); ++i) {
221 if (error[i] == MLM_TRUE) {
222 if (!result.empty()) {
223 result += ", ";
224 }
225 result += names[i];
226 }
227 }
228
229 if (result.empty()) {
230 result = "(None)";
231 }
232
233 return (result);
234 };
235
248 std::vector<MYSQL_BIND> createBindForSend(const HostPtr& host, const bool unique_ip) {
249 // Store host object to ensure it remains valid.
250 host_ = host;
251
252 // Initialize prior to constructing the array of MYSQL_BIND structures.
253 // It sets all fields, including is_null, to zero, so we need to set
254 // is_null only if it should be true. This gives up minor performance
255 // benefit while being safe approach.
256 memset(&bind_[0], 0, sizeof(MYSQL_BIND) * bind_.size());
257
258 // Set up the structures for the various components of the host structure.
259
260 try {
261 // host_id : INT UNSIGNED NOT NULL
262 // The host_id is auto_incremented by MySQL database,
263 // so we need to pass the NULL value
264 host_id_ = 0;
265 bind_[0].buffer_type = MYSQL_TYPE_LONG;
266 bind_[0].buffer = reinterpret_cast<char*>(&host_id_);
267 bind_[0].is_unsigned = MLM_TRUE;
268
269 // dhcp_identifier : VARBINARY(128) NOT NULL
270 dhcp_identifier_length_ = host->getIdentifier().size();
271 memcpy(static_cast<void*>(dhcp_identifier_buffer_),
272 &(host->getIdentifier())[0],
273 host->getIdentifier().size());
274
275 bind_[1].buffer_type = MYSQL_TYPE_BLOB;
276 bind_[1].buffer = dhcp_identifier_buffer_;
277 bind_[1].buffer_length = dhcp_identifier_length_;
278 bind_[1].length = &dhcp_identifier_length_;
279
280 // dhcp_identifier_type : TINYINT NOT NULL
281 dhcp_identifier_type_ = static_cast<uint8_t>(host->getIdentifierType());
282 bind_[2].buffer_type = MYSQL_TYPE_TINY;
283 bind_[2].buffer = reinterpret_cast<char*>(&dhcp_identifier_type_);
284 bind_[2].is_unsigned = MLM_TRUE;
285
286 // dhcp4_subnet_id : INT UNSIGNED NULL
287 // Can't take an address of intermediate object, so let's store it
288 // in dhcp4_subnet_id_
289 dhcp4_subnet_id_ = host->getIPv4SubnetID();
290 dhcp4_subnet_id_null_ = host->getIPv4SubnetID() == SUBNET_ID_UNUSED ? MLM_TRUE : MLM_FALSE;
291 bind_[3].buffer_type = MYSQL_TYPE_LONG;
292 bind_[3].buffer = reinterpret_cast<char*>(&dhcp4_subnet_id_);
293 bind_[3].is_unsigned = MLM_TRUE;
294 bind_[3].is_null = &dhcp4_subnet_id_null_;
295
296 // dhcp6_subnet_id : INT UNSIGNED NULL
297 // Can't take an address of intermediate object, so let's store it
298 // in dhcp6_subnet_id_
299 dhcp6_subnet_id_ = host->getIPv6SubnetID();
300 dhcp6_subnet_id_null_ = host->getIPv6SubnetID() == SUBNET_ID_UNUSED ? MLM_TRUE : MLM_FALSE;
301 bind_[4].buffer_type = MYSQL_TYPE_LONG;
302 bind_[4].buffer = reinterpret_cast<char*>(&dhcp6_subnet_id_);
303 bind_[4].is_unsigned = MLM_TRUE;
304 bind_[4].is_null = &dhcp6_subnet_id_null_;
305
306 // ipv4_address : INT UNSIGNED NULL
307 // The address in the Host structure is an IOAddress object. Convert
308 // this to an integer for storage.
309 ipv4_address_ = host->getIPv4Reservation().toUint32();
310 ipv4_address_null_ = ipv4_address_ == 0 ? MLM_TRUE : MLM_FALSE;
311 bind_[5].buffer_type = MYSQL_TYPE_LONG;
312 bind_[5].buffer = reinterpret_cast<char*>(&ipv4_address_);
313 bind_[5].is_unsigned = MLM_TRUE;
314 bind_[5].is_null = &ipv4_address_null_;
315
316 // hostname : VARCHAR(255) NULL
317 strncpy(hostname_, host->getHostname().c_str(), HOSTNAME_MAX_LEN - 1);
318 hostname_length_ = host->getHostname().length();
319 bind_[6].buffer_type = MYSQL_TYPE_STRING;
320 bind_[6].buffer = reinterpret_cast<char*>(hostname_);
321 bind_[6].buffer_length = hostname_length_;
322
323 // dhcp4_client_classes : VARCHAR(255) NULL
324 bind_[7].buffer_type = MYSQL_TYPE_STRING;
325 // Override default separator to not include space after comma.
326 string classes4_txt = host->getClientClasses4().toText(",");
327 strncpy(dhcp4_client_classes_, classes4_txt.c_str(), CLIENT_CLASSES_MAX_LEN - 1);
328 bind_[7].buffer = dhcp4_client_classes_;
329 bind_[7].buffer_length = classes4_txt.length();
330
331 // dhcp6_client_classes : VARCHAR(255) NULL
332 bind_[8].buffer_type = MYSQL_TYPE_STRING;
333 // Override default separator to not include space after comma.
334 string classes6_txt = host->getClientClasses6().toText(",");
335 strncpy(dhcp6_client_classes_, classes6_txt.c_str(), CLIENT_CLASSES_MAX_LEN - 1);
336 bind_[8].buffer = dhcp6_client_classes_;
337 bind_[8].buffer_length = classes6_txt.length();
338
339 // user_context : TEXT NULL
340 ConstElementPtr ctx = host->getContext();
341 if (ctx) {
342 bind_[9].buffer_type = MYSQL_TYPE_STRING;
343 string ctx_txt = ctx->str();
344 strncpy(user_context_, ctx_txt.c_str(), USER_CONTEXT_MAX_LEN - 1);
345 bind_[9].buffer = user_context_;
346 bind_[9].buffer_length = ctx_txt.length();
347 } else {
348 bind_[9].buffer_type = MYSQL_TYPE_NULL;
349 }
350
351 // ipv4_address : INT UNSIGNED NULL
352 // The address in the Host structure is an IOAddress object. Convert
353 // this to an integer for storage.
354 dhcp4_next_server_ = host->getNextServer().toUint32();
355 bind_[10].buffer_type = MYSQL_TYPE_LONG;
356 bind_[10].buffer = reinterpret_cast<char*>(&dhcp4_next_server_);
357 bind_[10].is_unsigned = MLM_TRUE;
358 // bind_[10].is_null = &MLM_FALSE; // commented out for performance
359 // reasons, see memset() above
360
361 // dhcp4_server_hostname
362 bind_[11].buffer_type = MYSQL_TYPE_STRING;
363 std::string server_hostname = host->getServerHostname();
364 strncpy(dhcp4_server_hostname_, server_hostname.c_str(),
366 bind_[11].buffer = dhcp4_server_hostname_;
367 bind_[11].buffer_length = server_hostname.length();
368
369 // dhcp4_boot_file_name
370 bind_[12].buffer_type = MYSQL_TYPE_STRING;
371 std::string boot_file_name = host->getBootFileName();
372 strncpy(dhcp4_boot_file_name_, boot_file_name.c_str(),
374 bind_[12].buffer = dhcp4_boot_file_name_;
375 bind_[12].buffer_length = boot_file_name.length();
376
377 // auth key
378 bind_[13].buffer_type = MYSQL_TYPE_STRING;
379 std::string auth_key = host->getKey().toText();
380 std::strncpy(auth_key_, auth_key.c_str(), TEXT_AUTH_KEY_LEN - 1);
381 auth_key_null_ = auth_key.empty() ? MLM_TRUE : MLM_FALSE;
382 bind_[13].buffer = auth_key_;
383 bind_[13].buffer_length = auth_key.length();
384
385 } catch (const std::exception& ex) {
387 "Could not create bind array from Host: "
388 << host->getHostname() << ", reason: " << ex.what());
389 }
390
391 // Add the data to the vector.
392 std::vector<MYSQL_BIND> vec(bind_.begin(), bind_.begin() + HOST_COLUMNS);
393
394 // When checking whether the IP is unique we need to bind the IPv4 address
395 // at the end of the query as it has additional binding for the IPv4
396 // address.
397 if (unique_ip) {
398 vec.push_back(bind_[5]); // ipv4_address
399 vec.push_back(bind_[3]); // subnet_id
400 }
401 return (vec);
402 };
403
411 virtual std::vector<MYSQL_BIND> createBindForReceive() {
412 // Initialize MYSQL_BIND array.
413 // It sets all fields, including is_null, to zero, so we need to set
414 // is_null only if it should be true. This gives up minor performance
415 // benefit while being safe approach. For improved readability, the
416 // code that explicitly sets is_null is there, but is commented out.
417 // This also takes care of setting bind_[X].is_null to MLM_FALSE.
418 memset(&bind_[0], 0, sizeof(MYSQL_BIND) * bind_.size());
419
420 // host_id : INT UNSIGNED NOT NULL
421 bind_[0].buffer_type = MYSQL_TYPE_LONG;
422 bind_[0].buffer = reinterpret_cast<char*>(&host_id_);
423 bind_[0].is_unsigned = MLM_TRUE;
424
425 // dhcp_identifier : VARBINARY(128) NOT NULL
426 dhcp_identifier_length_ = sizeof(dhcp_identifier_buffer_);
427 bind_[1].buffer_type = MYSQL_TYPE_BLOB;
428 bind_[1].buffer = reinterpret_cast<char*>(dhcp_identifier_buffer_);
429 bind_[1].buffer_length = dhcp_identifier_length_;
430 bind_[1].length = &dhcp_identifier_length_;
431
432 // dhcp_identifier_type : TINYINT NOT NULL
433 bind_[2].buffer_type = MYSQL_TYPE_TINY;
434 bind_[2].buffer = reinterpret_cast<char*>(&dhcp_identifier_type_);
435 bind_[2].is_unsigned = MLM_TRUE;
436
437 // dhcp4_subnet_id : INT UNSIGNED NULL
438 dhcp4_subnet_id_null_ = MLM_FALSE;
439 bind_[3].buffer_type = MYSQL_TYPE_LONG;
440 bind_[3].buffer = reinterpret_cast<char*>(&dhcp4_subnet_id_);
441 bind_[3].is_unsigned = MLM_TRUE;
442 bind_[3].is_null = &dhcp4_subnet_id_null_;
443
444 // dhcp6_subnet_id : INT UNSIGNED NULL
445 dhcp6_subnet_id_null_ = MLM_FALSE;
446 bind_[4].buffer_type = MYSQL_TYPE_LONG;
447 bind_[4].buffer = reinterpret_cast<char*>(&dhcp6_subnet_id_);
448 bind_[4].is_unsigned = MLM_TRUE;
449 bind_[4].is_null = &dhcp6_subnet_id_null_;
450
451 // ipv4_address : INT UNSIGNED NULL
452 ipv4_address_null_ = MLM_FALSE;
453 bind_[5].buffer_type = MYSQL_TYPE_LONG;
454 bind_[5].buffer = reinterpret_cast<char*>(&ipv4_address_);
455 bind_[5].is_unsigned = MLM_TRUE;
456 bind_[5].is_null = &ipv4_address_null_;
457
458 // hostname : VARCHAR(255) NULL
459 hostname_null_ = MLM_FALSE;
460 hostname_length_ = sizeof(hostname_);
461 bind_[6].buffer_type = MYSQL_TYPE_STRING;
462 bind_[6].buffer = reinterpret_cast<char*>(hostname_);
463 bind_[6].buffer_length = hostname_length_;
464 bind_[6].length = &hostname_length_;
465 bind_[6].is_null = &hostname_null_;
466
467 // dhcp4_client_classes : VARCHAR(255) NULL
468 dhcp4_client_classes_null_ = MLM_FALSE;
469 dhcp4_client_classes_length_ = sizeof(dhcp4_client_classes_);
470 bind_[7].buffer_type = MYSQL_TYPE_STRING;
471 bind_[7].buffer = reinterpret_cast<char*>(dhcp4_client_classes_);
472 bind_[7].buffer_length = dhcp4_client_classes_length_;
473 bind_[7].length = &dhcp4_client_classes_length_;
474 bind_[7].is_null = &dhcp4_client_classes_null_;
475
476 // dhcp6_client_classes : VARCHAR(255) NULL
477 dhcp6_client_classes_null_ = MLM_FALSE;
478 dhcp6_client_classes_length_ = sizeof(dhcp6_client_classes_);
479 bind_[8].buffer_type = MYSQL_TYPE_STRING;
480 bind_[8].buffer = reinterpret_cast<char*>(dhcp6_client_classes_);
481 bind_[8].buffer_length = dhcp6_client_classes_length_;
482 bind_[8].length = &dhcp6_client_classes_length_;
483 bind_[8].is_null = &dhcp6_client_classes_null_;
484
485 // user_context : TEXT NULL
486 user_context_null_ = MLM_FALSE;
487 user_context_length_ = sizeof(user_context_);
488 bind_[9].buffer_type = MYSQL_TYPE_STRING;
489 bind_[9].buffer = reinterpret_cast<char*>(user_context_);
490 bind_[9].buffer_length = user_context_length_;
491 bind_[9].length = &user_context_length_;
492 bind_[9].is_null = &user_context_null_;
493
494 // dhcp4_next_server
495 dhcp4_next_server_null_ = MLM_FALSE;
496 bind_[10].buffer_type = MYSQL_TYPE_LONG;
497 bind_[10].buffer = reinterpret_cast<char*>(&dhcp4_next_server_);
498 bind_[10].is_unsigned = MLM_TRUE;
499 bind_[10].is_null = &dhcp4_next_server_null_;
500
501 // dhcp4_server_hostname
502 dhcp4_server_hostname_null_ = MLM_FALSE;
503 dhcp4_server_hostname_length_ = sizeof(dhcp4_server_hostname_);
504 bind_[11].buffer_type = MYSQL_TYPE_STRING;
505 bind_[11].buffer = reinterpret_cast<char*>(dhcp4_server_hostname_);
506 bind_[11].buffer_length = dhcp4_server_hostname_length_;
507 bind_[11].length = &dhcp4_server_hostname_length_;
508 bind_[11].is_null = &dhcp4_server_hostname_null_;
509
510 // dhcp4_boot_file_name
511 dhcp4_boot_file_name_null_ = MLM_FALSE;
512 dhcp4_boot_file_name_length_ = sizeof(dhcp4_boot_file_name_);
513 bind_[12].buffer_type = MYSQL_TYPE_STRING;
514 bind_[12].buffer = reinterpret_cast<char*>(dhcp4_boot_file_name_);
515 bind_[12].buffer_length = dhcp4_boot_file_name_length_;
516 bind_[12].length = &dhcp4_boot_file_name_length_;
517 bind_[12].is_null = &dhcp4_boot_file_name_null_;
518
519 // auth_key_
520 auth_key_null_ = MLM_FALSE;
521 auth_key_length_ = sizeof(auth_key_);
522 bind_[13].buffer_type = MYSQL_TYPE_STRING;
523 bind_[13].buffer = reinterpret_cast<char*>(auth_key_);
524 bind_[13].buffer_length = auth_key_length_;
525 bind_[13].length = &auth_key_length_;
526 bind_[13].is_null = &auth_key_null_;
527
528 // Add the error flags
529 setErrorIndicators(bind_, error_);
530
531 // Add the data to the vector. Note the end element is one after the
532 // end of the array.
533 return (bind_);
534 };
535
544 HostPtr retrieveHost() {
545 // Check if the identifier stored in the database is correct.
546 if (dhcp_identifier_type_ > MAX_IDENTIFIER_TYPE) {
547 isc_throw(BadValue, "invalid dhcp identifier type returned: "
548 << static_cast<int>(dhcp_identifier_type_));
549 }
550 // Set the dhcp identifier type in a variable of the appropriate
551 // data type.
553 static_cast<Host::IdentifierType>(dhcp_identifier_type_);
554
555 // Set DHCPv4 subnet ID to the value returned. If NULL returned,
556 // set to 0.
557 SubnetID ipv4_subnet_id(SUBNET_ID_UNUSED);
558 if (dhcp4_subnet_id_null_ == MLM_FALSE) {
559 ipv4_subnet_id = static_cast<SubnetID>(dhcp4_subnet_id_);
560 }
561
562 // Set DHCPv6 subnet ID to the value returned. If NULL returned,
563 // set to 0.
564 SubnetID ipv6_subnet_id(SUBNET_ID_UNUSED);
565 if (dhcp6_subnet_id_null_ == MLM_FALSE) {
566 ipv6_subnet_id = static_cast<SubnetID>(dhcp6_subnet_id_);
567 }
568
569 // Set IPv4 address reservation if it was given, if not, set IPv4 zero
570 // address
571 asiolink::IOAddress ipv4_reservation = asiolink::IOAddress::IPV4_ZERO_ADDRESS();
572 if (ipv4_address_null_ == MLM_FALSE) {
573 ipv4_reservation = asiolink::IOAddress(ipv4_address_);
574 }
575
576 // Set hostname if non NULL value returned. Otherwise, leave an
577 // empty string.
578 std::string hostname;
579 if (hostname_null_ == MLM_FALSE) {
580 hostname = std::string(hostname_, hostname_length_);
581 }
582
583 // Set DHCPv4 client classes if non NULL value returned.
584 std::string dhcp4_client_classes;
585 if (dhcp4_client_classes_null_ == MLM_FALSE) {
586 dhcp4_client_classes = std::string(dhcp4_client_classes_,
587 dhcp4_client_classes_length_);
588 }
589
590 // Set DHCPv6 client classes if non NULL value returned.
591 std::string dhcp6_client_classes;
592 if (dhcp6_client_classes_null_ == MLM_FALSE) {
593 dhcp6_client_classes = std::string(dhcp6_client_classes_,
594 dhcp6_client_classes_length_);
595 }
596
597 // Convert user_context to string as well.
598 std::string user_context;
599 if (user_context_null_ == MLM_FALSE) {
600 user_context_[user_context_length_] = '\0';
601 user_context.assign(user_context_);
602 }
603
604 // Set next server value (siaddr) if non NULL value returned.
605 asiolink::IOAddress next_server = asiolink::IOAddress::IPV4_ZERO_ADDRESS();
606 if (dhcp4_next_server_null_ == MLM_FALSE) {
607 next_server = asiolink::IOAddress(dhcp4_next_server_);
608 }
609
610 // Set server hostname (sname) if non NULL value returned.
611 std::string dhcp4_server_hostname;
612 if (dhcp4_server_hostname_null_ == MLM_FALSE) {
613 dhcp4_server_hostname = std::string(dhcp4_server_hostname_,
614 dhcp4_server_hostname_length_);
615 }
616
617 // Set boot file name (file) if non NULL value returned.
618 std::string dhcp4_boot_file_name;
619 if (dhcp4_boot_file_name_null_ == MLM_FALSE) {
620 dhcp4_boot_file_name = std::string(dhcp4_boot_file_name_,
621 dhcp4_boot_file_name_length_);
622 }
623
624 // Set the auth key if a non empty array is retrieved
625 std::string auth_key;
626 if (auth_key_null_ == MLM_FALSE) {
627 auth_key = std::string(auth_key_, auth_key_length_);
628 }
629
630 // Create and return Host object from the data gathered.
631 HostPtr h(new Host(dhcp_identifier_buffer_, dhcp_identifier_length_,
632 type, ipv4_subnet_id, ipv6_subnet_id, ipv4_reservation,
633 hostname, dhcp4_client_classes, dhcp6_client_classes,
634 next_server, dhcp4_server_hostname,
635 dhcp4_boot_file_name, AuthKey(auth_key)));
636 h->setHostId(host_id_);
637
638 // Set the user context if there is one.
639 if (!user_context.empty()) {
640 try {
641 ConstElementPtr ctx = Element::fromJSON(user_context);
642 if (!ctx || (ctx->getType() != Element::map)) {
643 isc_throw(BadValue, "user context '" << user_context
644 << "' is not a JSON map");
645 }
646 h->setContext(ctx);
647 } catch (const isc::data::JSONError& ex) {
648 isc_throw(BadValue, "user context '" << user_context
649 << "' is invalid JSON: " << ex.what());
650 }
651 }
652
653 return (h);
654 };
655
669 virtual void processFetchedData(ConstHostCollection& hosts) {
670 HostPtr host;
671 // Add new host only if there are no hosts yet or the host id of the
672 // most recently added host is different than the host id of the
673 // currently processed host.
674 if (hosts.empty() || (hosts.back()->getHostId() != getHostId())) {
675 // Create Host object from the fetched data and append it to the
676 // collection.
677 host = retrieveHost();
678 hosts.push_back(host);
679 }
680 }
681
692 std::string getErrorColumns() {
693 return (getColumnsInError(error_, columns_));
694 };
695
696protected:
697
699 size_t columns_num_;
700
702 std::vector<MYSQL_BIND> bind_;
703
705 std::vector<std::string> columns_;
706
708 std::vector<my_bools> error_;
709
712 HostPtr host_;
713
714private:
715
717 uint64_t host_id_;
718
721 uint8_t dhcp_identifier_buffer_[DUID::MAX_DUID_LEN];
722
724 unsigned long dhcp_identifier_length_;
725
728 uint8_t dhcp_identifier_type_;
729
731 uint32_t dhcp4_subnet_id_;
732
734 uint32_t dhcp6_subnet_id_;
735
737 uint32_t ipv4_address_;
738
740 char hostname_[HOSTNAME_MAX_LEN];
741
743 unsigned long hostname_length_;
744
746 char dhcp4_client_classes_[CLIENT_CLASSES_MAX_LEN];
747
750 unsigned long dhcp4_client_classes_length_;
751
753 char dhcp6_client_classes_[CLIENT_CLASSES_MAX_LEN];
754
757 unsigned long dhcp6_client_classes_length_;
758
760 char user_context_[USER_CONTEXT_MAX_LEN];
761
763 unsigned long user_context_length_;
764
766 uint32_t dhcp4_next_server_;
767
769 char dhcp4_server_hostname_[SERVER_HOSTNAME_MAX_LEN];
770
772 unsigned long dhcp4_server_hostname_length_;
773
775 char dhcp4_boot_file_name_[BOOT_FILE_NAME_MAX_LEN];
776
778 unsigned long dhcp4_boot_file_name_length_;
779
781 char auth_key_[TEXT_AUTH_KEY_LEN];
782
784 unsigned long auth_key_length_;
785
788
789
790 my_bool dhcp4_subnet_id_null_;
791
793 my_bool dhcp6_subnet_id_null_;
794
796 my_bool ipv4_address_null_;
797
799 my_bool hostname_null_;
800
803 my_bool dhcp4_client_classes_null_;
804
807 my_bool dhcp6_client_classes_null_;
808
810 my_bool user_context_null_;
811
813 my_bool dhcp4_next_server_null_;
814
816 my_bool dhcp4_server_hostname_null_;
817
819 my_bool dhcp4_boot_file_name_null_;
820
822 my_bool auth_key_null_;
823
825};
826
836class MySqlHostWithOptionsExchange : public MySqlHostExchange {
837private:
838
840 static const size_t OPTION_COLUMNS = 7;
841
856 class OptionProcessor {
857 public:
858
865 OptionProcessor(const Option::Universe& universe,
866 const size_t start_column)
867 : universe_(universe), start_column_(start_column), option_id_(0),
868 code_(0), value_length_(0), formatted_value_length_(0),
869 space_length_(0), persistent_(false), user_context_length_(0),
870 option_id_null_(MLM_FALSE), code_null_(MLM_FALSE),
871 value_null_(MLM_FALSE), formatted_value_null_(MLM_FALSE),
872 space_null_(MLM_FALSE), user_context_null_(MLM_FALSE),
873 option_id_index_(start_column), code_index_(start_column_ + 1),
874 value_index_(start_column_ + 2),
875 formatted_value_index_(start_column_ + 3),
876 space_index_(start_column_ + 4),
877 persistent_index_(start_column_ + 5),
878 user_context_index_(start_column_ + 6),
879 most_recent_option_id_(0) {
880
881 memset(value_, 0, sizeof(value_));
882 memset(formatted_value_, 0, sizeof(formatted_value_));
883 memset(space_, 0, sizeof(space_));
884 memset(user_context_, 0, sizeof(user_context_));
885 }
886
888 uint64_t getOptionId() const {
889 if (option_id_null_ == MLM_FALSE) {
890 return (option_id_);
891 }
892 return (0);
893 }
894
907 void retrieveOption(const CfgOptionPtr& cfg) {
908 // option_id may be NULL if dhcp4_options or dhcp6_options table
909 // doesn't contain any options for the particular host. Also, the
910 // current option id must be greater than id if the most recent
911 // option because options are ordered by option id. Otherwise
912 // we assume that this is already processed option.
913 if ((option_id_null_ == MLM_TRUE) ||
914 (most_recent_option_id_ >= option_id_)) {
915 return;
916 }
917
918 // Remember current option id as the most recent processed one. We
919 // will be comparing it with option ids in subsequent rows.
920 most_recent_option_id_ = option_id_;
921
922 // Convert it to string object for easier comparison.
923 std::string space;
924 if (space_null_ == MLM_FALSE) {
925 // Typically, the string values returned by the database are not
926 // NULL terminated.
927 space_[space_length_] = '\0';
928 space.assign(space_);
929 }
930
931 // If empty or null space provided, use a default top level space.
932 if (space.empty()) {
933 space = (universe_ == Option::V4 ?
935 }
936
937 // Convert formatted_value to string as well.
938 std::string formatted_value;
939 if (formatted_value_null_ == MLM_FALSE) {
940 formatted_value_[formatted_value_length_] = '\0';
941 formatted_value.assign(formatted_value_);
942 }
943
944 // Convert user_context to string as well.
945 std::string user_context;
946 if (user_context_null_ == MLM_FALSE) {
947 user_context_[user_context_length_] = '\0';
948 user_context.assign(user_context_);
949 }
950
951 // Options are held in a binary or textual format in the database.
952 // This is similar to having an option specified in a server
953 // configuration file. Such option is converted to appropriate C++
954 // class, using option definition. Thus, we need to find the
955 // option definition for this option code and option space.
956
957 // Check if this is a standard option.
958 OptionDefinitionPtr def = LibDHCP::getOptionDef(space, code_);
959
960 // Otherwise, we may check if this an option encapsulated within the
961 // vendor space.
962 if (!def && (space != DHCP4_OPTION_SPACE) &&
963 (space != DHCP6_OPTION_SPACE)) {
964 uint32_t vendor_id = LibDHCP::optionSpaceToVendorId(space);
965 if (vendor_id > 0) {
966 def = LibDHCP::getVendorOptionDef(universe_, vendor_id, code_);
967 }
968 }
969
970 // In all other cases, we use runtime option definitions, which
971 // should be also registered within the libdhcp++.
972 if (!def) {
973 def = LibDHCP::getRuntimeOptionDef(space, code_);
974 }
975
976 OptionPtr option;
977
978 if (!def) {
979 // If no definition found, we use generic option type.
980 OptionBuffer buf(value_, value_ + value_length_);
981 option.reset(new Option(universe_, code_, buf.begin(),
982 buf.end()));
983 } else {
984 // The option value may be specified in textual or binary format
985 // in the database. If formatted_value is empty, the binary
986 // format is used. Depending on the format we use a different
987 // variant of the optionFactory function.
988 if (formatted_value.empty()) {
989 OptionBuffer buf(value_, value_ + value_length_);
990 option = def->optionFactory(universe_, code_, buf.begin(),
991 buf.end());
992 } else {
993 // Spit the value specified in comma separated values
994 // format.
995 std::vector<std::string> split_vec;
996 boost::split(split_vec, formatted_value, boost::is_any_of(","));
997 option = def->optionFactory(universe_, code_, split_vec);
998 }
999 }
1000
1001 OptionDescriptor desc(option, persistent_, formatted_value);
1002
1003 // Set the user context if there is one into the option descriptor.
1004 if (!user_context.empty()) {
1005 try {
1006 ConstElementPtr ctx = Element::fromJSON(user_context);
1007 if (!ctx || (ctx->getType() != Element::map)) {
1008 isc_throw(BadValue, "user context '" << user_context
1009 << "' is no a JSON map");
1010 }
1011 desc.setContext(ctx);
1012 } catch (const isc::data::JSONError& ex) {
1013 isc_throw(BadValue, "user context '" << user_context
1014 << "' is invalid JSON: " << ex.what());
1015 }
1016 }
1017
1018 cfg->add(desc, space);
1019 }
1020
1025 void setColumnNames(std::vector<std::string>& columns) {
1026 columns[option_id_index_] = "option_id";
1027 columns[code_index_] = "code";
1028 columns[value_index_] = "value";
1029 columns[formatted_value_index_] = "formatted_value";
1030 columns[space_index_] = "space";
1031 columns[persistent_index_] = "persistent";
1032 columns[user_context_index_] = "user_context";
1033 }
1034
1041 void setBindFields(std::vector<MYSQL_BIND>& bind) {
1042 // This method is called just before making a new query, so we
1043 // reset the most_recent_option_id_ and other exchange members to
1044 // start over with options processing.
1045 most_recent_option_id_ = 0;
1046
1047 option_id_ = 0;
1048 code_ = 0;
1049 persistent_ = false;
1050 option_id_null_ = MLM_FALSE;
1051 code_null_ = MLM_FALSE;
1052 value_null_ = MLM_FALSE;
1053 formatted_value_null_ = MLM_FALSE;
1054 space_null_ = MLM_FALSE;
1055 user_context_null_ = MLM_FALSE;
1056
1057 memset(value_, 0, sizeof(value_));
1058 memset(formatted_value_, 0, sizeof(formatted_value_));
1059 memset(space_, 0, sizeof(space_));
1060 memset(user_context_, 0, sizeof(user_context_));
1061
1062 // option_id : INT UNSIGNED NOT NULL AUTO_INCREMENT,
1063 bind[option_id_index_].buffer_type = MYSQL_TYPE_LONG;
1064 bind[option_id_index_].buffer = reinterpret_cast<char*>(&option_id_);
1065 bind[option_id_index_].is_unsigned = MLM_TRUE;
1066 bind[option_id_index_].is_null = &option_id_null_;
1067
1068 // code : TINYINT OR SHORT UNSIGNED NOT NULL
1069 bind[code_index_].buffer_type = MYSQL_TYPE_SHORT;
1070 bind[code_index_].buffer = reinterpret_cast<char*>(&code_);
1071 bind[code_index_].is_unsigned = MLM_TRUE;
1072 bind[code_index_].is_null = &code_null_;
1073
1074 // value : BLOB NULL
1075 value_length_ = sizeof(value_);
1076 bind[value_index_].buffer_type = MYSQL_TYPE_BLOB;
1077 bind[value_index_].buffer = reinterpret_cast<char*>(value_);
1078 bind[value_index_].buffer_length = value_length_;
1079 bind[value_index_].length = &value_length_;
1080 bind[value_index_].is_null = &value_null_;
1081
1082 // formatted_value : TEXT NULL
1083 formatted_value_length_ = sizeof(formatted_value_);
1084 bind[formatted_value_index_].buffer_type = MYSQL_TYPE_STRING;
1085 bind[formatted_value_index_].buffer = reinterpret_cast<char*>(formatted_value_);
1086 bind[formatted_value_index_].buffer_length = formatted_value_length_;
1087 bind[formatted_value_index_].length = &formatted_value_length_;
1088 bind[formatted_value_index_].is_null = &formatted_value_null_;
1089
1090 // space : VARCHAR(128) NULL
1091 space_length_ = sizeof(space_);
1092 bind[space_index_].buffer_type = MYSQL_TYPE_STRING;
1093 bind[space_index_].buffer = reinterpret_cast<char*>(space_);
1094 bind[space_index_].buffer_length = space_length_;
1095 bind[space_index_].length = &space_length_;
1096 bind[space_index_].is_null = &space_null_;
1097
1098 // persistent : TINYINT(1) NOT NULL DEFAULT 0
1099 bind[persistent_index_].buffer_type = MYSQL_TYPE_TINY;
1100 bind[persistent_index_].buffer = reinterpret_cast<char*>(&persistent_);
1101 bind[persistent_index_].is_unsigned = MLM_TRUE;
1102
1103 // user_context : TEXT NULL
1104 user_context_length_ = sizeof(user_context_);
1105 bind[user_context_index_].buffer_type = MYSQL_TYPE_STRING;
1106 bind[user_context_index_].buffer = reinterpret_cast<char*>(user_context_);
1107 bind[user_context_index_].buffer_length = user_context_length_;
1108 bind[user_context_index_].length = &user_context_length_;
1109 bind[user_context_index_].is_null = &user_context_null_;
1110 }
1111
1112 private:
1113
1115 Option::Universe universe_;
1116
1118 size_t start_column_;
1119
1121 uint32_t option_id_;
1122
1124 uint16_t code_;
1125
1127 uint8_t value_[OPTION_VALUE_MAX_LEN];
1128
1130 unsigned long value_length_;
1131
1133 char formatted_value_[OPTION_FORMATTED_VALUE_MAX_LEN];
1134
1136 unsigned long formatted_value_length_;
1137
1139 char space_[OPTION_SPACE_MAX_LEN];
1140
1142 unsigned long space_length_;
1143
1146 bool persistent_;
1147
1149 char user_context_[USER_CONTEXT_MAX_LEN];
1150
1152 unsigned long user_context_length_;
1153
1156
1157
1158 my_bool option_id_null_;
1159
1161 my_bool code_null_;
1162
1164 my_bool value_null_;
1165
1168 my_bool formatted_value_null_;
1169
1171 my_bool space_null_;
1172
1174 my_bool user_context_null_;
1176
1178
1179
1180 size_t option_id_index_;
1181
1183 size_t code_index_;
1184
1186 size_t value_index_;
1187
1189 size_t formatted_value_index_;
1190
1192 size_t space_index_;
1193
1195 size_t persistent_index_;
1197
1199 size_t user_context_index_;
1200
1202 uint32_t most_recent_option_id_;
1203 };
1204
1206 typedef boost::shared_ptr<OptionProcessor> OptionProcessorPtr;
1207
1208public:
1209
1216 enum FetchedOptions {
1217 DHCP4_ONLY,
1218 DHCP6_ONLY,
1219 DHCP4_AND_DHCP6
1220 };
1221
1230 MySqlHostWithOptionsExchange(const FetchedOptions& fetched_options,
1231 const size_t additional_columns_num = 0)
1232 : MySqlHostExchange(getRequiredColumnsNum(fetched_options)
1233 + additional_columns_num),
1234 opt_proc4_(), opt_proc6_() {
1235
1236 // Create option processor for DHCPv4 options, if required.
1237 if ((fetched_options == DHCP4_ONLY) ||
1238 (fetched_options == DHCP4_AND_DHCP6)) {
1239 opt_proc4_.reset(new OptionProcessor(Option::V4,
1240 findAvailColumn()));
1241 opt_proc4_->setColumnNames(columns_);
1242 }
1243
1244 // Create option processor for DHCPv6 options, if required.
1245 if ((fetched_options == DHCP6_ONLY) ||
1246 (fetched_options == DHCP4_AND_DHCP6)) {
1247 opt_proc6_.reset(new OptionProcessor(Option::V6,
1248 findAvailColumn()));
1249 opt_proc6_->setColumnNames(columns_);
1250 }
1251 }
1252
1261 virtual void processFetchedData(ConstHostCollection& hosts) {
1262 // Holds pointer to the previously parsed host.
1263 HostPtr most_recent_host;
1264 if (!hosts.empty()) {
1265 // Const cast is not very elegant way to deal with it, but
1266 // there is a good reason to use it here. This method is called
1267 // to build a collection of const hosts to be returned to the
1268 // caller. If we wanted to use non-const collection we'd need
1269 // to copy the whole collection before returning it, which has
1270 // performance implications. Alternatively, we could store the
1271 // most recently added host in a class member but this would
1272 // make the code less readable.
1273 most_recent_host = boost::const_pointer_cast<Host>(hosts.back());
1274 }
1275
1276 // If no host has been parsed yet or we're at the row holding next
1277 // host, we create a new host object and put it at the end of the
1278 // list.
1279 if (!most_recent_host || (most_recent_host->getHostId() < getHostId())) {
1280 HostPtr host = retrieveHost();
1281 hosts.push_back(host);
1282 most_recent_host = host;
1283 }
1284
1285 // Parse DHCPv4 options if required to do so.
1286 if (opt_proc4_) {
1287 CfgOptionPtr cfg = most_recent_host->getCfgOption4();
1288 opt_proc4_->retrieveOption(cfg);
1289 }
1290
1291 // Parse DHCPv6 options if required to do so.
1292 if (opt_proc6_) {
1293 CfgOptionPtr cfg = most_recent_host->getCfgOption6();
1294 opt_proc6_->retrieveOption(cfg);
1295 }
1296 }
1297
1301 virtual std::vector<MYSQL_BIND> createBindForReceive() {
1302 // The following call sets bind_ values between 0 and 8.
1303 static_cast<void>(MySqlHostExchange::createBindForReceive());
1304
1305 // Bind variables for DHCPv4 options.
1306 if (opt_proc4_) {
1307 opt_proc4_->setBindFields(bind_);
1308 }
1309
1310 // Bind variables for DHCPv6 options.
1311 if (opt_proc6_) {
1312 opt_proc6_->setBindFields(bind_);
1313 }
1314
1315 // Add the error flags
1316 setErrorIndicators(bind_, error_);
1317
1318 return (bind_);
1319 };
1320
1321private:
1322
1334 static size_t getRequiredColumnsNum(const FetchedOptions& fetched_options) {
1335 return (fetched_options == DHCP4_AND_DHCP6 ? 2 * OPTION_COLUMNS :
1336 OPTION_COLUMNS);
1337 }
1338
1342 OptionProcessorPtr opt_proc4_;
1343
1347 OptionProcessorPtr opt_proc6_;
1348};
1349
1362class MySqlHostIPv6Exchange : public MySqlHostWithOptionsExchange {
1363private:
1364
1366 static const size_t RESERVATION_COLUMNS = 5;
1367
1368public:
1369
1374 MySqlHostIPv6Exchange(const FetchedOptions& fetched_options)
1375 : MySqlHostWithOptionsExchange(fetched_options, RESERVATION_COLUMNS),
1376 reservation_id_(0),
1377 reserv_type_(0), reserv_type_null_(MLM_FALSE),
1378 ipv6_address_buffer_len_(0), prefix_len_(0), iaid_(0),
1379 reservation_id_index_(findAvailColumn()),
1380 address_index_(reservation_id_index_ + 1),
1381 prefix_len_index_(reservation_id_index_ + 2),
1382 type_index_(reservation_id_index_ + 3),
1383 iaid_index_(reservation_id_index_ + 4),
1384 most_recent_reservation_id_(0) {
1385
1386 memset(ipv6_address_buffer_, 0, sizeof(ipv6_address_buffer_));
1387
1388 // Provide names of additional columns returned by the queries.
1389 columns_[reservation_id_index_] = "reservation_id";
1390 columns_[address_index_] = "address";
1391 columns_[prefix_len_index_] = "prefix_len";
1392 columns_[type_index_] = "type";
1393 columns_[iaid_index_] = "dhcp6_iaid";
1394 }
1395
1399 uint32_t getReservationId() const {
1400 if (reserv_type_null_ == MLM_FALSE) {
1401 return (reservation_id_);
1402 }
1403 return (0);
1404 };
1405
1412 IPv6Resrv retrieveReservation() {
1413 // Set the IPv6 Reservation type (0 = IA_NA, 2 = IA_PD)
1414 IPv6Resrv::Type type = IPv6Resrv::TYPE_NA;
1415
1416 switch (reserv_type_) {
1417 case 0:
1418 type = IPv6Resrv::TYPE_NA;
1419 break;
1420
1421 case 2:
1422 type = IPv6Resrv::TYPE_PD;
1423 break;
1424
1425 default:
1427 "invalid IPv6 reservation type returned: "
1428 << static_cast<int>(reserv_type_)
1429 << ". Only 0 or 2 are allowed.");
1430 }
1431
1432 ipv6_address_buffer_[ipv6_address_buffer_len_] = '\0';
1433 std::string address = ipv6_address_buffer_;
1434 IPv6Resrv r(type, IOAddress(address), prefix_len_);
1435 return (r);
1436 };
1437
1457 virtual void processFetchedData(ConstHostCollection& hosts) {
1458
1459 // Call parent class to fetch host information and options.
1460 MySqlHostWithOptionsExchange::processFetchedData(hosts);
1461
1462 if (getReservationId() == 0) {
1463 return;
1464 }
1465
1466 if (hosts.empty()) {
1467 isc_throw(Unexpected, "no host information while retrieving"
1468 " IPv6 reservation");
1469 }
1470 HostPtr host = boost::const_pointer_cast<Host>(hosts.back());
1471
1472 // If we're dealing with a new reservation, let's add it to the
1473 // host.
1474 if (getReservationId() > most_recent_reservation_id_) {
1475 most_recent_reservation_id_ = getReservationId();
1476
1477 if (most_recent_reservation_id_ > 0) {
1478 host->addReservation(retrieveReservation());
1479 }
1480 }
1481 }
1482
1491 virtual std::vector<MYSQL_BIND> createBindForReceive() {
1492 // Reset most recent reservation id value because we're now making
1493 // a new SELECT query.
1494 most_recent_reservation_id_ = 0;
1495
1496 // Bind values supported by parent classes.
1497 static_cast<void>(MySqlHostWithOptionsExchange::createBindForReceive());
1498
1499 // reservation_id : INT UNSIGNED NOT NULL AUTO_INCREMENT
1500 bind_[reservation_id_index_].buffer_type = MYSQL_TYPE_LONG;
1501 bind_[reservation_id_index_].buffer = reinterpret_cast<char*>(&reservation_id_);
1502 bind_[reservation_id_index_].is_unsigned = MLM_TRUE;
1503
1504 // IPv6 address/prefix VARCHAR(39)
1505 ipv6_address_buffer_len_ = sizeof(ipv6_address_buffer_) - 1;
1506 bind_[address_index_].buffer_type = MYSQL_TYPE_STRING;
1507 bind_[address_index_].buffer = ipv6_address_buffer_;
1508 bind_[address_index_].buffer_length = ipv6_address_buffer_len_;
1509 bind_[address_index_].length = &ipv6_address_buffer_len_;
1510
1511 // prefix_len : TINYINT
1512 bind_[prefix_len_index_].buffer_type = MYSQL_TYPE_TINY;
1513 bind_[prefix_len_index_].buffer = reinterpret_cast<char*>(&prefix_len_);
1514 bind_[prefix_len_index_].is_unsigned = MLM_TRUE;
1515
1516 // (reservation) type : TINYINT
1517 reserv_type_null_ = MLM_FALSE;
1518 bind_[type_index_].buffer_type = MYSQL_TYPE_TINY;
1519 bind_[type_index_].buffer = reinterpret_cast<char*>(&reserv_type_);
1520 bind_[type_index_].is_unsigned = MLM_TRUE;
1521 bind_[type_index_].is_null = &reserv_type_null_;
1522
1523 // dhcp6_iaid INT UNSIGNED
1524 bind_[iaid_index_].buffer_type = MYSQL_TYPE_LONG;
1525 bind_[iaid_index_].buffer = reinterpret_cast<char*>(&iaid_);
1526 bind_[iaid_index_].is_unsigned = MLM_TRUE;
1527
1528 // Add the error flags
1529 setErrorIndicators(bind_, error_);
1530
1531 return (bind_);
1532 };
1533
1534private:
1535
1537 uint32_t reservation_id_;
1538
1540 uint8_t reserv_type_;
1541
1546 my_bool reserv_type_null_;
1547
1549 char ipv6_address_buffer_[ADDRESS6_TEXT_MAX_LEN + 1];
1550
1552 unsigned long ipv6_address_buffer_len_;
1553
1555 uint8_t prefix_len_;
1556
1558 uint32_t iaid_;
1559
1561
1562
1563 size_t reservation_id_index_;
1564
1566 size_t address_index_;
1567
1569 size_t prefix_len_index_;
1570
1572 size_t type_index_;
1573
1575 size_t iaid_index_;
1576
1578
1580 uint32_t most_recent_reservation_id_;
1581};
1582
1593class MySqlIPv6ReservationExchange {
1594private:
1595
1597 static const size_t RESRV_COLUMNS = 6;
1598
1599public:
1600
1604 MySqlIPv6ReservationExchange()
1605 : host_id_(0), address_("::"), address_len_(0), prefix_len_(0), type_(0),
1606 iaid_(0), resv_(IPv6Resrv::TYPE_NA, asiolink::IOAddress("::"), 128) {
1607
1608 // Reset error table.
1609 std::fill(&error_[0], &error_[RESRV_COLUMNS], MLM_FALSE);
1610
1611 // Set the column names (for error messages)
1612 columns_[0] = "host_id";
1613 columns_[1] = "address";
1614 columns_[2] = "prefix_len";
1615 columns_[3] = "type";
1616 columns_[4] = "dhcp6_iaid";
1617 BOOST_STATIC_ASSERT(4 < RESRV_COLUMNS);
1618 }
1619
1634 std::vector<MYSQL_BIND> createBindForSend(const IPv6Resrv& resv,
1635 const HostID& id,
1636 const bool unique_ip) {
1637
1638 // Store the values to ensure they remain valid.
1639 resv_ = resv;
1640 host_id_ = id;
1641
1642 // Initialize prior to constructing the array of MYSQL_BIND structures.
1643 // It sets all fields, including is_null, to zero, so we need to set
1644 // is_null only if it should be true. This gives up minor performance
1645 // benefit while being safe approach. For improved readability, the
1646 // code that explicitly sets is_null is there, but is commented out.
1647 memset(bind_, 0, sizeof(bind_));
1648
1649 // Set up the structures for the various components of the host structure.
1650
1651 try {
1652 // address VARCHAR(39)
1653 address_ = resv.getPrefix().toText();
1654 address_len_ = address_.length();
1655 bind_[0].buffer_type = MYSQL_TYPE_BLOB;
1656 bind_[0].buffer = reinterpret_cast<char*>
1657 (const_cast<char*>(address_.c_str()));
1658 bind_[0].buffer_length = address_len_;
1659 bind_[0].length = &address_len_;
1660
1661 // prefix_len tinyint
1662 prefix_len_ = resv.getPrefixLen();
1663 bind_[1].buffer_type = MYSQL_TYPE_TINY;
1664 bind_[1].buffer = reinterpret_cast<char*>(&prefix_len_);
1665 bind_[1].is_unsigned = MLM_TRUE;
1666
1667 // type tinyint
1668 // See lease6_types for values (0 = IA_NA, 1 = IA_TA, 2 = IA_PD)
1669 type_ = resv.getType() == IPv6Resrv::TYPE_NA ? 0 : 2;
1670 bind_[2].buffer_type = MYSQL_TYPE_TINY;
1671 bind_[2].buffer = reinterpret_cast<char*>(&type_);
1672 bind_[2].is_unsigned = MLM_TRUE;
1673
1674 // dhcp6_iaid INT UNSIGNED
1676 iaid_ = 0;
1677 bind_[3].buffer_type = MYSQL_TYPE_LONG;
1678 bind_[3].buffer = reinterpret_cast<char*>(&iaid_);
1679 bind_[3].is_unsigned = MLM_TRUE;
1680
1681 // host_id INT UNSIGNED NOT NULL
1682 bind_[4].buffer_type = MYSQL_TYPE_LONG;
1683 bind_[4].buffer = reinterpret_cast<char*>(&host_id_);
1684 bind_[4].is_unsigned = MLM_TRUE;
1685
1686 } catch (const std::exception& ex) {
1688 "Could not create bind array from IPv6 Reservation: "
1689 << resv_.toText() << ", reason: " << ex.what());
1690 }
1691
1692 // Add the data to the vector. Note the end element is one after the
1693 // end of the array.
1694 // RESRV_COLUMNS -1 as we do not set reservation_id.
1695 std::vector<MYSQL_BIND> vec(&bind_[0], &bind_[RESRV_COLUMNS-1]);
1696
1697 // When checking whether the IP is unique we need to bind the IPv6 address
1698 // and prefix length at the end of the query as it has additional binding
1699 // for the IPv6 address and prefix length.
1700 if (unique_ip) {
1701 vec.push_back(bind_[0]); // address
1702 vec.push_back(bind_[1]); // prefix_len
1703 }
1704
1705 return (vec);
1706 }
1707
1708private:
1709
1711 uint64_t host_id_;
1712
1714 std::string address_;
1715
1717 unsigned long address_len_;
1718
1720 uint8_t prefix_len_;
1721
1723 uint8_t type_;
1724
1726 uint8_t iaid_;
1727
1729 IPv6Resrv resv_;
1730
1732 MYSQL_BIND bind_[RESRV_COLUMNS];
1733
1735 std::string columns_[RESRV_COLUMNS];
1736
1739 my_bool error_[RESRV_COLUMNS];
1740};
1741
1745class MySqlOptionExchange {
1746private:
1747
1749 static const size_t OPTION_COLUMNS = 10;
1750
1751public:
1752
1754 MySqlOptionExchange()
1755 : type_(0), value_len_(0), formatted_value_len_(0), space_(),
1756 space_len_(0), persistent_(false), user_context_(),
1757 user_context_len_(0), subnet_id_(SUBNET_ID_UNUSED),
1758 host_id_(0), option_() {
1759
1760 BOOST_STATIC_ASSERT(9 < OPTION_COLUMNS);
1761 }
1762
1766 std::vector<MYSQL_BIND> createBindForSend(const OptionDescriptor& opt_desc,
1767 const std::string& opt_space,
1768 const Optional<SubnetID>& subnet_id,
1769 const HostID& host_id) {
1770
1771 // Hold pointer to the option to make sure it remains valid until
1772 // we complete a query.
1773 option_ = opt_desc.option_;
1774
1775 memset(bind_, 0, sizeof(bind_));
1776
1777 try {
1778 // option_id: INT UNSIGNED NOT NULL
1779 // The option_id is auto_incremented, so we need to pass the NULL
1780 // value.
1781 bind_[0].buffer_type = MYSQL_TYPE_NULL;
1782
1783 // code: SMALLINT UNSIGNED NOT NULL
1784 type_ = option_->getType();
1785 bind_[1].buffer_type = MYSQL_TYPE_SHORT;
1786 bind_[1].buffer = reinterpret_cast<char*>(&type_);
1787 bind_[1].is_unsigned = MLM_TRUE;
1788
1789 // value: BLOB NULL
1790 if (opt_desc.formatted_value_.empty() &&
1791 (opt_desc.option_->len() > opt_desc.option_->getHeaderLen())) {
1792 // The formatted_value is empty and the option value is
1793 // non-empty so we need to prepare on-wire format for the
1794 // option and store it in the database as a blob.
1795 OutputBuffer buf(opt_desc.option_->len());
1796 opt_desc.option_->pack(buf);
1797 const char* buf_ptr = static_cast<const char*>(buf.getData());
1798 value_.assign(buf_ptr + opt_desc.option_->getHeaderLen(),
1799 buf_ptr + buf.getLength());
1800 value_len_ = value_.size();
1801 bind_[2].buffer_type = MYSQL_TYPE_BLOB;
1802 bind_[2].buffer = &value_[0];
1803 bind_[2].buffer_length = value_len_;
1804 bind_[2].length = &value_len_;
1805
1806 } else {
1807 // No value or formatted_value specified. In this case, the
1808 // value blob is NULL.
1809 value_.clear();
1810 bind_[2].buffer_type = MYSQL_TYPE_NULL;
1811 }
1812
1813 // formatted_value: TEXT NULL,
1814 if (!opt_desc.formatted_value_.empty()) {
1815 formatted_value_len_ = opt_desc.formatted_value_.size();
1816 bind_[3].buffer_type = MYSQL_TYPE_STRING;
1817 bind_[3].buffer = const_cast<char*>(opt_desc.formatted_value_.c_str());
1818 bind_[3].buffer_length = formatted_value_len_;
1819 bind_[3].length = &formatted_value_len_;
1820
1821 } else {
1822 bind_[3].buffer_type = MYSQL_TYPE_NULL;
1823 }
1824
1825 // space: VARCHAR(128) NULL
1826 space_ = opt_space;
1827 space_len_ = space_.size();
1828 bind_[4].buffer_type = MYSQL_TYPE_STRING;
1829 bind_[4].buffer = const_cast<char*>(space_.c_str());
1830 bind_[4].buffer_length = space_len_;
1831 bind_[4].length = &space_len_;
1832
1833 // persistent: TINYINT(1) NOT NULL DEFAULT 0
1834 persistent_ = opt_desc.persistent_;
1835 bind_[5].buffer_type = MYSQL_TYPE_TINY;
1836 bind_[5].buffer = reinterpret_cast<char*>(&persistent_);
1837 bind_[5].is_unsigned = MLM_TRUE;
1838
1839 // user_context: TEST NULL,
1840 ConstElementPtr ctx = opt_desc.getContext();
1841 if (ctx) {
1842 user_context_ = ctx->str();
1843 user_context_len_ = user_context_.size();
1844 bind_[6].buffer_type = MYSQL_TYPE_STRING;
1845 bind_[6].buffer = const_cast<char*>(user_context_.c_str());
1846 bind_[6].buffer_length = user_context_len_;
1847 bind_[6].length = &user_context_len_;
1848 } else {
1849 bind_[6].buffer_type = MYSQL_TYPE_NULL;
1850 }
1851
1852 // dhcp4_subnet_id: INT UNSIGNED NULL
1853 if (!subnet_id.unspecified()) {
1854 subnet_id_ = subnet_id;
1855 bind_[7].buffer_type = MYSQL_TYPE_LONG;
1856 bind_[7].buffer = reinterpret_cast<char*>(subnet_id_);
1857 bind_[7].is_unsigned = MLM_TRUE;
1858
1859 } else {
1860 bind_[7].buffer_type = MYSQL_TYPE_NULL;
1861 }
1862
1863 // host_id: INT UNSIGNED NOT NULL
1864 host_id_ = host_id;
1865 bind_[8].buffer_type = MYSQL_TYPE_LONG;
1866 bind_[8].buffer = reinterpret_cast<char*>(&host_id_);
1867 bind_[8].is_unsigned = MLM_TRUE;
1868
1869 } catch (const std::exception& ex) {
1871 "Could not create bind array for inserting DHCP "
1872 "option: " << option_->toText() << ", reason: "
1873 << ex.what());
1874 }
1875
1876 return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[OPTION_COLUMNS]));
1877 }
1878
1879private:
1880
1882 uint16_t type_;
1883
1885 std::vector<uint8_t> value_;
1886
1888 unsigned long value_len_;
1889
1891 unsigned long formatted_value_len_;
1892
1894 std::string space_;
1895
1897 unsigned long space_len_;
1898
1901 bool persistent_;
1902
1904 std::string user_context_;
1905
1907 unsigned long user_context_len_;
1908
1910 uint32_t subnet_id_;
1911
1913 uint32_t host_id_;
1914
1916 OptionPtr option_;
1917
1919 MYSQL_BIND bind_[OPTION_COLUMNS];
1920};
1921
1922} // namespace
1923
1924namespace isc {
1925namespace dhcp {
1926
1937public:
1938
1945 IOServiceAccessorPtr io_service_accessor,
1946 db::DbCallback db_reconnect_callback);
1947
1952
1955 boost::shared_ptr<MySqlHostWithOptionsExchange> host_ipv4_exchange_;
1956
1959 boost::shared_ptr<MySqlHostIPv6Exchange> host_ipv6_exchange_;
1960
1964 boost::shared_ptr<MySqlHostIPv6Exchange> host_ipv46_exchange_;
1965
1968 boost::shared_ptr<MySqlIPv6ReservationExchange> host_ipv6_reservation_exchange_;
1969
1973 boost::shared_ptr<MySqlOptionExchange> host_option_exchange_;
1974
1977
1980};
1981
1989public:
1990
1992 std::vector<MySqlHostContextPtr> pool_;
1993
1995 std::mutex mutex_;
1996};
1997
1999typedef boost::shared_ptr<MySqlHostContextPool> MySqlHostContextPoolPtr;
2000
2003public:
2004
2014 GET_HOST_DHCPID, // Gets hosts by host identifier
2015 GET_HOST_ADDR, // Gets hosts by IPv4 address
2016 GET_HOST_SUBID4_DHCPID, // Gets host by IPv4 SubnetID, HW address/DUID
2017 GET_HOST_SUBID6_DHCPID, // Gets host by IPv6 SubnetID, HW address/DUID
2018 GET_HOST_SUBID_ADDR, // Gets host by IPv4 SubnetID and IPv4 address
2019 GET_HOST_PREFIX, // Gets host by IPv6 prefix
2020 GET_HOST_SUBID6_ADDR, // Gets host by IPv6 SubnetID and IPv6 prefix
2021 GET_HOST_SUBID4, // Gets hosts by IPv4 SubnetID
2022 GET_HOST_SUBID6, // Gets hosts by IPv6 SubnetID
2023 GET_HOST_HOSTNAME, // Gets hosts by hostname
2024 GET_HOST_HOSTNAME_SUBID4, // Gets hosts by hostname and IPv4 SubnetID
2025 GET_HOST_HOSTNAME_SUBID6, // Gets hosts by hostname and IPv6 SubnetID
2026 GET_HOST_SUBID4_PAGE, // Gets hosts by IPv4 SubnetID beginning by HID
2027 GET_HOST_SUBID6_PAGE, // Gets hosts by IPv6 SubnetID beginning by HID
2028 GET_HOST_PAGE4, // Gets v4 hosts beginning by HID
2029 GET_HOST_PAGE6, // Gets v6 hosts beginning by HID
2030 INSERT_HOST_NON_UNIQUE_IP, // Insert new host to collection with allowing IP duplicates
2031 INSERT_HOST_UNIQUE_IP, // Insert new host to collection with checking for IP duplicates
2032 INSERT_V6_RESRV_NON_UNIQUE,// Insert v6 reservation without checking that it is unique
2033 INSERT_V6_RESRV_UNIQUE, // Insert v6 reservation with checking that it is unique
2034 INSERT_V4_HOST_OPTION, // Insert DHCPv4 option
2035 INSERT_V6_HOST_OPTION, // Insert DHCPv6 option
2036 DEL_HOST_ADDR4, // Delete v4 host (subnet-id, addr4)
2037 DEL_HOST_ADDR6, // Delete v6 host (subnet-id, addr6)
2038 DEL_HOST_SUBID4_ID, // Delete v4 host (subnet-id, ident.type, identifier)
2039 DEL_HOST_SUBID6_ID, // Delete v6 host (subnet-id, ident.type, identifier)
2040 NUM_STATEMENTS // Number of statements
2042
2049
2055
2058
2081 static bool dbReconnect(ReconnectCtlPtr db_reconnect_ctl);
2082
2093
2105 std::pair<uint32_t, uint32_t> getVersion() const;
2106
2117 std::vector<MYSQL_BIND>& bind);
2118
2128 StatementIndex stindex,
2129 MYSQL_BIND* bind);
2130
2136 void addResv(MySqlHostContextPtr& ctx,
2137 const IPv6Resrv& resv,
2138 const HostID& id);
2139
2151 const OptionDescriptor& opt_desc,
2152 const std::string& opt_space,
2153 const Optional<SubnetID>& subnet_id,
2154 const HostID& host_id);
2155
2164 const StatementIndex& stindex,
2165 const ConstCfgOptionPtr& options_cfg,
2166 const uint64_t host_id);
2167
2180 const int status,
2181 const StatementIndex index,
2182 const char* what) const;
2183
2203 StatementIndex stindex,
2204 MYSQL_BIND* bind,
2205 boost::shared_ptr<MySqlHostExchange> exchange,
2206 ConstHostCollection& result,
2207 bool single) const;
2208
2227 const SubnetID& subnet_id,
2228 const Host::IdentifierType& identifier_type,
2229 const uint8_t* identifier_begin,
2230 const size_t identifier_len,
2231 StatementIndex stindex,
2232 boost::shared_ptr<MySqlHostExchange> exchange) const;
2233
2243 void checkReadOnly(MySqlHostContextPtr& ctx) const;
2244
2247
2251
2254
2258
2260 std::string timer_name_;
2261};
2262
2263namespace {
2264
2266typedef boost::array<TaggedStatement, MySqlHostDataSourceImpl::NUM_STATEMENTS>
2267TaggedStatementArray;
2268
2271TaggedStatementArray tagged_statements = { {
2272 // Retrieves host information, IPv6 reservations and both DHCPv4 and
2273 // DHCPv6 options associated with the host. The LEFT JOIN clause is used
2274 // to retrieve information from 4 different tables using a single query.
2275 // Hence, this query returns multiple rows for a single host.
2277 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2278 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, "
2279 "h.hostname, h.dhcp4_client_classes, h.dhcp6_client_classes, "
2280 "h.user_context, "
2281 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2282 "h.dhcp4_boot_file_name, h.auth_key, "
2283 "o4.option_id, o4.code, o4.value, o4.formatted_value, o4.space, "
2284 "o4.persistent, o4.user_context, "
2285 "o6.option_id, o6.code, o6.value, o6.formatted_value, o6.space, "
2286 "o6.persistent, o6.user_context, "
2287 "r.reservation_id, r.address, r.prefix_len, r.type, "
2288 "r.dhcp6_iaid "
2289 "FROM hosts AS h "
2290 "LEFT JOIN dhcp4_options AS o4 "
2291 "ON h.host_id = o4.host_id "
2292 "LEFT JOIN dhcp6_options AS o6 "
2293 "ON h.host_id = o6.host_id "
2294 "LEFT JOIN ipv6_reservations AS r "
2295 "ON h.host_id = r.host_id "
2296 "WHERE dhcp_identifier = ? AND dhcp_identifier_type = ? "
2297 "ORDER BY h.host_id, o4.option_id, o6.option_id, r.reservation_id"},
2298
2299 // Retrieves host information along with the DHCPv4 options associated with
2300 // it. Left joining the dhcp4_options table results in multiple rows being
2301 // returned for the same host. The host is retrieved by IPv4 address.
2303 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2304 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2305 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2306 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2307 "h.dhcp4_boot_file_name, h.auth_key, "
2308 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2309 "o.persistent, o.user_context "
2310 "FROM hosts AS h "
2311 "LEFT JOIN dhcp4_options AS o "
2312 "ON h.host_id = o.host_id "
2313 "WHERE ipv4_address = ? "
2314 "ORDER BY h.host_id, o.option_id"},
2315
2316 // Retrieves host information and DHCPv4 options using subnet identifier
2317 // and client's identifier. Left joining the dhcp4_options table results in
2318 // multiple rows being returned for the same host.
2320 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2321 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2322 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2323 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2324 "h.dhcp4_boot_file_name, h.auth_key, "
2325 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2326 "o.persistent, o.user_context "
2327 "FROM hosts AS h "
2328 "LEFT JOIN dhcp4_options AS o "
2329 "ON h.host_id = o.host_id "
2330 "WHERE h.dhcp4_subnet_id = ? AND h.dhcp_identifier_type = ? "
2331 "AND h.dhcp_identifier = ? "
2332 "ORDER BY h.host_id, o.option_id"},
2333
2334 // Retrieves host information, IPv6 reservations and DHCPv6 options
2335 // associated with a host. The number of rows returned is a multiplication
2336 // of number of IPv6 reservations and DHCPv6 options.
2338 "SELECT h.host_id, h.dhcp_identifier, "
2339 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2340 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2341 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2342 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2343 "h.dhcp4_boot_file_name, h.auth_key, "
2344 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2345 "o.persistent, o.user_context, "
2346 "r.reservation_id, r.address, r.prefix_len, r.type, "
2347 "r.dhcp6_iaid "
2348 "FROM hosts AS h "
2349 "LEFT JOIN dhcp6_options AS o "
2350 "ON h.host_id = o.host_id "
2351 "LEFT JOIN ipv6_reservations AS r "
2352 "ON h.host_id = r.host_id "
2353 "WHERE h.dhcp6_subnet_id = ? AND h.dhcp_identifier_type = ? "
2354 "AND h.dhcp_identifier = ? "
2355 "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2356
2357 // Retrieves host information and DHCPv4 options for the host using subnet
2358 // identifier and IPv4 reservation. Left joining the dhcp4_options table
2359 // results in multiple rows being returned for the host. The number of
2360 // rows depends on the number of options defined for the host.
2362 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2363 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2364 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2365 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2366 "h.dhcp4_boot_file_name, h.auth_key, "
2367 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2368 "o.persistent, o.user_context "
2369 "FROM hosts AS h "
2370 "LEFT JOIN dhcp4_options AS o "
2371 "ON h.host_id = o.host_id "
2372 "WHERE h.dhcp4_subnet_id = ? AND h.ipv4_address = ? "
2373 "ORDER BY h.host_id, o.option_id"},
2374
2375 // Retrieves host information, IPv6 reservations and DHCPv6 options
2376 // associated with a host using prefix and prefix length. This query
2377 // returns host information for a single host. However, multiple rows
2378 // are returned due to left joining IPv6 reservations and DHCPv6 options.
2379 // The number of rows returned is multiplication of number of existing
2380 // IPv6 reservations and DHCPv6 options.
2382 "SELECT h.host_id, h.dhcp_identifier, "
2383 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2384 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2385 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2386 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2387 "h.dhcp4_boot_file_name, h.auth_key, "
2388 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2389 "o.persistent, o.user_context,"
2390 "r.reservation_id, r.address, r.prefix_len, r.type, "
2391 "r.dhcp6_iaid "
2392 "FROM hosts AS h "
2393 "LEFT JOIN dhcp6_options AS o "
2394 "ON h.host_id = o.host_id "
2395 "LEFT JOIN ipv6_reservations AS r "
2396 "ON h.host_id = r.host_id "
2397 "WHERE h.host_id = "
2398 "( SELECT host_id FROM ipv6_reservations "
2399 "WHERE address = ? AND prefix_len = ? ) "
2400 "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2401
2402 // Retrieves host information, IPv6 reservations and DHCPv6 options
2403 // associated with a host using IPv6 subnet id and prefix. This query
2404 // returns host information for a single host. However, multiple rows
2405 // are returned due to left joining IPv6 reservations and DHCPv6 options.
2406 // The number of rows returned is multiplication of number of existing
2407 // IPv6 reservations and DHCPv6 options.
2409 "SELECT h.host_id, h.dhcp_identifier, "
2410 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2411 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2412 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2413 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2414 "h.dhcp4_boot_file_name, h.auth_key, "
2415 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2416 "o.persistent, o.user_context, "
2417 "r.reservation_id, r.address, r.prefix_len, r.type, "
2418 "r.dhcp6_iaid "
2419 "FROM hosts AS h "
2420 "LEFT JOIN dhcp6_options AS o "
2421 "ON h.host_id = o.host_id "
2422 "LEFT JOIN ipv6_reservations AS r "
2423 "ON h.host_id = r.host_id "
2424 "WHERE h.dhcp6_subnet_id = ? AND r.address = ? "
2425 "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2426
2427 // Retrieves host information along with the DHCPv4 options associated with
2428 // it. Left joining the dhcp4_options table results in multiple rows being
2429 // returned for the same host. Hosts are retrieved by IPv4 subnet id.
2431 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2432 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2433 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2434 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2435 "h.dhcp4_boot_file_name, h.auth_key, "
2436 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2437 "o.persistent, o.user_context "
2438 "FROM hosts AS h "
2439 "LEFT JOIN dhcp4_options AS o "
2440 "ON h.host_id = o.host_id "
2441 "WHERE h.dhcp4_subnet_id = ? "
2442 "ORDER BY h.host_id, o.option_id"},
2443
2444 // Retrieves host information, IPv6 reservations and DHCPv6 options
2445 // associated with a host. The number of rows returned is a multiplication
2446 // of number of IPv6 reservations and DHCPv6 options. Hosts are retrieved
2447 // by IPv6 subnet id.
2449 "SELECT h.host_id, h.dhcp_identifier, "
2450 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2451 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2452 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2453 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2454 "h.dhcp4_boot_file_name, h.auth_key, "
2455 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2456 "o.persistent, o.user_context, "
2457 "r.reservation_id, r.address, r.prefix_len, r.type, "
2458 "r.dhcp6_iaid "
2459 "FROM hosts AS h "
2460 "LEFT JOIN dhcp6_options AS o "
2461 "ON h.host_id = o.host_id "
2462 "LEFT JOIN ipv6_reservations AS r "
2463 "ON h.host_id = r.host_id "
2464 "WHERE h.dhcp6_subnet_id = ? "
2465 "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2466
2467 // Retrieves host information, IPv6 reservations and both DHCPv4 and
2468 // DHCPv6 options associated with the host. The LEFT JOIN clause is used
2469 // to retrieve information from 4 different tables using a single query.
2470 // Hence, this query returns multiple rows for a single host.
2472 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2473 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, "
2474 "h.hostname, h.dhcp4_client_classes, h.dhcp6_client_classes, "
2475 "h.user_context, "
2476 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2477 "h.dhcp4_boot_file_name, h.auth_key, "
2478 "o4.option_id, o4.code, o4.value, o4.formatted_value, o4.space, "
2479 "o4.persistent, o4.user_context, "
2480 "o6.option_id, o6.code, o6.value, o6.formatted_value, o6.space, "
2481 "o6.persistent, o6.user_context, "
2482 "r.reservation_id, r.address, r.prefix_len, r.type, "
2483 "r.dhcp6_iaid "
2484 "FROM hosts AS h "
2485 "LEFT JOIN dhcp4_options AS o4 "
2486 "ON h.host_id = o4.host_id "
2487 "LEFT JOIN dhcp6_options AS o6 "
2488 "ON h.host_id = o6.host_id "
2489 "LEFT JOIN ipv6_reservations AS r "
2490 "ON h.host_id = r.host_id "
2491 "WHERE h.hostname = ? "
2492 "ORDER BY h.host_id, o4.option_id, o6.option_id, r.reservation_id"},
2493
2494 // Retrieves host information and DHCPv4 options using hostname and
2495 // subnet identifier. Left joining the dhcp4_options table results in
2496 // multiple rows being returned for the same host.
2498 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2499 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2500 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2501 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2502 "h.dhcp4_boot_file_name, h.auth_key, "
2503 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2504 "o.persistent, o.user_context "
2505 "FROM hosts AS h "
2506 "LEFT JOIN dhcp4_options AS o "
2507 "ON h.host_id = o.host_id "
2508 "WHERE h.hostname = ? AND h.dhcp4_subnet_id = ? "
2509 "ORDER BY h.host_id, o.option_id"},
2510
2511 // Retrieves host information, IPv6 reservations and DHCPv6 options
2512 // using hostname and subnet identifier. The number of rows returned
2513 // is a multiplication of number of IPv6 reservations and DHCPv6 options.
2515 "SELECT h.host_id, h.dhcp_identifier, "
2516 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2517 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2518 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2519 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2520 "h.dhcp4_boot_file_name, h.auth_key, "
2521 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2522 "o.persistent, o.user_context, "
2523 "r.reservation_id, r.address, r.prefix_len, r.type, "
2524 "r.dhcp6_iaid "
2525 "FROM hosts AS h "
2526 "LEFT JOIN dhcp6_options AS o "
2527 "ON h.host_id = o.host_id "
2528 "LEFT JOIN ipv6_reservations AS r "
2529 "ON h.host_id = r.host_id "
2530 "WHERE h.hostname = ? AND h.dhcp6_subnet_id = ? "
2531 "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2532
2533 // Retrieves host information along with the DHCPv4 options associated with
2534 // it. Left joining the dhcp4_options table results in multiple rows being
2535 // returned for the same host. Hosts are retrieved by IPv4 subnet id
2536 // and with a host id greater than the start one.
2537 // The number of hosts returned is lower or equal to the limit.
2539 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2540 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2541 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2542 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2543 "h.dhcp4_boot_file_name, h.auth_key, "
2544 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2545 "o.persistent, o.user_context "
2546 "FROM ( SELECT * FROM hosts AS h "
2547 "WHERE h.dhcp4_subnet_id = ? AND h.host_id > ? "
2548 "ORDER BY h.host_id "
2549 "LIMIT ? ) AS h "
2550 "LEFT JOIN dhcp4_options AS o "
2551 "ON h.host_id = o.host_id "
2552 "ORDER BY h.host_id, o.option_id"},
2553
2554 // Retrieves host information, IPv6 reservations and DHCPv6 options
2555 // associated with a host. The number of rows returned is a multiplication
2556 // of number of IPv6 reservations and DHCPv6 options. Hosts are retrieved
2557 // by IPv6 subnet id and with a host id greater than the start one.
2558 // The number of hosts returned is lower or equal to the limit.
2560 "SELECT h.host_id, h.dhcp_identifier, "
2561 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2562 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2563 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2564 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2565 "h.dhcp4_boot_file_name, h.auth_key, "
2566 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2567 "o.persistent, o.user_context, "
2568 "r.reservation_id, r.address, r.prefix_len, r.type, "
2569 "r.dhcp6_iaid "
2570 "FROM ( SELECT * FROM hosts AS h "
2571 "WHERE h.dhcp6_subnet_id = ? AND h.host_id > ? "
2572 "ORDER BY h.host_id "
2573 "LIMIT ? ) AS h "
2574 "LEFT JOIN dhcp6_options AS o "
2575 "ON h.host_id = o.host_id "
2576 "LEFT JOIN ipv6_reservations AS r "
2577 "ON h.host_id = r.host_id "
2578 "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2579
2580 // Retrieves host information along with the DHCPv4 options associated with
2581 // it. Left joining the dhcp4_options table results in multiple rows being
2582 // returned for the same host. Hosts are retrieved with a host id greater
2583 // than the start one.
2584 // The number of hosts returned is lower or equal to the limit.
2586 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2587 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2588 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2589 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2590 "h.dhcp4_boot_file_name, h.auth_key, "
2591 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2592 "o.persistent, o.user_context "
2593 "FROM ( SELECT * FROM hosts AS h "
2594 "WHERE h.host_id > ? "
2595 "ORDER BY h.host_id "
2596 "LIMIT ? ) AS h "
2597 "LEFT JOIN dhcp4_options AS o "
2598 "ON h.host_id = o.host_id "
2599 "ORDER BY h.host_id, o.option_id"},
2600
2601 // Retrieves host information, IPv6 reservations and DHCPv6 options
2602 // associated with a host. The number of rows returned is a multiplication
2603 // of number of IPv6 reservations and DHCPv6 options. Hosts are retrieved
2604 // with a host id greater than the start one.
2605 // The number of hosts returned is lower or equal to the limit.
2607 "SELECT h.host_id, h.dhcp_identifier, "
2608 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2609 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2610 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2611 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2612 "h.dhcp4_boot_file_name, h.auth_key, "
2613 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2614 "o.persistent, o.user_context, "
2615 "r.reservation_id, r.address, r.prefix_len, r.type, "
2616 "r.dhcp6_iaid "
2617 "FROM ( SELECT * FROM hosts AS h "
2618 "WHERE h.host_id > ? "
2619 "ORDER BY h.host_id "
2620 "LIMIT ? ) AS h "
2621 "LEFT JOIN dhcp6_options AS o "
2622 "ON h.host_id = o.host_id "
2623 "LEFT JOIN ipv6_reservations AS r "
2624 "ON h.host_id = r.host_id "
2625 "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2626
2627 // Inserts a host into the 'hosts' table without checking that there is
2628 // a reservation for the IP address.
2630 "INSERT INTO hosts(host_id, dhcp_identifier, dhcp_identifier_type, "
2631 "dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
2632 "dhcp4_client_classes, dhcp6_client_classes, "
2633 "user_context, dhcp4_next_server, "
2634 "dhcp4_server_hostname, dhcp4_boot_file_name, auth_key) "
2635 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"},
2636
2637 // Inserts a host into the 'hosts' table with checking that reserved IP
2638 // address is unique. The innermost query checks if there is at least
2639 // one host for the given IP/subnet combination. For checking whether
2640 // hosts exists or not it doesn't matter if we select actual columns,
2641 // thus SELECT 1 was used as an optimization to avoid selecting data
2642 // that will be ignored anyway. If the host does not exist the new
2643 // host is inserted. DUAL is a special MySQL table from which we can
2644 // select the values to be inserted. If the host with the given IP
2645 // address already exists the new host won't be inserted. The caller
2646 // can check the number of affected rows to detect that there was
2647 // a duplicate host in the database.
2649 "INSERT INTO hosts(host_id, dhcp_identifier, dhcp_identifier_type, "
2650 "dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
2651 "dhcp4_client_classes, dhcp6_client_classes, "
2652 "user_context, dhcp4_next_server, "
2653 "dhcp4_server_hostname, dhcp4_boot_file_name, auth_key) "
2654 "SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? FROM DUAL "
2655 "WHERE NOT EXISTS ("
2656 "SELECT 1 FROM hosts "
2657 "WHERE ipv4_address = ? AND dhcp4_subnet_id = ? "
2658 "LIMIT 1"
2659 ")"},
2660
2661 // Inserts a single IPv6 reservation into 'reservations' table without
2662 // checking that the inserted reservation is unique.
2664 "INSERT INTO ipv6_reservations(address, prefix_len, type, "
2665 "dhcp6_iaid, host_id) "
2666 "VALUES (?, ?, ?, ?, ?)"},
2667
2668 // Inserts a single IPv6 reservation into 'reservations' table with
2669 // checking that the inserted reservation is unique.
2671 "INSERT INTO ipv6_reservations(address, prefix_len, type, "
2672 "dhcp6_iaid, host_id) "
2673 "SELECT ?, ?, ?, ?, ? FROM DUAL "
2674 "WHERE NOT EXISTS ("
2675 "SELECT 1 FROM ipv6_reservations "
2676 "WHERE address = ? AND prefix_len = ? "
2677 "LIMIT 1"
2678 ")"},
2679
2680 // Inserts a single DHCPv4 option into 'dhcp4_options' table.
2681 // Using fixed scope_id = 3, which associates an option with host.
2683 "INSERT INTO dhcp4_options(option_id, code, value, formatted_value, space, "
2684 "persistent, user_context, dhcp4_subnet_id, host_id, scope_id) "
2685 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 3)"},
2686
2687 // Inserts a single DHCPv6 option into 'dhcp6_options' table.
2688 // Using fixed scope_id = 3, which associates an option with host.
2690 "INSERT INTO dhcp6_options(option_id, code, value, formatted_value, space, "
2691 "persistent, user_context, dhcp6_subnet_id, host_id, scope_id) "
2692 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 3)"},
2693
2694 // Delete IPv4 reservations by subnet id and reserved address.
2696 "DELETE FROM hosts WHERE dhcp4_subnet_id = ? AND ipv4_address = ?"},
2697
2698 // Delete IPv6 reservations by subnet id and reserved address/prefix.
2700 "DELETE h FROM hosts AS h "
2701 "INNER JOIN ipv6_reservations AS r "
2702 "ON h.host_id = r.host_id "
2703 "WHERE h.dhcp6_subnet_id = ? AND r.address = ?"},
2704
2705 // Delete a single IPv4 reservation by subnet id and identifier.
2707 "DELETE FROM hosts WHERE dhcp4_subnet_id = ? AND dhcp_identifier_type=? "
2708 "AND dhcp_identifier = ?"},
2709
2710 // Delete a single IPv6 reservation by subnet id and identifier.
2712 "DELETE FROM hosts WHERE dhcp6_subnet_id = ? AND dhcp_identifier_type=? "
2713 "AND dhcp_identifier = ?"}
2714 }
2715};
2716
2717} // namespace
2718
2719// MySqlHostContext Constructor
2720
2722 IOServiceAccessorPtr io_service_accessor,
2723 db::DbCallback db_reconnect_callback)
2724 : conn_(parameters, io_service_accessor, db_reconnect_callback),
2725 is_readonly_(true) {
2726}
2727
2728// MySqlHostContextAlloc Constructor and Destructor
2729
2731 MySqlHostDataSourceImpl& mgr) : ctx_(), mgr_(mgr) {
2732
2733 if (MultiThreadingMgr::instance().getMode()) {
2734 // multi-threaded
2735 {
2736 // we need to protect the whole pool_ operation, hence extra scope {}
2737 lock_guard<mutex> lock(mgr_.pool_->mutex_);
2738 if (!mgr_.pool_->pool_.empty()) {
2739 ctx_ = mgr_.pool_->pool_.back();
2740 mgr_.pool_->pool_.pop_back();
2741 }
2742 }
2743 if (!ctx_) {
2744 ctx_ = mgr_.createContext();
2745 }
2746 } else {
2747 // single-threaded
2748 if (mgr_.pool_->pool_.empty()) {
2749 isc_throw(Unexpected, "No available MySQL host context?!");
2750 }
2751 ctx_ = mgr_.pool_->pool_.back();
2752 }
2753}
2754
2756 if (MultiThreadingMgr::instance().getMode()) {
2757 // multi-threaded
2758 lock_guard<mutex> lock(mgr_.pool_->mutex_);
2759 mgr_.pool_->pool_.push_back(ctx_);
2760 if (ctx_->conn_.isUnusable()) {
2761 mgr_.unusable_ = true;
2762 }
2763 } else if (ctx_->conn_.isUnusable()) {
2764 mgr_.unusable_ = true;
2765 }
2766}
2767
2769 : parameters_(parameters), ip_reservations_unique_(true), unusable_(false),
2770 timer_name_("") {
2771
2772 // Create unique timer name per instance.
2773 timer_name_ = "MySqlHostMgr[";
2774 timer_name_ += boost::lexical_cast<std::string>(reinterpret_cast<uint64_t>(this));
2775 timer_name_ += "]DbReconnectTimer";
2776
2777 // Validate the schema version first.
2778 std::pair<uint32_t, uint32_t> code_version(MYSQL_SCHEMA_VERSION_MAJOR,
2780 std::pair<uint32_t, uint32_t> db_version = getVersion();
2781 if (code_version != db_version) {
2783 "MySQL schema version mismatch: need version: "
2784 << code_version.first << "." << code_version.second
2785 << " found version: " << db_version.first << "."
2786 << db_version.second);
2787 }
2788
2789 // Create an initial context.
2790 pool_.reset(new MySqlHostContextPool());
2791 pool_->pool_.push_back(createContext());
2792}
2793
2794// Create context.
2795
2801
2802 // Open the database.
2803 ctx->conn_.openDatabase();
2804
2805 // Check if we have TLS when we required it.
2806 if (ctx->conn_.getTls()) {
2807 std::string cipher = ctx->conn_.getTlsCipher();
2808 if (cipher.empty()) {
2810 } else {
2813 .arg(cipher);
2814 }
2815 }
2816
2817 // Prepare query statements. Those are will be only used to retrieve
2818 // information from the database, so they can be used even if the
2819 // database is read only for the current user.
2820 ctx->conn_.prepareStatements(tagged_statements.begin(),
2821 tagged_statements.begin() + WRITE_STMTS_BEGIN);
2822
2823 // Check if the backend is explicitly configured to operate with
2824 // read only access to the database.
2825 ctx->is_readonly_ = ctx->conn_.configuredReadOnly();
2826
2827 // If we are using read-write mode for the database we also prepare
2828 // statements for INSERTS etc.
2829 if (!ctx->is_readonly_) {
2830 // Prepare statements for writing to the database, e.g. INSERT.
2831 ctx->conn_.prepareStatements(tagged_statements.begin() + WRITE_STMTS_BEGIN,
2832 tagged_statements.end());
2833 } else {
2835 }
2836
2837 // Create the exchange objects for use in exchanging data between the
2838 // program and the database.
2839 ctx->host_ipv4_exchange_.reset(new MySqlHostWithOptionsExchange(MySqlHostWithOptionsExchange::DHCP4_ONLY));
2840 ctx->host_ipv6_exchange_.reset(new MySqlHostIPv6Exchange(MySqlHostWithOptionsExchange::DHCP6_ONLY));
2841 ctx->host_ipv46_exchange_.reset(new MySqlHostIPv6Exchange(MySqlHostWithOptionsExchange::DHCP4_AND_DHCP6));
2842 ctx->host_ipv6_reservation_exchange_.reset(new MySqlIPv6ReservationExchange());
2843 ctx->host_option_exchange_.reset(new MySqlOptionExchange());
2844
2845 // Create ReconnectCtl for this connection.
2846 ctx->conn_.makeReconnectCtl(timer_name_);
2847
2848 return (ctx);
2849}
2850
2852}
2853
2854bool
2857
2858 // Invoke application layer connection lost callback.
2859 if (!DatabaseConnection::invokeDbLostCallback(db_reconnect_ctl)) {
2860 return (false);
2861 }
2862
2863 bool reopened = false;
2864
2865 const std::string timer_name = db_reconnect_ctl->timerName();
2866
2867 // At least one connection was lost.
2868 try {
2869 CfgDbAccessPtr cfg_db = CfgMgr::instance().getCurrentCfg()->getCfgDbAccess();
2870 std::list<std::string> host_db_access_list = cfg_db->getHostDbAccessStringList();
2871 for (std::string& hds : host_db_access_list) {
2872 auto parameters = DatabaseConnection::parse(hds);
2873 if (HostMgr::delBackend("mysql", hds, true)) {
2875 }
2876 }
2877 reopened = true;
2878 } catch (const std::exception& ex) {
2880 .arg(ex.what());
2881 }
2882
2883 if (reopened) {
2884 // Cancel the timer.
2885 if (TimerMgr::instance()->isTimerRegistered(timer_name)) {
2886 TimerMgr::instance()->unregisterTimer(timer_name);
2887 }
2888
2889 // Invoke application layer connection recovered callback.
2890 if (!DatabaseConnection::invokeDbRecoveredCallback(db_reconnect_ctl)) {
2891 return (false);
2892 }
2893 } else {
2894 if (!db_reconnect_ctl->checkRetries()) {
2895 // We're out of retries, log it and initiate shutdown.
2897 .arg(db_reconnect_ctl->maxRetries());
2898
2899 // Cancel the timer.
2900 if (TimerMgr::instance()->isTimerRegistered(timer_name)) {
2901 TimerMgr::instance()->unregisterTimer(timer_name);
2902 }
2903
2904 // Invoke application layer connection failed callback.
2906 return (false);
2907 }
2908
2910 .arg(db_reconnect_ctl->maxRetries() - db_reconnect_ctl->retriesLeft() + 1)
2911 .arg(db_reconnect_ctl->maxRetries())
2912 .arg(db_reconnect_ctl->retryInterval());
2913
2914 // Start the timer.
2915 if (!TimerMgr::instance()->isTimerRegistered(timer_name)) {
2916 TimerMgr::instance()->registerTimer(timer_name,
2917 std::bind(&MySqlHostDataSourceImpl::dbReconnect, db_reconnect_ctl),
2918 db_reconnect_ctl->retryInterval(),
2920 }
2921 TimerMgr::instance()->setup(timer_name);
2922 }
2923
2924 return (true);
2925}
2926
2927std::pair<uint32_t, uint32_t>
2931
2933}
2934
2935void
2937 StatementIndex stindex,
2938 std::vector<MYSQL_BIND>& bind) {
2939 // Bind the parameters to the statement
2940 int status = mysql_stmt_bind_param(ctx->conn_.statements_[stindex], &bind[0]);
2941 checkError(ctx, status, stindex, "unable to bind parameters");
2942
2943 // Execute the statement
2944 status = MysqlExecuteStatement(ctx->conn_.statements_[stindex]);
2945
2946 if (status != 0) {
2947 // Failure: check for the special case of duplicate entry.
2948 if (mysql_errno(ctx->conn_.mysql_) == ER_DUP_ENTRY) {
2949 isc_throw(DuplicateEntry, "Database duplicate entry error");
2950 }
2951 checkError(ctx, status, stindex, "unable to execute");
2952 }
2953
2954 // If the number of rows inserted is 0 it means that the query detected
2955 // an attempt to insert duplicated data for which there is no unique
2956 // index in the database. Unique indexes are not created in the database
2957 // when it may be sometimes allowed to insert duplicated records per
2958 // server's configuration.
2959 my_ulonglong numrows = mysql_stmt_affected_rows(ctx->conn_.statements_[stindex]);
2960 if (numrows == 0) {
2961 isc_throw(DuplicateEntry, "Database duplicate entry error");
2962 }
2963}
2964
2965bool
2967 StatementIndex stindex,
2968 MYSQL_BIND* bind) {
2969 // Bind the parameters to the statement
2970 int status = mysql_stmt_bind_param(ctx->conn_.statements_[stindex], &bind[0]);
2971 checkError(ctx, status, stindex, "unable to bind parameters");
2972
2973 // Execute the statement
2974 status = MysqlExecuteStatement(ctx->conn_.statements_[stindex]);
2975
2976 if (status != 0) {
2977 checkError(ctx, status, stindex, "unable to execute");
2978 }
2979
2980 // Let's check how many hosts were deleted.
2981 my_ulonglong numrows = mysql_stmt_affected_rows(ctx->conn_.statements_[stindex]);
2982
2983 return (numrows != 0);
2984}
2985
2986void
2988 const IPv6Resrv& resv,
2989 const HostID& id) {
2990 std::vector<MYSQL_BIND> bind = ctx->host_ipv6_reservation_exchange_->
2991 createBindForSend(resv, id, ip_reservations_unique_);
2992
2994}
2995
2996void
2998 const StatementIndex& stindex,
2999 const OptionDescriptor& opt_desc,
3000 const std::string& opt_space,
3001 const Optional<SubnetID>& subnet_id,
3002 const HostID& id) {
3003 std::vector<MYSQL_BIND> bind = ctx->host_option_exchange_->createBindForSend(opt_desc, opt_space, subnet_id, id);
3004
3005 addStatement(ctx, stindex, bind);
3006}
3007
3008void
3010 const StatementIndex& stindex,
3011 const ConstCfgOptionPtr& options_cfg,
3012 const uint64_t host_id) {
3013 // Get option space names and vendor space names and combine them within a
3014 // single list.
3015 std::list<std::string> option_spaces = options_cfg->getOptionSpaceNames();
3016 std::list<std::string> vendor_spaces = options_cfg->getVendorIdsSpaceNames();
3017 option_spaces.insert(option_spaces.end(), vendor_spaces.begin(),
3018 vendor_spaces.end());
3019
3020 // For each option space retrieve all options and insert them into the
3021 // database.
3022 for (auto space = option_spaces.begin(); space != option_spaces.end(); ++space) {
3023 OptionContainerPtr options = options_cfg->getAll(*space);
3024 if (options && !options->empty()) {
3025 for (auto opt = options->begin(); opt != options->end(); ++opt) {
3026 addOption(ctx, stindex, *opt, *space, Optional<SubnetID>(), host_id);
3027 }
3028 }
3029 }
3030}
3031
3032void
3034 const int status,
3035 const StatementIndex index,
3036 const char* what) const {
3037 ctx->conn_.checkError(status, index, what);
3038}
3039
3040void
3042 StatementIndex stindex,
3043 MYSQL_BIND* bind,
3044 boost::shared_ptr<MySqlHostExchange> exchange,
3045 ConstHostCollection& result,
3046 bool single) const {
3047
3048 // Bind the selection parameters to the statement
3049 int status = mysql_stmt_bind_param(ctx->conn_.statements_[stindex], bind);
3050 checkError(ctx, status, stindex, "unable to bind WHERE clause parameter");
3051
3052 // Set up the MYSQL_BIND array for the data being returned and bind it to
3053 // the statement.
3054 std::vector<MYSQL_BIND> outbind = exchange->createBindForReceive();
3055 status = mysql_stmt_bind_result(ctx->conn_.statements_[stindex], &outbind[0]);
3056 checkError(ctx, status, stindex, "unable to bind SELECT clause parameters");
3057
3058 // Execute the statement
3059 status = MysqlExecuteStatement(ctx->conn_.statements_[stindex]);
3060 checkError(ctx, status, stindex, "unable to execute");
3061
3062 // Ensure that all the lease information is retrieved in one go to avoid
3063 // overhead of going back and forth between client and server.
3064 status = mysql_stmt_store_result(ctx->conn_.statements_[stindex]);
3065 checkError(ctx, status, stindex, "unable to set up for storing all results");
3066
3067 // Set up the fetch "release" object to release resources associated
3068 // with the call to mysql_stmt_fetch when this method exits, then
3069 // retrieve the data. mysql_stmt_fetch return value equal to 0 represents
3070 // successful data fetch.
3071 MySqlFreeResult fetch_release(ctx->conn_.statements_[stindex]);
3072 while ((status = mysql_stmt_fetch(ctx->conn_.statements_[stindex])) ==
3074 try {
3075 exchange->processFetchedData(result);
3076
3077 } catch (const isc::BadValue& ex) {
3078 // Rethrow the exception with a bit more data.
3079 isc_throw(BadValue, ex.what() << ". Statement is <" <<
3080 ctx->conn_.text_statements_[stindex] << ">");
3081 }
3082
3083 if (single && (result.size() > 1)) {
3084 isc_throw(MultipleRecords, "multiple records were found in the "
3085 "database where only one was expected for query "
3086 << ctx->conn_.text_statements_[stindex]);
3087 }
3088 }
3089
3090 // How did the fetch end?
3091 // If mysql_stmt_fetch return value is equal to 1 an error occurred.
3092 if (status == MLM_MYSQL_FETCH_FAILURE) {
3093 // Error - unable to fetch results
3094 checkError(ctx, status, stindex, "unable to fetch results");
3095
3096 } else if (status == MYSQL_DATA_TRUNCATED) {
3097 // Data truncated - throw an exception indicating what was at fault
3098 isc_throw(DataTruncated, ctx->conn_.text_statements_[stindex]
3099 << " returned truncated data: columns affected are "
3100 << exchange->getErrorColumns());
3101 }
3102}
3103
3106 const SubnetID& subnet_id,
3107 const Host::IdentifierType& identifier_type,
3108 const uint8_t* identifier_begin,
3109 const size_t identifier_len,
3110 StatementIndex stindex,
3111 boost::shared_ptr<MySqlHostExchange> exchange) const {
3112
3113 // Set up the WHERE clause value
3114 MYSQL_BIND inbind[3];
3115 memset(inbind, 0, sizeof(inbind));
3116
3117 uint32_t subnet_buffer = static_cast<uint32_t>(subnet_id);
3118 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3119 inbind[0].buffer = reinterpret_cast<char*>(&subnet_buffer);
3120 inbind[0].is_unsigned = MLM_TRUE;
3121
3122 // Identifier value.
3123 std::vector<char> identifier_vec(identifier_begin,
3124 identifier_begin + identifier_len);
3125 unsigned long length = identifier_vec.size();
3126 inbind[2].buffer_type = MYSQL_TYPE_BLOB;
3127 inbind[2].buffer = &identifier_vec[0];
3128 inbind[2].buffer_length = length;
3129 inbind[2].length = &length;
3130
3131 // Identifier type.
3132 char identifier_type_copy = static_cast<char>(identifier_type);
3133 inbind[1].buffer_type = MYSQL_TYPE_TINY;
3134 inbind[1].buffer = reinterpret_cast<char*>(&identifier_type_copy);
3135 inbind[1].is_unsigned = MLM_TRUE;
3136
3137 ConstHostCollection collection;
3138 getHostCollection(ctx, stindex, inbind, exchange, collection, true);
3139
3140 // Return single record if present, else clear the host.
3141 ConstHostPtr result;
3142 if (!collection.empty()) {
3143 result = *collection.begin();
3144 }
3145
3146 return (result);
3147}
3148
3149void
3151 if (ctx->is_readonly_) {
3152 isc_throw(ReadOnlyDb, "MySQL host database backend is configured to"
3153 " operate in read only mode");
3154 }
3155}
3156
3158 : impl_(new MySqlHostDataSourceImpl(parameters)) {
3159}
3160
3162}
3163
3166 return (impl_->parameters_);
3167}
3168
3169void
3171 // Get a context
3172 MySqlHostContextAlloc get_context(*impl_);
3173 MySqlHostContextPtr ctx = get_context.ctx_;
3174
3175 // If operating in read-only mode, throw exception.
3176 impl_->checkReadOnly(ctx);
3177
3178 // Initiate MySQL transaction as we will have to make multiple queries
3179 // to insert host information into multiple tables. If that fails on
3180 // any stage, the transaction will be rolled back by the destructor of
3181 // the MySqlTransaction class.
3182 MySqlTransaction transaction(ctx->conn_);
3183
3184 // If we're configured to check that an IP reservation within a given subnet
3185 // is unique, the IP reservation exists and the subnet is actually set
3186 // we will be using a special query that checks for uniqueness. Otherwise,
3187 // we will use a regular insert statement.
3188 bool unique_ip = impl_->ip_reservations_unique_ && !host->getIPv4Reservation().isV4Zero()
3189 && host->getIPv4SubnetID() != SUBNET_ID_UNUSED;
3190
3191 // Create the MYSQL_BIND array for the host
3192 std::vector<MYSQL_BIND> bind = ctx->host_ipv4_exchange_->createBindForSend(host, unique_ip);
3193
3194 // ... and insert the host.
3195 impl_->addStatement(ctx, unique_ip ? MySqlHostDataSourceImpl::INSERT_HOST_UNIQUE_IP :
3197
3198 // Gets the last inserted hosts id
3199 uint64_t host_id = mysql_insert_id(ctx->conn_.mysql_);
3200
3201 // Insert DHCPv4 options.
3202 ConstCfgOptionPtr cfg_option4 = host->getCfgOption4();
3203 if (cfg_option4) {
3205 cfg_option4, host_id);
3206 }
3207
3208 // Insert DHCPv6 options.
3209 ConstCfgOptionPtr cfg_option6 = host->getCfgOption6();
3210 if (cfg_option6) {
3212 cfg_option6, host_id);
3213 }
3214
3215 // Insert IPv6 reservations.
3216 IPv6ResrvRange v6resv = host->getIPv6Reservations();
3217 if (std::distance(v6resv.first, v6resv.second) > 0) {
3218 for (IPv6ResrvIterator resv = v6resv.first; resv != v6resv.second;
3219 ++resv) {
3220 impl_->addResv(ctx, resv->second, host_id);
3221 }
3222 }
3223
3224 // Everything went fine, so explicitly commit the transaction.
3225 transaction.commit();
3226}
3227
3228bool
3230 const asiolink::IOAddress& addr) {
3231 // Get a context
3232 MySqlHostContextAlloc get_context(*impl_);
3233 MySqlHostContextPtr ctx = get_context.ctx_;
3234
3235 // If operating in read-only mode, throw exception.
3236 impl_->checkReadOnly(ctx);
3237
3238 // Set up the WHERE clause value
3239 MYSQL_BIND inbind[2];
3240
3241 uint32_t subnet = subnet_id;
3242 memset(inbind, 0, sizeof(inbind));
3243 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3244 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3245 inbind[0].is_unsigned = MLM_TRUE;
3246
3247 // v4
3248 if (addr.isV4()) {
3249 uint32_t addr4 = addr.toUint32();
3250 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3251 inbind[1].buffer = reinterpret_cast<char*>(&addr4);
3252 inbind[1].is_unsigned = MLM_TRUE;
3253
3254 return (impl_->delStatement(ctx, MySqlHostDataSourceImpl::DEL_HOST_ADDR4, inbind));
3255 }
3256
3257 // v6
3258 std::string addr_str = addr.toText();
3259 unsigned long addr_len = addr_str.size();
3260 inbind[1].buffer_type = MYSQL_TYPE_BLOB;
3261 inbind[1].buffer = reinterpret_cast<char*>
3262 (const_cast<char*>(addr_str.c_str()));
3263 inbind[1].length = &addr_len;
3264 inbind[1].buffer_length = addr_len;
3265
3266 return (impl_->delStatement(ctx, MySqlHostDataSourceImpl::DEL_HOST_ADDR6, inbind));
3267}
3268
3269bool
3271 const Host::IdentifierType& identifier_type,
3272 const uint8_t* identifier_begin,
3273 const size_t identifier_len) {
3274 // Get a context
3275 MySqlHostContextAlloc get_context(*impl_);
3276 MySqlHostContextPtr ctx = get_context.ctx_;
3277
3278 // If operating in read-only mode, throw exception.
3279 impl_->checkReadOnly(ctx);
3280
3281 // Set up the WHERE clause value
3282 MYSQL_BIND inbind[3];
3283
3284 // subnet-id
3285 memset(inbind, 0, sizeof(inbind));
3286 uint32_t subnet = static_cast<uint32_t>(subnet_id);
3287 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3288 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3289 inbind[0].is_unsigned = MLM_TRUE;
3290
3291 // identifier type
3292 char identifier_type_copy = static_cast<char>(identifier_type);
3293 inbind[1].buffer_type = MYSQL_TYPE_TINY;
3294 inbind[1].buffer = reinterpret_cast<char*>(&identifier_type_copy);
3295 inbind[1].is_unsigned = MLM_TRUE;
3296
3297 // identifier value
3298 std::vector<char> identifier_vec(identifier_begin,
3299 identifier_begin + identifier_len);
3300 unsigned long length = identifier_vec.size();
3301 inbind[2].buffer_type = MYSQL_TYPE_BLOB;
3302 inbind[2].buffer = &identifier_vec[0];
3303 inbind[2].buffer_length = length;
3304 inbind[2].length = &length;
3305
3306 ConstHostCollection collection;
3307 return (impl_->delStatement(ctx, MySqlHostDataSourceImpl::DEL_HOST_SUBID4_ID, inbind));
3308}
3309
3310bool
3312 const Host::IdentifierType& identifier_type,
3313 const uint8_t* identifier_begin,
3314 const size_t identifier_len) {
3315 // Get a context
3316 MySqlHostContextAlloc get_context(*impl_);
3317 MySqlHostContextPtr ctx = get_context.ctx_;
3318
3319 // If operating in read-only mode, throw exception.
3320 impl_->checkReadOnly(ctx);
3321
3322 // Set up the WHERE clause value
3323 MYSQL_BIND inbind[3];
3324
3325 // subnet-id
3326 memset(inbind, 0, sizeof(inbind));
3327 uint32_t subnet = static_cast<uint32_t>(subnet_id);
3328 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3329 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3330 inbind[0].is_unsigned = MLM_TRUE;
3331
3332 // identifier type
3333 char identifier_type_copy = static_cast<char>(identifier_type);
3334 inbind[1].buffer_type = MYSQL_TYPE_TINY;
3335 inbind[1].buffer = reinterpret_cast<char*>(&identifier_type_copy);
3336 inbind[1].is_unsigned = MLM_TRUE;
3337
3338 // identifier value
3339 std::vector<char> identifier_vec(identifier_begin,
3340 identifier_begin + identifier_len);
3341 unsigned long length = identifier_vec.size();
3342 inbind[2].buffer_type = MYSQL_TYPE_BLOB;
3343 inbind[2].buffer = &identifier_vec[0];
3344 inbind[2].buffer_length = length;
3345 inbind[2].length = &length;
3346
3347 ConstHostCollection collection;
3348 return (impl_->delStatement(ctx, MySqlHostDataSourceImpl::DEL_HOST_SUBID6_ID, inbind));
3349}
3350
3353 const uint8_t* identifier_begin,
3354 const size_t identifier_len) const {
3355 // Get a context
3356 MySqlHostContextAlloc get_context(*impl_);
3357 MySqlHostContextPtr ctx = get_context.ctx_;
3358
3359 // Set up the WHERE clause value
3360 MYSQL_BIND inbind[2];
3361 memset(inbind, 0, sizeof(inbind));
3362
3363 // Identifier type.
3364 char identifier_type_copy = static_cast<char>(identifier_type);
3365 inbind[1].buffer = &identifier_type_copy;
3366 inbind[1].buffer_type = MYSQL_TYPE_TINY;
3367 inbind[1].is_unsigned = MLM_TRUE;
3368
3369 // Identifier value.
3370 std::vector<char> identifier_vec(identifier_begin,
3371 identifier_begin + identifier_len);
3372 unsigned long int length = identifier_vec.size();
3373 inbind[0].buffer_type = MYSQL_TYPE_BLOB;
3374 inbind[0].buffer = &identifier_vec[0];
3375 inbind[0].buffer_length = length;
3376 inbind[0].length = &length;
3377
3378 ConstHostCollection result;
3379 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_DHCPID, inbind,
3380 ctx->host_ipv46_exchange_, result, false);
3381
3382 return (result);
3383}
3384
3387 // Get a context
3388 MySqlHostContextAlloc get_context(*impl_);
3389 MySqlHostContextPtr ctx = get_context.ctx_;
3390
3391 // Set up the WHERE clause value
3392 MYSQL_BIND inbind[1];
3393 memset(inbind, 0, sizeof(inbind));
3394 uint32_t subnet = subnet_id;
3395 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3396 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3397 inbind[0].is_unsigned = MLM_TRUE;
3398
3399 ConstHostCollection result;
3400 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID4, inbind,
3401 ctx->host_ipv4_exchange_, result, false);
3402
3403 return (result);
3404}
3405
3408 // Get a context
3409 MySqlHostContextAlloc get_context(*impl_);
3410 MySqlHostContextPtr ctx = get_context.ctx_;
3411
3412 // Set up the WHERE clause value
3413 MYSQL_BIND inbind[1];
3414 memset(inbind, 0, sizeof(inbind));
3415 uint32_t subnet = subnet_id;
3416 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3417 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3418 inbind[0].is_unsigned = MLM_TRUE;
3419
3420 ConstHostCollection result;
3421 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID6, inbind,
3422 ctx->host_ipv6_exchange_, result, false);
3423
3424 return (result);
3425}
3426
3428MySqlHostDataSource::getAllbyHostname(const std::string& hostname) const {
3429 // Get a context
3430 MySqlHostContextAlloc get_context(*impl_);
3431 MySqlHostContextPtr ctx = get_context.ctx_;
3432
3433 // Set up the WHERE clause value
3434 MYSQL_BIND inbind[1];
3435 memset(inbind, 0, sizeof(inbind));
3436
3437 // Hostname
3438 char hostname_[HOSTNAME_MAX_LEN];
3439 strncpy(hostname_, hostname.c_str(), HOSTNAME_MAX_LEN - 1);
3440 unsigned long length = hostname.length();
3441 inbind[0].buffer_type = MYSQL_TYPE_STRING;
3442 inbind[0].buffer = reinterpret_cast<char*>(hostname_);
3443 inbind[0].buffer_length = length;
3444 inbind[0].length = &length;
3445
3446 ConstHostCollection result;
3447 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_HOSTNAME, inbind,
3448 ctx->host_ipv46_exchange_, result, false);
3449
3450 return (result);
3451}
3452
3454MySqlHostDataSource::getAllbyHostname4(const std::string& hostname,
3455 const SubnetID& subnet_id) const {
3456 // Get a context
3457 MySqlHostContextAlloc get_context(*impl_);
3458 MySqlHostContextPtr ctx = get_context.ctx_;
3459
3460 // Set up the WHERE clause value
3461 MYSQL_BIND inbind[2];
3462 memset(inbind, 0, sizeof(inbind));
3463
3464 // Hostname
3465 char hostname_[HOSTNAME_MAX_LEN];
3466 strncpy(hostname_, hostname.c_str(), HOSTNAME_MAX_LEN - 1);
3467 unsigned long length = hostname.length();
3468 inbind[0].buffer_type = MYSQL_TYPE_STRING;
3469 inbind[0].buffer = reinterpret_cast<char*>(hostname_);
3470 inbind[0].buffer_length = length;
3471 inbind[0].length = &length;
3472
3473 // Subnet ID
3474 uint32_t subnet = subnet_id;
3475 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3476 inbind[1].buffer = reinterpret_cast<char*>(&subnet);
3477 inbind[1].is_unsigned = MLM_TRUE;
3478
3479 ConstHostCollection result;
3480 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_HOSTNAME_SUBID4, inbind,
3481 ctx->host_ipv4_exchange_, result, false);
3482
3483 return (result);
3484}
3485
3487MySqlHostDataSource::getAllbyHostname6(const std::string& hostname,
3488 const SubnetID& subnet_id) const {
3489 // Get a context
3490 MySqlHostContextAlloc get_context(*impl_);
3491 MySqlHostContextPtr ctx = get_context.ctx_;
3492
3493 // Set up the WHERE clause value
3494 MYSQL_BIND inbind[2];
3495 memset(inbind, 0, sizeof(inbind));
3496
3497 // Hostname
3498 char hostname_[HOSTNAME_MAX_LEN];
3499 strncpy(hostname_, hostname.c_str(), HOSTNAME_MAX_LEN - 1);
3500 unsigned long length = hostname.length();
3501 inbind[0].buffer_type = MYSQL_TYPE_STRING;
3502 inbind[0].buffer = reinterpret_cast<char*>(hostname_);
3503 inbind[0].buffer_length = length;
3504 inbind[0].length = &length;
3505
3506 // Subnet ID
3507 uint32_t subnet = subnet_id;
3508 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3509 inbind[1].buffer = reinterpret_cast<char*>(&subnet);
3510 inbind[1].is_unsigned = MLM_TRUE;
3511
3512 ConstHostCollection result;
3513 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_HOSTNAME_SUBID6, inbind,
3514 ctx->host_ipv6_exchange_, result, false);
3515
3516 return (result);
3517}
3518
3521 size_t& /*source_index*/,
3522 uint64_t lower_host_id,
3523 const HostPageSize& page_size) const {
3524 // Get a context
3525 MySqlHostContextAlloc get_context(*impl_);
3526 MySqlHostContextPtr ctx = get_context.ctx_;
3527
3528 // Set up the WHERE clause value
3529 MYSQL_BIND inbind[3];
3530 memset(inbind, 0, sizeof(inbind));
3531
3532 // Bind subnet id
3533 uint32_t subnet = subnet_id;
3534 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3535 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3536 inbind[0].is_unsigned = MLM_TRUE;
3537
3538 // Bind lower host id
3539 uint32_t host_id = lower_host_id;
3540 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3541 inbind[1].buffer = reinterpret_cast<char*>(&host_id);
3542 inbind[1].is_unsigned = MLM_TRUE;
3543
3544 // Bind page size value
3545 uint32_t page_size_data = page_size.page_size_;
3546 inbind[2].buffer_type = MYSQL_TYPE_LONG;
3547 inbind[2].buffer = reinterpret_cast<char*>(&page_size_data);
3548 inbind[2].is_unsigned = MLM_TRUE;
3549
3550 ConstHostCollection result;
3551 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID4_PAGE, inbind,
3552 ctx->host_ipv4_exchange_, result, false);
3553
3554 return (result);
3555}
3556
3559 size_t& /*source_index*/,
3560 uint64_t lower_host_id,
3561 const HostPageSize& page_size) const {
3562 // Get a context
3563 MySqlHostContextAlloc get_context(*impl_);
3564 MySqlHostContextPtr ctx = get_context.ctx_;
3565
3566 // Set up the WHERE clause value
3567 MYSQL_BIND inbind[3];
3568 memset(inbind, 0, sizeof(inbind));
3569
3570 // Bind subnet id
3571 uint32_t subnet = subnet_id;
3572 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3573 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3574 inbind[0].is_unsigned = MLM_TRUE;
3575
3576 // Bind lower host id
3577 uint32_t host_id = lower_host_id;
3578 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3579 inbind[1].buffer = reinterpret_cast<char*>(&host_id);
3580 inbind[1].is_unsigned = MLM_TRUE;
3581
3582 // Bind page size value
3583 uint32_t page_size_data = page_size.page_size_;
3584 inbind[2].buffer_type = MYSQL_TYPE_LONG;
3585 inbind[2].buffer = reinterpret_cast<char*>(&page_size_data);
3586 inbind[2].is_unsigned = MLM_TRUE;
3587
3588 ConstHostCollection result;
3589 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID6_PAGE, inbind,
3590 ctx->host_ipv6_exchange_, result, false);
3591
3592 return (result);
3593}
3594
3596MySqlHostDataSource::getPage4(size_t& /*source_index*/,
3597 uint64_t lower_host_id,
3598 const HostPageSize& page_size) const {
3599 // Get a context
3600 MySqlHostContextAlloc get_context(*impl_);
3601 MySqlHostContextPtr ctx = get_context.ctx_;
3602
3603 // Set up the WHERE clause value
3604 MYSQL_BIND inbind[2];
3605 memset(inbind, 0, sizeof(inbind));
3606
3607 // Bind lower host id
3608 uint32_t host_id = lower_host_id;
3609 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3610 inbind[0].buffer = reinterpret_cast<char*>(&host_id);
3611 inbind[0].is_unsigned = MLM_TRUE;
3612
3613 // Bind page size value
3614 uint32_t page_size_data = page_size.page_size_;
3615 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3616 inbind[1].buffer = reinterpret_cast<char*>(&page_size_data);
3617 inbind[1].is_unsigned = MLM_TRUE;
3618
3619 ConstHostCollection result;
3620 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_PAGE4, inbind,
3621 ctx->host_ipv4_exchange_, result, false);
3622
3623 return (result);
3624}
3625
3627MySqlHostDataSource::getPage6(size_t& /*source_index*/,
3628 uint64_t lower_host_id,
3629 const HostPageSize& page_size) const {
3630 // Get a context
3631 MySqlHostContextAlloc get_context(*impl_);
3632 MySqlHostContextPtr ctx = get_context.ctx_;
3633
3634 // Set up the WHERE clause value
3635 MYSQL_BIND inbind[2];
3636 memset(inbind, 0, sizeof(inbind));
3637
3638 // Bind lower host id
3639 uint32_t host_id = lower_host_id;
3640 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3641 inbind[0].buffer = reinterpret_cast<char*>(&host_id);
3642 inbind[0].is_unsigned = MLM_TRUE;
3643
3644 // Bind page size value
3645 uint32_t page_size_data = page_size.page_size_;
3646 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3647 inbind[1].buffer = reinterpret_cast<char*>(&page_size_data);
3648 inbind[1].is_unsigned = MLM_TRUE;
3649
3650 ConstHostCollection result;
3651 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_PAGE6, inbind,
3652 ctx->host_ipv6_exchange_, result, false);
3653
3654 return (result);
3655}
3656
3659 // Get a context
3660 MySqlHostContextAlloc get_context(*impl_);
3661 MySqlHostContextPtr ctx = get_context.ctx_;
3662
3663 // Set up the WHERE clause value
3664 MYSQL_BIND inbind[1];
3665 memset(inbind, 0, sizeof(inbind));
3666
3667 uint32_t addr4 = address.toUint32();
3668 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3669 inbind[0].buffer = reinterpret_cast<char*>(&addr4);
3670 inbind[0].is_unsigned = MLM_TRUE;
3671
3672 ConstHostCollection result;
3673 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_ADDR, inbind,
3674 ctx->host_ipv4_exchange_, result, false);
3675
3676 return (result);
3677}
3678
3681 const Host::IdentifierType& identifier_type,
3682 const uint8_t* identifier_begin,
3683 const size_t identifier_len) const {
3684 // Get a context
3685 MySqlHostContextAlloc get_context(*impl_);
3686 MySqlHostContextPtr ctx = get_context.ctx_;
3687
3688 return (impl_->getHost(ctx, subnet_id, identifier_type, identifier_begin, identifier_len,
3690 ctx->host_ipv4_exchange_));
3691}
3692
3695 const asiolink::IOAddress& address) const {
3696 if (!address.isV4()) {
3697 isc_throw(BadValue, "MySqlHostDataSource::get4(id, address): "
3698 "wrong address type, address supplied is an IPv6 address");
3699 }
3700
3701 // Get a context
3702 MySqlHostContextAlloc get_context(*impl_);
3703 MySqlHostContextPtr ctx = get_context.ctx_;
3704
3705 // Set up the WHERE clause value
3706 MYSQL_BIND inbind[2];
3707 uint32_t subnet = subnet_id;
3708 memset(inbind, 0, sizeof(inbind));
3709 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3710 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3711 inbind[0].is_unsigned = MLM_TRUE;
3712
3713 uint32_t addr4 = address.toUint32();
3714 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3715 inbind[1].buffer = reinterpret_cast<char*>(&addr4);
3716 inbind[1].is_unsigned = MLM_TRUE;
3717
3718 ConstHostCollection collection;
3719 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID_ADDR, inbind,
3720 ctx->host_ipv4_exchange_, collection, true);
3721
3722 // Return single record if present, else clear the host.
3723 ConstHostPtr result;
3724 if (!collection.empty()) {
3725 result = *collection.begin();
3726 }
3727
3728 return (result);
3729}
3730
3733 const asiolink::IOAddress& address) const {
3734 if (!address.isV4()) {
3735 isc_throw(BadValue, "MySqlHostDataSource::getAll4(id, address): "
3736 "wrong address type, address supplied is an IPv6 address");
3737 }
3738
3739 // Get a context
3740 MySqlHostContextAlloc get_context(*impl_);
3741 MySqlHostContextPtr ctx = get_context.ctx_;
3742
3743 // Set up the WHERE clause value
3744 MYSQL_BIND inbind[2];
3745 uint32_t subnet = subnet_id;
3746 memset(inbind, 0, sizeof(inbind));
3747 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3748 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3749 inbind[0].is_unsigned = MLM_TRUE;
3750
3751 uint32_t addr4 = address.toUint32();
3752 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3753 inbind[1].buffer = reinterpret_cast<char*>(&addr4);
3754 inbind[1].is_unsigned = MLM_TRUE;
3755
3756 ConstHostCollection collection;
3757 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID_ADDR, inbind,
3758 ctx->host_ipv4_exchange_, collection, false);
3759 return (collection);
3760}
3761
3764 const Host::IdentifierType& identifier_type,
3765 const uint8_t* identifier_begin,
3766 const size_t identifier_len) const {
3767 // Get a context
3768 MySqlHostContextAlloc get_context(*impl_);
3769 MySqlHostContextPtr ctx = get_context.ctx_;
3770
3771 return (impl_->getHost(ctx, subnet_id, identifier_type, identifier_begin, identifier_len,
3773 ctx->host_ipv6_exchange_));
3774}
3775
3778 const uint8_t prefix_len) const {
3779 if (!prefix.isV6()) {
3780 isc_throw(BadValue, "MySqlHostDataSource::get6(prefix, prefix_len): "
3781 "wrong address type, address supplied is an IPv4 address");
3782 }
3783
3784 // Get a context
3785 MySqlHostContextAlloc get_context(*impl_);
3786 MySqlHostContextPtr ctx = get_context.ctx_;
3787
3788 // Set up the WHERE clause value
3789 MYSQL_BIND inbind[2];
3790 memset(inbind, 0, sizeof(inbind));
3791
3792 std::string addr6 = prefix.toText();
3793 unsigned long addr6_length = addr6.size();
3794
3795 inbind[0].buffer_type = MYSQL_TYPE_BLOB;
3796 inbind[0].buffer = reinterpret_cast<char*>
3797 (const_cast<char*>(addr6.c_str()));
3798 inbind[0].length = &addr6_length;
3799 inbind[0].buffer_length = addr6_length;
3800
3801 uint8_t tmp = prefix_len;
3802 inbind[1].buffer_type = MYSQL_TYPE_TINY;
3803 inbind[1].buffer = reinterpret_cast<char*>(&tmp);
3804 inbind[1].is_unsigned = MLM_TRUE;
3805
3806 ConstHostCollection collection;
3807 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_PREFIX, inbind,
3808 ctx->host_ipv6_exchange_, collection, true);
3809
3810 // Return single record if present, else clear the host.
3811 ConstHostPtr result;
3812 if (!collection.empty()) {
3813 result = *collection.begin();
3814 }
3815
3816 return (result);
3817}
3818
3821 const asiolink::IOAddress& address) const {
3822 if (!address.isV6()) {
3823 isc_throw(BadValue, "MySqlHostDataSource::get6(id, address): "
3824 "wrong address type, address supplied is an IPv4 address");
3825 }
3826
3827 // Get a context
3828 MySqlHostContextAlloc get_context(*impl_);
3829 MySqlHostContextPtr ctx = get_context.ctx_;
3830
3831 // Set up the WHERE clause value
3832 MYSQL_BIND inbind[2];
3833 memset(inbind, 0, sizeof(inbind));
3834
3835 uint32_t subnet_buffer = static_cast<uint32_t>(subnet_id);
3836 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3837 inbind[0].buffer = reinterpret_cast<char*>(&subnet_buffer);
3838 inbind[0].is_unsigned = MLM_TRUE;
3839
3840 std::string addr6 = address.toText();
3841 unsigned long addr6_length = addr6.size();
3842
3843 inbind[1].buffer_type = MYSQL_TYPE_BLOB;
3844 inbind[1].buffer = reinterpret_cast<char*>
3845 (const_cast<char*>(addr6.c_str()));
3846 inbind[1].length = &addr6_length;
3847 inbind[1].buffer_length = addr6_length;
3848
3849 ConstHostCollection collection;
3850 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR, inbind,
3851 ctx->host_ipv6_exchange_, collection, true);
3852
3853 // Return single record if present, else clear the host.
3854 ConstHostPtr result;
3855 if (!collection.empty()) {
3856 result = *collection.begin();
3857 }
3858
3859 return (result);
3860}
3861
3864 const asiolink::IOAddress& address) const {
3865 if (!address.isV6()) {
3866 isc_throw(BadValue, "MySqlHostDataSource::getAll6(id, address): "
3867 "wrong address type, address supplied is an IPv4 address");
3868 }
3869
3870 // Get a context
3871 MySqlHostContextAlloc get_context(*impl_);
3872 MySqlHostContextPtr ctx = get_context.ctx_;
3873
3874 // Set up the WHERE clause value
3875 MYSQL_BIND inbind[2];
3876 memset(inbind, 0, sizeof(inbind));
3877
3878 uint32_t subnet_buffer = static_cast<uint32_t>(subnet_id);
3879 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3880 inbind[0].buffer = reinterpret_cast<char*>(&subnet_buffer);
3881 inbind[0].is_unsigned = MLM_TRUE;
3882
3883 std::string addr6 = address.toText();
3884 unsigned long addr6_length = addr6.size();
3885
3886 inbind[1].buffer_type = MYSQL_TYPE_BLOB;
3887 inbind[1].buffer = reinterpret_cast<char*>
3888 (const_cast<char*>(addr6.c_str()));
3889 inbind[1].length = &addr6_length;
3890 inbind[1].buffer_length = addr6_length;
3891
3892 ConstHostCollection collection;
3893 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR, inbind,
3894 ctx->host_ipv6_exchange_, collection, false);
3895 return (collection);
3896}
3897
3898// Miscellaneous database methods.
3899
3900std::string
3902 std::string name = "";
3903 // Get a context
3904 MySqlHostContextAlloc get_context(*impl_);
3905 MySqlHostContextPtr ctx = get_context.ctx_;
3906
3907 try {
3908 name = ctx->conn_.getParameter("name");
3909 } catch (...) {
3910 // Return an empty name
3911 }
3912 return (name);
3913}
3914
3915std::string
3917 return (std::string("Host data source that stores host information"
3918 "in MySQL database"));
3919}
3920
3921std::pair<uint32_t, uint32_t>
3923 return(impl_->getVersion());
3924}
3925
3926void
3928 // Get a context
3929 MySqlHostContextAlloc get_context(*impl_);
3930 MySqlHostContextPtr ctx = get_context.ctx_;
3931
3932 // If operating in read-only mode, throw exception.
3933 impl_->checkReadOnly(ctx);
3934 if (ctx->conn_.isTransactionStarted()) {
3935 ctx->conn_.commit();
3936 }
3937}
3938
3939void
3941 // Get a context
3942 MySqlHostContextAlloc get_context(*impl_);
3943 MySqlHostContextPtr ctx = get_context.ctx_;
3944
3945 // If operating in read-only mode, throw exception.
3946 impl_->checkReadOnly(ctx);
3947 if (ctx->conn_.isTransactionStarted()) {
3948 ctx->conn_.rollback();
3949 }
3950}
3951
3952bool
3954 impl_->ip_reservations_unique_ = unique;
3955 return (true);
3956}
3957
3958bool
3960 return (impl_->unusable_);
3961}
3962
3963} // namespace dhcp
3964} // namespace isc
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.
A standard Data module exception that is thrown if a parse error is encountered when constructing an ...
Definition: data.h:47
Data is truncated.
Definition: db_exceptions.h:23
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.
std::map< std::string, std::string > ParameterMap
Database configuration parameter map.
Exception thrown on failure to open database.
Exception thrown on failure to execute a database function.
Database duplicate entry error.
Definition: db_exceptions.h:30
Multiple lease records found where one expected.
Definition: db_exceptions.h:16
Common MySQL Connector Pool.
static std::pair< uint32_t, uint32_t > getVersion(const ParameterMap &parameters)
Get the schema version.
Fetch and Release MySQL Results.
RAII object representing MySQL transaction.
void commit()
Commits transaction.
Attempt to modify data in read-only database.
Definition: db_exceptions.h:44
Authentication keys.
Definition: host.h:75
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition: cfgmgr.cc:25
SrvConfigPtr getCurrentCfg()
Returns a pointer to the current configuration.
Definition: cfgmgr.cc:161
static bool delBackend(const std::string &db_type)
Delete an alternate host backend (aka host data source).
Definition: host_mgr.cc:53
static void addBackend(const std::string &access)
Add an alternate host backend (aka host data source).
Definition: host_mgr.cc:48
static isc::asiolink::IOServicePtr & getIOService()
Returns pointer to the IO service.
Definition: host_mgr.h:643
Wraps value holding size of the page with host reservations.
const size_t page_size_
Holds page size.
Represents a device with IPv4 and/or IPv6 reservations.
Definition: host.h:297
IdentifierType
Type of the host identifier.
Definition: host.h:307
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
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.
std::pair< uint32_t, uint32_t > getVersion() const
Returns backend version.
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.
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 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 std::pair< uint32_t, uint32_t > getVersion() const
Returns backend version.
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.
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.
Option descriptor.
Definition: cfg_option.h:42
OptionPtr option_
Option instance.
Definition: cfg_option.h:45
std::string formatted_value_
Option value in textual (CSV) format.
Definition: cfg_option.h:66
bool persistent_
Persistence flag.
Definition: cfg_option.h:51
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:449
RAII class creating a critical section.
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
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
#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:27
const my_bool MLM_FALSE
MySQL false value.
const uint32_t MYSQL_SCHEMA_VERSION_MAJOR
boost::shared_ptr< IOServiceAccessor > IOServiceAccessorPtr
Pointer to an instance of IOServiceAccessor.
@ error
Definition: db_log.h:115
const uint32_t MYSQL_SCHEMA_VERSION_MINOR
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
IPv6ResrvCollection::const_iterator IPv6ResrvIterator
Definition: host.h:241
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:706
boost::shared_ptr< Host > HostPtr
Pointer to the Host object.
Definition: host.h:785
std::vector< ConstHostPtr > ConstHostCollection
Collection of the const Host objects.
Definition: host.h:791
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:24
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:272
const size_t ADDRESS6_TEXT_MAX_LEN
Maximum size of an IPv6 address represented as a text string.
Definition: host.h:32
boost::shared_ptr< const Host > ConstHostPtr
Const pointer to the Host object.
Definition: host.h:788
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:36
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:709
const size_t OPTION_SPACE_MAX_LEN
Maximum length of option space name.
Definition: host.h:51
Definition: edns.h:19
boost::shared_ptr< ReconnectCtl > ReconnectCtlPtr
Pointer to an instance of ReconnectCtl.
Defines the logger used by the top-level component of kea-lfc.
uint16_t code_
#define DHCP4_OPTION_SPACE
global std option spaces
#define DHCP6_OPTION_SPACE
data::ConstElementPtr getContext() const
Returns const pointer to the user context.
Definition: user_context.h:24