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