Kea  1.5.0
pgsql_host_data_source.cc
Go to the documentation of this file.
1 // Copyright (C) 2016-2018 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 
10 #include <dhcp/libdhcp++.h>
11 #include <dhcp/option.h>
12 #include <dhcp/option_definition.h>
13 #include <dhcp/option_space.h>
14 #include <dhcpsrv/cfg_option.h>
15 #include <dhcpsrv/dhcpsrv_log.h>
17 #include <util/buffer.h>
18 #include <util/optional_value.h>
19 
20 #include <boost/algorithm/string/split.hpp>
21 #include <boost/algorithm/string/classification.hpp>
22 #include <boost/array.hpp>
23 #include <boost/pointer_cast.hpp>
24 #include <boost/static_assert.hpp>
25 
26 #include <stdint.h>
27 #include <string>
28 
29 using namespace isc;
30 using namespace isc::asiolink;
31 using namespace isc::db;
32 using namespace isc::dhcp;
33 using namespace isc::util;
34 using namespace isc::data;
35 using namespace std;
36 
37 namespace {
38 
42 const size_t OPTION_VALUE_MAX_LEN = 4096;
43 
48 const uint8_t MAX_IDENTIFIER_TYPE = static_cast<uint8_t>(Host::LAST_IDENTIFIER_TYPE);
49 
51 const size_t DHCP_IDENTIFIER_MAX_LEN = 128;
52 
79 class PgSqlHostExchange : public PgSqlExchange {
80 private:
81 
86  static const int HOST_ID_COL = 0;
87  static const int DHCP_IDENTIFIER_COL = 1;
88  static const int DHCP_IDENTIFIER_TYPE_COL = 2;
89  static const int DHCP4_SUBNET_ID_COL = 3;
90  static const int DHCP6_SUBNET_ID_COL = 4;
91  static const int IPV4_ADDRESS_COL = 5;
92  static const int HOSTNAME_COL = 6;
93  static const int DHCP4_CLIENT_CLASSES_COL = 7;
94  static const int DHCP6_CLIENT_CLASSES_COL = 8;
95  static const int USER_CONTEXT_COL = 9;
96  static const int DHCP4_NEXT_SERVER_COL = 10;
97  static const int DHCP4_SERVER_HOSTNAME_COL = 11;
98  static const int DHCP4_BOOT_FILE_NAME_COL = 12;
99  static const int AUTH_KEY_COL = 13;
101  static const size_t HOST_COLUMNS = 14;
102 
103 public:
104 
111  PgSqlHostExchange(const size_t additional_columns_num = 0)
112  : PgSqlExchange(HOST_COLUMNS + additional_columns_num) {
113  // Set the column names for use by this class. This only comprises
114  // names used by the PgSqlHostExchange class. Derived classes will
115  // need to set names for the columns they use. Currently these are
116  // only used for logging purposes.
117  columns_[HOST_ID_COL] = "host_id";
118  columns_[DHCP_IDENTIFIER_COL] = "dhcp_identifier";
119  columns_[DHCP_IDENTIFIER_TYPE_COL] = "dhcp_identifier_type";
120  columns_[DHCP4_SUBNET_ID_COL] = "dhcp4_subnet_id";
121  columns_[DHCP6_SUBNET_ID_COL] = "dhcp6_subnet_id";
122  columns_[IPV4_ADDRESS_COL] = "ipv4_address";
123  columns_[HOSTNAME_COL] = "hostname";
124  columns_[DHCP4_CLIENT_CLASSES_COL] = "dhcp4_client_classes";
125  columns_[DHCP6_CLIENT_CLASSES_COL] = "dhcp6_client_classes";
126  columns_[USER_CONTEXT_COL] = "user_context";
127  columns_[DHCP4_NEXT_SERVER_COL] = "dhcp4_next_server";
128  columns_[DHCP4_SERVER_HOSTNAME_COL] = "dhcp4_server_hostname";
129  columns_[DHCP4_BOOT_FILE_NAME_COL] = "dhcp4_boot_file_name";
130  columns_[AUTH_KEY_COL] = "auth_key";
131 
132  BOOST_STATIC_ASSERT(12 < HOST_COLUMNS);
133  };
134 
136  virtual ~PgSqlHostExchange() {
137  }
138 
144  virtual void clear() {
145  host_.reset();
146  };
147 
162  size_t findAvailColumn() const {
163  std::vector<std::string>::const_iterator empty_column =
164  std::find(columns_.begin(), columns_.end(), std::string());
165  return (std::distance(columns_.begin(), empty_column));
166  }
167 
172  HostID getHostId(const PgSqlResult& r, int row) {
173  HostID host_id;
174  getColumnValue(r, row, HOST_ID_COL, host_id);
175  return (host_id);
176  }
177 
192  createBindForSend(const HostPtr& host) {
193  if (!host) {
194  isc_throw(BadValue, "createBindForSend:: host object is NULL");
195  }
196 
197  // Store the host to ensure bound values remain in scope
198  host_ = host;
199 
200  // Bind the host data to the array
201  PsqlBindArrayPtr bind_array(new PsqlBindArray());
202  try {
203  // host_id : is auto_incremented skip it
204 
205  // dhcp_identifier : BYTEA NOT NULL
206  bind_array->add(host->getIdentifier());
207 
208  // dhcp_identifier_type : SMALLINT NOT NULL
209  bind_array->add(host->getIdentifierType());
210 
211  // dhcp4_subnet_id : INT NULL
212  if (host->getIPv4SubnetID() == SUBNET_ID_UNUSED) {
213  bind_array->addNull();
214  }
215  else {
216  bind_array->add(host->getIPv4SubnetID());
217  }
218 
219  // dhcp6_subnet_id : INT NULL
220  if (host->getIPv6SubnetID() == SUBNET_ID_UNUSED) {
221  bind_array->addNull();
222  }
223  else {
224  bind_array->add(host->getIPv6SubnetID());
225  }
226 
227  // ipv4_address : BIGINT NULL
228  bind_array->add((host->getIPv4Reservation()));
229 
230  // hostname : VARCHAR(255) NULL
231  bind_array->add(host->getHostname());
232 
233  // dhcp4_client_classes : VARCHAR(255) NULL
234  // Override default separator to not include space after comma.
235  bind_array->addTempString(host->getClientClasses4().toText(","));
236 
237  // dhcp6_client_classes : VARCHAR(255) NULL
238  bind_array->addTempString(host->getClientClasses6().toText(","));
239 
240  // user_context: TEXT NULL
241  ConstElementPtr ctx = host->getContext();
242  if (ctx) {
243  std::string user_context_ = ctx->str();
244  bind_array->addTempString(user_context_);
245  } else {
246  bind_array->addNull();
247  }
248 
249  // dhcp4_next_server : BIGINT NULL
250  bind_array->add((host->getNextServer()));
251 
252  // dhcp4_server_hostname : VARCHAR(64)
253  bind_array->add(host->getServerHostname());
254 
255  // dhcp4_boot_file_name : VARCHAR(128)
256  bind_array->add(host->getBootFileName());
257 
258  // add auth keys
259  std::string key = host->getKey().ToText();
260  if (key.empty()) {
261  bind_array->addNull();
262  } else {
263  bind_array->add(key);
264  }
265 
266  } catch (const std::exception& ex) {
267  host_.reset();
269  "Could not create bind array from Host: "
270  << host->getHostname() << ", reason: " << ex.what());
271  }
272 
273  return (bind_array);
274  };
275 
289  virtual void processRowData(ConstHostCollection& hosts,
290  const PgSqlResult& r, int row) {
291  // Peek at the host id , so we can skip it if we already have it
292  // This lets us avoid constructing a copy of host for each
293  // of its sub-rows (options, etc...)
294  HostID row_host_id = getHostId(r, row);
295 
296  // Add new host only if there are no hosts or the host id of the
297  // most recently added host is different than the host id of the
298  // currently processed host.
299  if (hosts.empty() || row_host_id != hosts.back()->getHostId()) {
300  HostPtr host = retrieveHost(r, row, row_host_id);
301  hosts.push_back(host);
302  }
303  }
304 
315  HostPtr retrieveHost(const PgSqlResult& r, int row,
316  const HostID& peeked_host_id = 0) {
317 
318  // If the caller peeked ahead at the host_id use that, otherwise
319  // read it from the row.
320  HostID host_id = (peeked_host_id ? peeked_host_id : getHostId(r,row));
321 
322  // dhcp_identifier : BYTEA NOT NULL
323  uint8_t identifier_value[DHCP_IDENTIFIER_MAX_LEN];
324  size_t identifier_len;
325  convertFromBytea(r, row, DHCP_IDENTIFIER_COL, identifier_value,
326  sizeof(identifier_value), identifier_len);
327 
328  // dhcp_identifier_type : SMALLINT NOT NULL
329  uint8_t type;
330  getColumnValue(r, row, DHCP_IDENTIFIER_TYPE_COL, type);
331  if (type > MAX_IDENTIFIER_TYPE) {
332  isc_throw(BadValue, "invalid dhcp identifier type returned: "
333  << static_cast<int>(type));
334  }
335 
336  Host::IdentifierType identifier_type =
337  static_cast<Host::IdentifierType>(type);
338 
339  // dhcp4_subnet_id : INT NULL
340  uint32_t subnet_id(SUBNET_ID_UNUSED);
341  if (!isColumnNull(r, row, DHCP4_SUBNET_ID_COL)) {
342  getColumnValue(r, row, DHCP4_SUBNET_ID_COL, subnet_id);
343  }
344  SubnetID dhcp4_subnet_id = static_cast<SubnetID>(subnet_id);
345 
346  // dhcp6_subnet_id : INT NULL
347  subnet_id = SUBNET_ID_UNUSED;
348  if (!isColumnNull(r, row, DHCP6_SUBNET_ID_COL)) {
349  getColumnValue(r, row, DHCP6_SUBNET_ID_COL, subnet_id);
350  }
351  SubnetID dhcp6_subnet_id = static_cast<SubnetID>(subnet_id);
352 
353  // ipv4_address : BIGINT NULL
354  uint32_t addr4(0);
355  if (!isColumnNull(r, row, IPV4_ADDRESS_COL)) {
356  getColumnValue(r, row, IPV4_ADDRESS_COL, addr4);
357  }
358  isc::asiolink::IOAddress ipv4_reservation(addr4);
359 
360  // hostname : VARCHAR(255) NULL
361  std::string hostname;
362  if (!isColumnNull(r, row, HOSTNAME_COL)) {
363  getColumnValue(r, row, HOSTNAME_COL, hostname);
364  }
365 
366  // dhcp4_client_classes : VARCHAR(255) NULL
367  std::string dhcp4_client_classes;
368  if (!isColumnNull(r, row, DHCP4_CLIENT_CLASSES_COL)) {
369  getColumnValue(r, row, DHCP4_CLIENT_CLASSES_COL, dhcp4_client_classes);
370  }
371 
372  // dhcp6_client_classes : VARCHAR(255) NULL
373  std::string dhcp6_client_classes;
374  if (!isColumnNull(r, row, DHCP6_CLIENT_CLASSES_COL)) {
375  getColumnValue(r, row, DHCP6_CLIENT_CLASSES_COL, dhcp6_client_classes);
376  }
377 
378  // user_context: TEXT
379  std::string user_context;
380  if (!isColumnNull(r, row, USER_CONTEXT_COL)) {
381  getColumnValue(r, row, USER_CONTEXT_COL, user_context);
382  }
383 
384  // dhcp4_next_server : BIGINT NULL
385  uint32_t dhcp4_next_server_as_uint32(0);
386  if (!isColumnNull(r, row, DHCP4_NEXT_SERVER_COL)) {
387  getColumnValue(r, row, DHCP4_NEXT_SERVER_COL, dhcp4_next_server_as_uint32);
388  }
389  isc::asiolink::IOAddress dhcp4_next_server(dhcp4_next_server_as_uint32);
390 
391  // dhcp4_server_hostname : VARCHAR(64)
392  std::string dhcp4_server_hostname;
393  if (!isColumnNull(r, row, DHCP4_SERVER_HOSTNAME_COL)) {
394  getColumnValue(r, row, DHCP4_SERVER_HOSTNAME_COL, dhcp4_server_hostname);
395  }
396 
397  // dhcp4_boot_file_name : VARCHAR(128)
398  std::string dhcp4_boot_file_name;
399  if (!isColumnNull(r, row, DHCP4_BOOT_FILE_NAME_COL)) {
400  getColumnValue(r, row, DHCP4_BOOT_FILE_NAME_COL, dhcp4_boot_file_name);
401  }
402 
403  // auth_key : VARCHAR(16)
404  std::string auth_key;
405  if (!isColumnNull(r, row, AUTH_KEY_COL)) {
406  getColumnValue(r, row, AUTH_KEY_COL, auth_key);
407  }
408 
409  // Finally, attempt to create the new host.
410  HostPtr host;
411  try {
412  host.reset(new Host(identifier_value, identifier_len,
413  identifier_type, dhcp4_subnet_id,
414  dhcp6_subnet_id, ipv4_reservation, hostname,
415  dhcp4_client_classes, dhcp6_client_classes,
416  dhcp4_next_server, dhcp4_server_hostname,
417  dhcp4_boot_file_name, AuthKey(auth_key)));
418 
419  // Set the user context if there is one.
420  if (!user_context.empty()) {
421  try {
422  ConstElementPtr ctx = Element::fromJSON(user_context);
423  if (!ctx || (ctx->getType() != Element::map)) {
424  isc_throw(BadValue, "user context '" << user_context
425  << "' is not a JSON map");
426  }
427  host->setContext(ctx);
428  } catch (const isc::data::JSONError& ex) {
429  isc_throw(BadValue, "user context '" << user_context
430  << "' is invalid JSON: " << ex.what());
431  }
432  }
433 
434  host->setHostId(host_id);
435  } catch (const isc::Exception& ex) {
436  isc_throw(DbOperationError, "Could not create host: " << ex.what());
437  }
438 
439  return(host);
440  };
441 
442 protected:
445  HostPtr host_;
446 };
447 
457 class PgSqlHostWithOptionsExchange : public PgSqlHostExchange {
458 private:
459 
461  static const size_t OPTION_COLUMNS = 7;
462 
477  class OptionProcessor {
478  public:
479 
486  OptionProcessor(const Option::Universe& universe,
487  const size_t start_column)
488  : universe_(universe), start_column_(start_column),
489  option_id_index_(start_column), code_index_(start_column_ + 1),
490  value_index_(start_column_ + 2),
491  formatted_value_index_(start_column_ + 3),
492  space_index_(start_column_ + 4),
493  persistent_index_(start_column_ + 5),
494  user_context_index_(start_column_ + 6),
495  most_recent_option_id_(0) {
496  }
497 
502  void clear() {
503  most_recent_option_id_ = 0;
504  }
505 
538  void retrieveOption(const CfgOptionPtr& cfg, const PgSqlResult& r,
539  int row) {
540  // If the option id on this row is NULL, then there's no
541  // option of this type (4/6) on this row to fetch, so bail.
542  if (PgSqlExchange::isColumnNull(r, row, option_id_index_)) {
543  return;
544  }
545 
546  // option_id: INT
547  uint64_t option_id;
548  PgSqlExchange::getColumnValue(r, row, option_id_index_, option_id);
549 
550  // The row option id must be greater than id if the most recent
551  // option because they are ordered by option id. Otherwise
552  // we assume that we have already processed this option.
553  if (most_recent_option_id_ >= option_id) {
554  return;
555  }
556 
557  // Remember current option id as the most recent processed one. We
558  // will be comparing it with option ids in subsequent rows.
559  most_recent_option_id_ = option_id;
560 
561  // code: SMALLINT NOT NULL
562  uint16_t code;
563  PgSqlExchange::getColumnValue(r, row, code_index_, code);
564 
565  // value: BYTEA
566  uint8_t value[OPTION_VALUE_MAX_LEN];
567  size_t value_len(0);
568  if (!isColumnNull(r, row, value_index_)) {
569  PgSqlExchange::convertFromBytea(r, row, value_index_, value,
570  sizeof(value), value_len);
571  }
572 
573  // formatted_value: TEXT
574  std::string formatted_value;
575  if (!isColumnNull(r, row, formatted_value_index_)) {
576  PgSqlExchange::getColumnValue(r, row, formatted_value_index_,
577  formatted_value);
578  }
579 
580  // space: VARCHAR(128)
581  std::string space;
582  if (!isColumnNull(r, row, space_index_)) {
583  PgSqlExchange::getColumnValue(r, row, space_index_, space);
584  }
585 
586  // If empty or null space provided, use a default top level space.
587  if (space.empty()) {
588  space = (universe_ == Option::V4 ? "dhcp4" : "dhcp6");
589  }
590 
591  // persistent: BOOL default false
592  bool persistent;
593  PgSqlExchange::getColumnValue(r, row, persistent_index_,
594  persistent);
595 
596  // user_context: TEXT
597  std::string user_context;
598  if (!isColumnNull(r, row, user_context_index_)) {
599  PgSqlExchange::getColumnValue(r, row, user_context_index_,
600  user_context);
601  }
602 
603  // Options are held in a binary or textual format in the database.
604  // This is similar to having an option specified in a server
605  // configuration file. Such option is converted to appropriate C++
606  // class, using option definition. Thus, we need to find the
607  // option definition for this option code and option space.
608 
609  // If the option space is a standard DHCPv4 or DHCPv6 option space,
610  // this is most likely a standard option, for which we have a
611  // definition created within libdhcp++.
612  OptionDefinitionPtr def = LibDHCP::getOptionDef(space, code);
613 
614  // Otherwise, we may check if this an option encapsulated within the
615  // vendor space.
616  if (!def && (space != DHCP4_OPTION_SPACE) &&
617  (space != DHCP6_OPTION_SPACE)) {
618  uint32_t vendor_id = LibDHCP::optionSpaceToVendorId(space);
619  if (vendor_id > 0) {
620  def = LibDHCP::getVendorOptionDef(universe_, vendor_id,
621  code);
622  }
623  }
624 
625  // In all other cases, we use runtime option definitions, which
626  // should be also registered within the libdhcp++.
627  if (!def) {
628  def = LibDHCP::getRuntimeOptionDef(space, code);
629  }
630 
631  OptionPtr option;
632 
633  if (!def) {
634  // If no definition found, we use generic option type.
635  OptionBuffer buf(value, value + value_len);
636  option.reset(new Option(universe_, code, buf.begin(),
637  buf.end()));
638  } else {
639  // The option value may be specified in textual or binary format
640  // in the database. If formatted_value is empty, the binary
641  // format is used. Depending on the format we use a different
642  // variant of the optionFactory function.
643  if (formatted_value.empty()) {
644  OptionBuffer buf(value, value + value_len);
645  option = def->optionFactory(universe_, code, buf.begin(),
646  buf.end());
647  } else {
648  // Spit the value specified in comma separated values
649  // format.
650  std::vector<std::string> split_vec;
651  boost::split(split_vec, formatted_value,
652  boost::is_any_of(","));
653  option = def->optionFactory(universe_, code, split_vec);
654  }
655  }
656 
657  OptionDescriptor desc(option, persistent, formatted_value);
658 
659  // Set the user context if there is one into the option descriptor.
660  if (!user_context.empty()) {
661  try {
662  ConstElementPtr ctx = Element::fromJSON(user_context);
663  if (!ctx || (ctx->getType() != Element::map)) {
664  isc_throw(BadValue, "user context '" << user_context
665  << "' is no a JSON map");
666  }
667  desc.setContext(ctx);
668  } catch (const isc::data::JSONError& ex) {
669  isc_throw(BadValue, "user context '" << user_context
670  << "' is invalid JSON: " << ex.what());
671  }
672  }
673 
674  cfg->add(desc, space);
675  }
676 
681  void setColumnNames(std::vector<std::string>& columns) {
682  columns[option_id_index_] = "option_id";
683  columns[code_index_] = "code";
684  columns[value_index_] = "value";
685  columns[formatted_value_index_] = "formatted_value";
686  columns[space_index_] = "space";
687  columns[persistent_index_] = "persistent";
688  columns[user_context_index_] = "user_context";
689  }
690 
691  private:
693  Option::Universe universe_;
694 
696  size_t start_column_;
697 
699 
701 
702  size_t option_id_index_;
704 
706  size_t code_index_;
707 
709  size_t value_index_;
710 
712  size_t formatted_value_index_;
713 
715  size_t space_index_;
716 
718  size_t persistent_index_;
720 
722  size_t user_context_index_;
723 
725  uint64_t most_recent_option_id_;
726  };
727 
729  typedef boost::shared_ptr<OptionProcessor> OptionProcessorPtr;
730 
731 public:
732 
739  enum FetchedOptions {
740  DHCP4_ONLY,
741  DHCP6_ONLY,
742  DHCP4_AND_DHCP6
743  };
744 
753  PgSqlHostWithOptionsExchange(const FetchedOptions& fetched_options,
754  const size_t additional_columns_num = 0)
755  : PgSqlHostExchange(getRequiredColumnsNum(fetched_options)
756  + additional_columns_num),
757  opt_proc4_(), opt_proc6_() {
758 
759  // Create option processor for DHCPv4 options, if required.
760  if ((fetched_options == DHCP4_ONLY) ||
761  (fetched_options == DHCP4_AND_DHCP6)) {
762  opt_proc4_.reset(new OptionProcessor(Option::V4,
763  findAvailColumn()));
764  opt_proc4_->setColumnNames(columns_);
765  }
766 
767  // Create option processor for DHCPv6 options, if required.
768  if ((fetched_options == DHCP6_ONLY) ||
769  (fetched_options == DHCP4_AND_DHCP6)) {
770  opt_proc6_.reset(new OptionProcessor(Option::V6,
771  findAvailColumn()));
772  opt_proc6_->setColumnNames(columns_);
773  }
774  }
775 
781  virtual void clear() {
782  PgSqlHostExchange::clear();
783  if (opt_proc4_) {
784  opt_proc4_->clear();
785  }
786 
787  if (opt_proc6_) {
788  opt_proc6_->clear();
789  }
790  }
791 
801  virtual void processRowData(ConstHostCollection& hosts,
802  const PgSqlResult& r, int row) {
803  HostPtr current_host;
804  if (hosts.empty()) {
805  // Must be the first one, fetch it.
806  current_host = retrieveHost(r, row);
807  hosts.push_back(current_host);
808  } else {
809  // Peek at the host id so we can skip it if we already have
810  // this host. This lets us avoid retrieving the host needlessly
811  // for each of its sub-rows (options, etc...).
812  HostID row_host_id = getHostId(r, row);
813  current_host = boost::const_pointer_cast<Host>(hosts.back());
814 
815  // if the row's host id is greater than the one we've been
816  // working on we're starting a new host, so fetch it.
817  if (row_host_id > current_host->getHostId()) {
818  current_host = retrieveHost(r, row, row_host_id);
819  hosts.push_back(current_host);
820  }
821  }
822 
823  // Parse DHCPv4 options if required to do so.
824  if (opt_proc4_) {
825  CfgOptionPtr cfg = current_host->getCfgOption4();
826  opt_proc4_->retrieveOption(cfg, r, row);
827  }
828 
829  // Parse DHCPv6 options if required to do so.
830  if (opt_proc6_) {
831  CfgOptionPtr cfg = current_host->getCfgOption6();
832  opt_proc6_->retrieveOption(cfg, r, row);
833  }
834  }
835 
836 private:
837 
849  static size_t getRequiredColumnsNum(const FetchedOptions& fetched_options) {
850  return (fetched_options == DHCP4_AND_DHCP6 ? 2 * OPTION_COLUMNS :
851  OPTION_COLUMNS);
852  }
853 
857  OptionProcessorPtr opt_proc4_;
858 
862  OptionProcessorPtr opt_proc6_;
863 };
864 
877 class PgSqlHostIPv6Exchange : public PgSqlHostWithOptionsExchange {
878 private:
879 
881  static const size_t RESERVATION_COLUMNS = 5;
882 
883 public:
884 
889  PgSqlHostIPv6Exchange(const FetchedOptions& fetched_options)
890  : PgSqlHostWithOptionsExchange(fetched_options, RESERVATION_COLUMNS),
891  reservation_id_index_(findAvailColumn()),
892  address_index_(reservation_id_index_ + 1),
893  prefix_len_index_(reservation_id_index_ + 2),
894  type_index_(reservation_id_index_ + 3),
895  iaid_index_(reservation_id_index_ + 4),
896  most_recent_reservation_id_(0) {
897 
898  // Provide names of additional columns returned by the queries.
899  columns_[reservation_id_index_] = "reservation_id";
900  columns_[address_index_] = "address";
901  columns_[prefix_len_index_] = "prefix_len";
902  columns_[type_index_] = "type";
903  columns_[iaid_index_] = "dhcp6_iaid";
904 
905  BOOST_STATIC_ASSERT(4 < RESERVATION_COLUMNS);
906  }
907 
913  void clear() {
914  PgSqlHostWithOptionsExchange::clear();
915  most_recent_reservation_id_ = 0;
916  }
917 
921  uint64_t getReservationId(const PgSqlResult& r, int row) const {
922  uint64_t resv_id = 0;
923  if (!isColumnNull(r, row, reservation_id_index_)) {
924  getColumnValue(r, row, reservation_id_index_, resv_id);
925  }
926 
927  return (resv_id);
928  };
929 
934  IPv6Resrv retrieveReservation(const PgSqlResult& r, int row) {
935 
936  // type: SMALLINT NOT NULL
937  uint16_t tmp;
938  getColumnValue(r, row, type_index_, tmp);
939 
940  // Convert it to IPv6 Reservation type (0 = IA_NA, 2 = IA_PD)
941  IPv6Resrv::Type resv_type;
942  switch (tmp) {
943  case 0:
944  resv_type = IPv6Resrv::TYPE_NA;
945  break;
946 
947  case 2:
948  resv_type = IPv6Resrv::TYPE_PD;
949  break;
950 
951  default:
953  "invalid IPv6 reservation type returned: "
954  << tmp << ". Only 0 or 2 are allowed.");
955  }
956 
957  // address VARCHAR(39) NOT NULL
958  isc::asiolink::IOAddress address(getIPv6Value(r, row, address_index_));
959 
960  // prefix_len: SMALLINT NOT NULL
961  uint16_t prefix_len;
962  getColumnValue(r, row, prefix_len_index_, prefix_len);
963 
964  // @todo once we support populating iaid
965  // iaid: INT
966  // int iaid;
967  // getColumnValue(r, row, iaid_index_, iaid);
968 
969  // Create the reservation.
970  IPv6Resrv reservation(resv_type, IOAddress(address), prefix_len);
971  return (reservation);
972  };
973 
995  virtual void processRowData(ConstHostCollection& hosts,
996  const PgSqlResult& r, int row) {
997  // Call parent class to fetch host information and options.
998  PgSqlHostWithOptionsExchange::processRowData(hosts, r, row);
999 
1000  // Shouldn't happen but just in case
1001  if (hosts.empty()) {
1002  isc_throw(Unexpected, "no host information while retrieving"
1003  " IPv6 reservation");
1004  }
1005 
1006  // If we have reservation id we havent' seen yet, retrieve the
1007  // the reservation, adding it to the current host
1008  uint64_t reservation_id = getReservationId(r, row);
1009  if (reservation_id && (reservation_id > most_recent_reservation_id_)) {
1010  HostPtr host = boost::const_pointer_cast<Host>(hosts.back());
1011  host->addReservation(retrieveReservation(r, row));
1012  most_recent_reservation_id_ = reservation_id;
1013  }
1014  }
1015 
1016 private:
1018 
1019  size_t reservation_id_index_;
1021 
1023  size_t address_index_;
1024 
1026  size_t prefix_len_index_;
1027 
1029  size_t type_index_;
1030 
1032  size_t iaid_index_;
1033 
1035 
1037  uint64_t most_recent_reservation_id_;
1038 };
1039 
1050 class PgSqlIPv6ReservationExchange : public PgSqlExchange {
1051 private:
1052 
1054  static const size_t RESRV_COLUMNS = 6;
1055 
1056 public:
1057 
1061  PgSqlIPv6ReservationExchange()
1062  : PgSqlExchange(RESRV_COLUMNS),
1063  resv_(IPv6Resrv::TYPE_NA, asiolink::IOAddress("::"), 128) {
1064  // Set the column names (for error messages)
1065  columns_[0] = "host_id";
1066  columns_[1] = "address";
1067  columns_[2] = "prefix_len";
1068  columns_[3] = "type";
1069  columns_[4] = "dhcp6_iaid";
1070  BOOST_STATIC_ASSERT(5 < RESRV_COLUMNS);
1071  }
1072 
1085  PsqlBindArrayPtr createBindForSend(const IPv6Resrv& resv,
1086  const HostID& host_id) {
1087  // Store the values to ensure they remain valid.
1088  // Technically we don't need this, as currently all the values
1089  // are converted to strings and stored by the bind array.
1090  resv_ = resv;
1091 
1092  PsqlBindArrayPtr bind_array(new PsqlBindArray());
1093 
1094  try {
1095  // address VARCHAR(39) NOT NULL
1096  bind_array->add(resv.getPrefix());
1097 
1098  // prefix_len: SMALLINT NOT NULL
1099  bind_array->add(resv.getPrefixLen());
1100 
1101  // type: SMALLINT NOT NULL
1102  // See lease6_types table for values (0 = IA_NA, 2 = IA_PD)
1103  uint16_t type = resv.getType() == IPv6Resrv::TYPE_NA ? 0 : 2;
1104  bind_array->add(type);
1105 
1106  // dhcp6_iaid: INT UNSIGNED
1108  bind_array->addNull();
1109 
1110  // host_id: BIGINT NOT NULL
1111  bind_array->add(host_id);
1112  } catch (const std::exception& ex) {
1114  "Could not create bind array from IPv6 Reservation: "
1115  << resv_.toText() << ", reason: " << ex.what());
1116  }
1117 
1118  return (bind_array);
1119  }
1120 
1121 private:
1123  IPv6Resrv resv_;
1124 };
1125 
1129 class PgSqlOptionExchange : public PgSqlExchange {
1130 private:
1131 
1132  static const int OPTION_ID_COL = 0;
1133  static const int CODE_COL = 1;
1134  static const int VALUE_COL = 2;
1135  static const int FORMATTED_VALUE_COL = 3;
1136  static const int SPACE_COL = 4;
1137  static const int PERSISTENT_COL = 5;
1138  static const int USER_CONTEXT_COL = 6;
1139  static const int DHCP_CLIENT_CLASS_COL = 7;
1140  static const int DHCP_SUBNET_ID_COL = 8;
1141  static const int HOST_ID_COL = 9;
1142  static const int SCOPE_ID_COL = 10;
1143 
1145  static const size_t OPTION_COLUMNS = 11;
1146 
1147 public:
1148 
1150  PgSqlOptionExchange()
1151  : PgSqlExchange(OPTION_COLUMNS), value_(),
1152  value_len_(0), option_() {
1153  columns_[OPTION_ID_COL] = "option_id";
1154  columns_[CODE_COL] = "code";
1155  columns_[VALUE_COL] = "value";
1156  columns_[FORMATTED_VALUE_COL] = "formatted_value";
1157  columns_[SPACE_COL] = "space";
1158  columns_[PERSISTENT_COL] = "persistent";
1159  columns_[USER_CONTEXT_COL] = "user_context";
1160  columns_[DHCP_CLIENT_CLASS_COL] = "dhcp_client_class";
1161  columns_[DHCP_SUBNET_ID_COL] = "dhcp_subnet_id";
1162  columns_[HOST_ID_COL] = "host_id";
1163  columns_[SCOPE_ID_COL] = "scope_id";
1164 
1165  BOOST_STATIC_ASSERT(10 < OPTION_COLUMNS);
1166  }
1167 
1177  createBindForSend(const OptionDescriptor& opt_desc,
1178  const std::string& opt_space,
1179  const HostID& host_id) {
1180  // Hold pointer to the option to make sure it remains valid until
1181  // we complete a query.
1182  option_ = opt_desc.option_;
1183 
1184  // Create the bind-array
1185  PsqlBindArrayPtr bind_array(new PsqlBindArray());
1186 
1187  try {
1188  // option_id: is auto_incremented so skip it
1189 
1190  // code: SMALLINT UNSIGNED NOT NULL
1191  bind_array->add(option_->getType());
1192 
1193  // value: BYTEA NULL
1194  if (opt_desc.formatted_value_.empty() &&
1195  (opt_desc.option_->len() > opt_desc.option_->getHeaderLen())) {
1196  // The formatted_value is empty and the option value is
1197  // non-empty so we need to prepare on-wire format for the
1198  // option and store it in the database as a BYTEA.
1199  OutputBuffer buf(opt_desc.option_->len());
1200  opt_desc.option_->pack(buf);
1201  const char* buf_ptr = static_cast<const char*>(buf.getData());
1202  value_.assign(buf_ptr + opt_desc.option_->getHeaderLen(),
1203  buf_ptr + buf.getLength());
1204  value_len_ = value_.size();
1205  bind_array->add(value_);
1206  } else {
1207  // No value or formatted_value specified. In this case, the
1208  // value BYTEA should be NULL.
1209  bind_array->addNull(PsqlBindArray::BINARY_FMT);
1210  }
1211 
1212  // formatted_value: TEXT NULL,
1213  if (!opt_desc.formatted_value_.empty()) {
1214  bind_array->addTempString(opt_desc.formatted_value_);
1215  } else {
1216  bind_array->addNull();
1217  }
1218 
1219  // space: VARCHAR(128) NULL
1220  if (!opt_space.empty()) {
1221  bind_array->addTempString(opt_space);
1222  } else {
1223  bind_array->addNull();
1224  }
1225 
1226  // persistent: BOOLEAN DEFAULT false
1227  bind_array->add(opt_desc.persistent_);
1228 
1229  // user_context: TEXT NULL,
1230  ConstElementPtr ctx = opt_desc.getContext();
1231  if (ctx) {
1232  std::string user_context_ = ctx->str();
1233  bind_array->addTempString(user_context_);
1234  } else {
1235  bind_array->addNull();
1236  }
1237 
1238  // host_id: INT NULL
1239  if (!host_id) {
1240  isc_throw(BadValue, "host_id cannot be null");
1241  }
1242  bind_array->add(host_id);
1243 
1244  } catch (const std::exception& ex) {
1246  "Could not create bind array for inserting DHCP "
1247  "host option: " << option_->toText() << ", reason: "
1248  << ex.what());
1249  }
1250 
1251  return (bind_array);
1252  }
1253 
1254 private:
1255 
1257  std::vector<uint8_t> value_;
1258 
1260  size_t value_len_;
1261 
1263  OptionPtr option_;
1264 };
1265 
1266 } // end of anonymous namespace
1267 
1268 namespace isc {
1269 namespace dhcp {
1270 
1273 public:
1274 
1282  GET_HOST_DHCPID, // Gets hosts by host identifier
1283  GET_HOST_ADDR, // Gets hosts by IPv4 address
1284  GET_HOST_SUBID4_DHCPID, // Gets host by IPv4 SubnetID, HW address/DUID
1285  GET_HOST_SUBID6_DHCPID, // Gets host by IPv6 SubnetID, HW address/DUID
1286  GET_HOST_SUBID_ADDR, // Gets host by IPv4 SubnetID and IPv4 address
1287  GET_HOST_PREFIX, // Gets host by IPv6 prefix
1288  GET_HOST_SUBID6_ADDR, // Gets host by IPv6 SubnetID and IPv6 prefix
1289  INSERT_HOST, // Insert new host to collection
1290  INSERT_V6_RESRV, // Insert v6 reservation
1291  INSERT_V4_HOST_OPTION, // Insert DHCPv4 option
1292  INSERT_V6_HOST_OPTION, // Insert DHCPv6 option
1293  DEL_HOST_ADDR4, // Delete v4 host (subnet-id, addr4)
1294  DEL_HOST_SUBID4_ID, // Delete v4 host (subnet-id, ident.type, identifier)
1295  DEL_HOST_SUBID6_ID, // Delete v6 host (subnet-id, ident.type, identifier)
1296  NUM_STATEMENTS // Number of statements
1297  };
1298 
1304  static const StatementIndex WRITE_STMTS_BEGIN = INSERT_HOST;
1305 
1310  PgSqlHostDataSourceImpl(const PgSqlConnection::ParameterMap& parameters);
1311 
1314 
1330  uint64_t addStatement(PgSqlHostDataSourceImpl::StatementIndex stindex,
1331  PsqlBindArrayPtr& bind,
1332  const bool return_last_id = false);
1333 
1339  bool delStatement(PgSqlHostDataSourceImpl::StatementIndex stindex,
1340  PsqlBindArrayPtr& bind);
1341 
1346  void addResv(const IPv6Resrv& resv, const HostID& id);
1347 
1356  void addOption(const PgSqlHostDataSourceImpl::StatementIndex& stindex,
1357  const OptionDescriptor& opt_desc,
1358  const std::string& opt_space,
1359  const OptionalValue<SubnetID>& subnet_id,
1360  const HostID& host_id);
1361 
1369  void addOptions(const StatementIndex& stindex,
1370  const ConstCfgOptionPtr& options_cfg,
1371  const uint64_t host_id);
1372 
1390  void getHostCollection(StatementIndex stindex, PsqlBindArrayPtr bind,
1391  boost::shared_ptr<PgSqlHostExchange> exchange,
1392  ConstHostCollection& result, bool single) const;
1393 
1410  ConstHostPtr getHost(const SubnetID& subnet_id,
1411  const Host::IdentifierType& identifier_type,
1412  const uint8_t* identifier_begin,
1413  const size_t identifier_len,
1414  StatementIndex stindex,
1415  boost::shared_ptr<PgSqlHostExchange> exchange) const;
1416 
1424  void checkReadOnly() const;
1425 
1434  std::pair<uint32_t, uint32_t> getVersion() const;
1435 
1438  boost::shared_ptr<PgSqlHostWithOptionsExchange> host_exchange_;
1439 
1442  boost::shared_ptr<PgSqlHostIPv6Exchange> host_ipv6_exchange_;
1443 
1447  boost::shared_ptr<PgSqlHostIPv6Exchange> host_ipv46_exchange_;
1448 
1451  boost::shared_ptr<PgSqlIPv6ReservationExchange> host_ipv6_reservation_exchange_;
1452 
1456  boost::shared_ptr<PgSqlOptionExchange> host_option_exchange_;
1457 
1460 
1463 };
1464 
1465 namespace {
1466 
1468 typedef boost::array<PgSqlTaggedStatement, PgSqlHostDataSourceImpl::NUM_STATEMENTS>
1470 
1474  // PgSqlHostDataSourceImpl::GET_HOST_DHCPID
1475  // Retrieves host information, IPv6 reservations and both DHCPv4 and
1476  // DHCPv6 options associated with the host. The LEFT JOIN clause is used
1477  // to retrieve information from 4 different tables using a single query.
1478  // Hence, this query returns multiple rows for a single host.
1479  {2,
1480  { OID_BYTEA, OID_INT2 },
1481  "get_host_dhcpid",
1482  "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
1483  " h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, "
1484  " h.hostname, h.dhcp4_client_classes, h.dhcp6_client_classes, "
1485  " h.user_context, "
1486  " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1487  " h.dhcp4_boot_file_name, h.auth_key, "
1488  " o4.option_id, o4.code, o4.value, o4.formatted_value, o4.space, "
1489  " o4.persistent, o4.user_context, "
1490  " o6.option_id, o6.code, o6.value, o6.formatted_value, o6.space, "
1491  " o6.persistent, o6.user_context, "
1492  " r.reservation_id, r.address, r.prefix_len, r.type, r.dhcp6_iaid "
1493  "FROM hosts AS h "
1494  "LEFT JOIN dhcp4_options AS o4 ON h.host_id = o4.host_id "
1495  "LEFT JOIN dhcp6_options AS o6 ON h.host_id = o6.host_id "
1496  "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
1497  "WHERE dhcp_identifier = $1 AND dhcp_identifier_type = $2 "
1498  "ORDER BY h.host_id, o4.option_id, o6.option_id, r.reservation_id"
1499  },
1500 
1501  // PgSqlHostDataSourceImpl::GET_HOST_ADDR
1502  // Retrieves host information along with the DHCPv4 options associated with
1503  // it. Left joining the dhcp4_options table results in multiple rows being
1504  // returned for the same host. The host is retrieved by IPv4 address.
1505  {1,
1506  { OID_INT8 }, "get_host_addr",
1507  "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
1508  " h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1509  " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1510  " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1511  " h.dhcp4_boot_file_name, h.auth_key, "
1512  " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1513  " o.persistent, o.user_context "
1514  "FROM hosts AS h "
1515  "LEFT JOIN dhcp4_options AS o ON h.host_id = o.host_id "
1516  "WHERE ipv4_address = $1 "
1517  "ORDER BY h.host_id, o.option_id"
1518  },
1519 
1520  //PgSqlHostDataSourceImpl::GET_HOST_SUBID4_DHCPID
1521  // Retrieves host information and DHCPv4 options using subnet identifier
1522  // and client's identifier. Left joining the dhcp4_options table results in
1523  // multiple rows being returned for the same host.
1524  {3,
1525  { OID_INT8, OID_INT2, OID_BYTEA },
1526  "get_host_subid4_dhcpid",
1527  "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
1528  " h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1529  " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1530  " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1531  " h.dhcp4_boot_file_name, h.auth_key, "
1532  " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1533  " o.persistent, o.user_context "
1534  "FROM hosts AS h "
1535  "LEFT JOIN dhcp4_options AS o ON h.host_id = o.host_id "
1536  "WHERE h.dhcp4_subnet_id = $1 AND h.dhcp_identifier_type = $2 "
1537  " AND h.dhcp_identifier = $3 "
1538  "ORDER BY h.host_id, o.option_id"
1539  },
1540 
1541  // PgSqlHostDataSourceImpl::GET_HOST_SUBID6_DHCPID
1542  // Retrieves host information, IPv6 reservations and DHCPv6 options
1543  // associated with a host. The number of rows returned is a multiplication
1544  // of number of IPv6 reservations and DHCPv6 options.
1545  {3,
1546  { OID_INT8, OID_INT2, OID_BYTEA },
1547  "get_host_subid6_dhcpid",
1548  "SELECT h.host_id, h.dhcp_identifier, "
1549  " h.dhcp_identifier_type, h.dhcp4_subnet_id, "
1550  " h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1551  " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1552  " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1553  " h.dhcp4_boot_file_name, h.auth_key, "
1554  " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1555  " o.persistent, o.user_context, "
1556  " r.reservation_id, r.address, r.prefix_len, r.type, r.dhcp6_iaid "
1557  "FROM hosts AS h "
1558  "LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id "
1559  "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
1560  "WHERE h.dhcp6_subnet_id = $1 AND h.dhcp_identifier_type = $2 "
1561  " AND h.dhcp_identifier = $3 "
1562  "ORDER BY h.host_id, o.option_id, r.reservation_id"
1563  },
1564 
1565  //PgSqlHostDataSourceImpl::GET_HOST_SUBID_ADDR
1566  // Retrieves host information and DHCPv4 options for the host using subnet
1567  // identifier and IPv4 reservation. Left joining the dhcp4_options table
1568  // results in multiple rows being returned for the host. The number of
1569  // rows depends on the number of options defined for the host.
1570  {2,
1571  { OID_INT8, OID_INT8 },
1572  "get_host_subid_addr",
1573  "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
1574  " h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1575  " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1576  " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1577  " h.dhcp4_boot_file_name, h.auth_key, "
1578  " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1579  " o.persistent, o.user_context "
1580  "FROM hosts AS h "
1581  "LEFT JOIN dhcp4_options AS o ON h.host_id = o.host_id "
1582  "WHERE h.dhcp4_subnet_id = $1 AND h.ipv4_address = $2 "
1583  "ORDER BY h.host_id, o.option_id"
1584  },
1585 
1586  // PgSqlHostDataSourceImpl::GET_HOST_PREFIX
1587  // Retrieves host information, IPv6 reservations and DHCPv6 options
1588  // associated with a host using prefix and prefix length. This query
1589  // returns host information for a single host. However, multiple rows
1590  // are returned due to left joining IPv6 reservations and DHCPv6 options.
1591  // The number of rows returned is multiplication of number of existing
1592  // IPv6 reservations and DHCPv6 options.
1593  {2,
1594  { OID_VARCHAR, OID_INT2 },
1595  "get_host_prefix",
1596  "SELECT h.host_id, h.dhcp_identifier, "
1597  " h.dhcp_identifier_type, h.dhcp4_subnet_id, "
1598  " h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1599  " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1600  " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1601  " h.dhcp4_boot_file_name, h.auth_key, "
1602  " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1603  " o.persistent, o.user_context, "
1604  " r.reservation_id, r.address, r.prefix_len, r.type, "
1605  " r.dhcp6_iaid "
1606  "FROM hosts AS h "
1607  "LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id "
1608  "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
1609  "WHERE h.host_id = "
1610  " (SELECT host_id FROM ipv6_reservations "
1611  " WHERE address = $1 AND prefix_len = $2) "
1612  "ORDER BY h.host_id, o.option_id, r.reservation_id"
1613  },
1614 
1615  // PgSqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR
1616  // Retrieves host information, IPv6 reservations and DHCPv6 options
1617  // associated with a host using IPv6 subnet id and prefix. This query
1618  // returns host information for a single host. However, multiple rows
1619  // are returned due to left joining IPv6 reservations and DHCPv6 options.
1620  // The number of rows returned is multiplication of number of existing
1621  // IPv6 reservations and DHCPv6 options.
1622  {2,
1623  { OID_INT8, OID_VARCHAR },
1624  "get_host_subid6_addr",
1625  "SELECT h.host_id, h.dhcp_identifier, "
1626  " h.dhcp_identifier_type, h.dhcp4_subnet_id, "
1627  " h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1628  " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1629  " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1630  " h.dhcp4_boot_file_name, h.auth_key, "
1631  " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1632  " o.persistent, o.user_context, "
1633  " r.reservation_id, r.address, r.prefix_len, r.type, "
1634  " r.dhcp6_iaid "
1635  "FROM hosts AS h "
1636  "LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id "
1637  "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
1638  "WHERE h.dhcp6_subnet_id = $1 AND r.address = $2 "
1639  "ORDER BY h.host_id, o.option_id, r.reservation_id"
1640  },
1641 
1642  // PgSqlHostDataSourceImpl::INSERT_HOST
1643  // Inserts a host into the 'hosts' table. Returns the inserted host id.
1644  {13,
1645  { OID_BYTEA, OID_INT2,
1648  "insert_host",
1649  "INSERT INTO hosts(dhcp_identifier, dhcp_identifier_type, "
1650  " dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
1651  " dhcp4_client_classes, dhcp6_client_classes, user_context, "
1652  " dhcp4_next_server, dhcp4_server_hostname, dhcp4_boot_file_name, auth_key) "
1653  "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) "
1654  "RETURNING host_id"
1655  },
1656 
1657  //PgSqlHostDataSourceImpl::INSERT_V6_RESRV
1658  // Inserts a single IPv6 reservation into 'reservations' table.
1659  {5,
1661  "insert_v6_resrv",
1662  "INSERT INTO ipv6_reservations(address, prefix_len, type, "
1663  " dhcp6_iaid, host_id) "
1664  "VALUES ($1, $2, $3, $4, $5)"
1665  },
1666 
1667  // PgSqlHostDataSourceImpl::INSERT_V4_HOST_OPTION
1668  // Inserts a single DHCPv4 option into 'dhcp4_options' table.
1669  // Using fixed scope_id = 3, which associates an option with host.
1670  {7,
1673  "insert_v4_host_option",
1674  "INSERT INTO dhcp4_options(code, value, formatted_value, space, "
1675  " persistent, user_context, host_id, scope_id) "
1676  "VALUES ($1, $2, $3, $4, $5, $6, $7, 3)"
1677  },
1678 
1679  // PgSqlHostDataSourceImpl::INSERT_V6_HOST_OPTION
1680  // Inserts a single DHCPv6 option into 'dhcp6_options' table.
1681  // Using fixed scope_id = 3, which associates an option with host.
1682  {7,
1685  "insert_v6_host_option",
1686  "INSERT INTO dhcp6_options(code, value, formatted_value, space, "
1687  " persistent, user_context, host_id, scope_id) "
1688  "VALUES ($1, $2, $3, $4, $5, $6, $7, 3)"
1689  },
1690 
1691  // PgSqlHostDataSourceImpl::DEL_HOST_ADDR4
1692  // Deletes a v4 host that matches (subnet-id, addr4)
1693  {2,
1694  { OID_INT8, OID_INT8 },
1695  "del_host_addr4",
1696  "DELETE FROM hosts WHERE dhcp4_subnet_id = $1 AND ipv4_address = $2"
1697  },
1698 
1699  // PgSqlHostDataSourceImpl::DEL_HOST_SUBID4_ID
1700  // Deletes a v4 host that matches (subnet4-id, identifier-type, identifier)
1701  {3,
1702  { OID_INT8, OID_INT2, OID_BYTEA },
1703  "del_host_subid4_id",
1704  "DELETE FROM hosts WHERE dhcp4_subnet_id = $1 "
1705  "AND dhcp_identifier_type = $2 "
1706  "AND dhcp_identifier = $3"
1707  },
1708 
1709  // PgSqlHostDataSourceImpl::DEL_HOST_SUBID6_ID
1710  // Deletes a v6 host that matches (subnet6-id, identifier-type, identifier)
1711  {3,
1712  { OID_INT8, OID_INT2, OID_BYTEA },
1713  "del_host_subid6_id",
1714  "DELETE FROM hosts WHERE dhcp6_subnet_id = $1 "
1715  "AND dhcp_identifier_type = $2 "
1716  "AND dhcp_identifier = $3"
1717  }
1718 }
1719 };
1720 
1721 }; // end anonymous namespace
1722 
1723 PgSqlHostDataSourceImpl::
1724 PgSqlHostDataSourceImpl(const PgSqlConnection::ParameterMap& parameters)
1725  : host_exchange_(new PgSqlHostWithOptionsExchange(PgSqlHostWithOptionsExchange::DHCP4_ONLY)),
1726  host_ipv6_exchange_(new PgSqlHostIPv6Exchange(PgSqlHostWithOptionsExchange::DHCP6_ONLY)),
1727  host_ipv46_exchange_(new PgSqlHostIPv6Exchange(PgSqlHostWithOptionsExchange::
1728  DHCP4_AND_DHCP6)),
1729  host_ipv6_reservation_exchange_(new PgSqlIPv6ReservationExchange()),
1730  host_option_exchange_(new PgSqlOptionExchange()),
1731  conn_(parameters),
1732  is_readonly_(false) {
1733 
1734  // Open the database.
1735  conn_.openDatabase();
1736 
1737  // Validate the schema version first.
1738  std::pair<uint32_t, uint32_t> code_version(PG_SCHEMA_VERSION_MAJOR,
1740  std::pair<uint32_t, uint32_t> db_version = getVersion();
1741  if (code_version != db_version) {
1743  "PostgreSQL schema version mismatch: need version: "
1744  << code_version.first << "." << code_version.second
1745  << " found version: " << db_version.first << "."
1746  << db_version.second);
1747  }
1748 
1749  // Now prepare the SQL statements.
1752 
1753  // Check if the backend is explicitly configured to operate with
1754  // read only access to the database.
1756 
1757  // If we are using read-write mode for the database we also prepare
1758  // statements for INSERTS etc.
1759  if (!is_readonly_) {
1761  tagged_statements.end());
1762 
1763  } else {
1764  LOG_INFO(dhcpsrv_logger, DHCPSRV_PGSQL_HOST_DB_READONLY);
1765  }
1766 }
1767 
1769 }
1770 
1771 uint64_t
1773  PsqlBindArrayPtr& bind_array,
1774  const bool return_last_id) {
1775  uint64_t last_id = 0;
1776  PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
1777  tagged_statements[stindex].nbparams,
1778  &bind_array->values_[0],
1779  &bind_array->lengths_[0],
1780  &bind_array->formats_[0], 0));
1781 
1782  int s = PQresultStatus(r);
1783 
1784  if (s != PGRES_COMMAND_OK) {
1785  // Failure: check for the special case of duplicate entry.
1786  if (conn_.compareError(r, PgSqlConnection::DUPLICATE_KEY)) {
1787  isc_throw(DuplicateEntry, "Database duplicate entry error");
1788  }
1789 
1790  // Connection determines if the error is fatal or not, and
1791  // throws the appropriate exception
1793  }
1794 
1795  if (return_last_id) {
1796  PgSqlExchange::getColumnValue(r, 0, 0, last_id);
1797  }
1798 
1799  return (last_id);
1800 }
1801 
1802 bool
1804  PsqlBindArrayPtr& bind_array) {
1805  PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
1806  tagged_statements[stindex].nbparams,
1807  &bind_array->values_[0],
1808  &bind_array->lengths_[0],
1809  &bind_array->formats_[0], 0));
1810 
1811  int s = PQresultStatus(r);
1812 
1813  if (s != PGRES_COMMAND_OK) {
1814  // Connection determines if the error is fatal or not, and
1815  // throws the appropriate exception
1817  }
1818 
1819  // Now check how many rows (hosts) were deleted. This should be either
1820  // "0" or "1".
1821  char* rows_deleted = PQcmdTuples(r);
1822  if (!rows_deleted) {
1824  "Could not retrieve the number of deleted rows.");
1825  }
1826  return (rows_deleted[0] != '0');
1827 }
1828 
1829 void
1831  const HostID& id) {
1832  PsqlBindArrayPtr bind_array;
1833  bind_array = host_ipv6_reservation_exchange_->createBindForSend(resv, id);
1834  addStatement(INSERT_V6_RESRV, bind_array);
1835 }
1836 
1837 void
1839  const OptionDescriptor& opt_desc,
1840  const std::string& opt_space,
1841  const OptionalValue<SubnetID>&,
1842  const HostID& id) {
1843  PsqlBindArrayPtr bind_array;
1844  bind_array = host_option_exchange_->createBindForSend(opt_desc, opt_space,
1845  id);
1846  addStatement(stindex, bind_array);
1847 }
1848 
1849 void
1851  const ConstCfgOptionPtr& options_cfg,
1852  const uint64_t host_id) {
1853  // Get option space names and vendor space names and combine them within a
1854  // single list.
1855  std::list<std::string> option_spaces = options_cfg->getOptionSpaceNames();
1856  std::list<std::string> vendor_spaces = options_cfg->getVendorIdsSpaceNames();
1857  option_spaces.insert(option_spaces.end(), vendor_spaces.begin(),
1858  vendor_spaces.end());
1859 
1860  // For each option space retrieve all options and insert them into the
1861  // database.
1862  for (std::list<std::string>::const_iterator space = option_spaces.begin();
1863  space != option_spaces.end(); ++space) {
1864  OptionContainerPtr options = options_cfg->getAll(*space);
1865  if (options && !options->empty()) {
1866  for (OptionContainer::const_iterator opt = options->begin();
1867  opt != options->end(); ++opt) {
1868  addOption(stindex, *opt, *space, OptionalValue<SubnetID>(),
1869  host_id);
1870  }
1871  }
1872  }
1873 }
1874 
1875 void
1878  boost::shared_ptr<PgSqlHostExchange> exchange,
1879  ConstHostCollection& result, bool single) const {
1880 
1881  exchange->clear();
1882  PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
1883  tagged_statements[stindex].nbparams,
1884  &bind_array->values_[0],
1885  &bind_array->lengths_[0],
1886  &bind_array->formats_[0], 0));
1887 
1889 
1890  int rows = r.getRows();
1891  for(int row = 0; row < rows; ++row) {
1892  exchange->processRowData(result, r, row);
1893 
1894  if (single && result.size() > 1) {
1895  isc_throw(MultipleRecords, "multiple records were found in the "
1896  "database where only one was expected for query "
1897  << tagged_statements[stindex].name);
1898  }
1899  }
1900 }
1901 
1904 getHost(const SubnetID& subnet_id,
1905  const Host::IdentifierType& identifier_type,
1906  const uint8_t* identifier_begin,
1907  const size_t identifier_len,
1908  StatementIndex stindex,
1909  boost::shared_ptr<PgSqlHostExchange> exchange) const {
1910 
1911  // Set up the WHERE clause value
1912  PsqlBindArrayPtr bind_array(new PsqlBindArray());
1913 
1914  // Add the subnet id.
1915  bind_array->add(subnet_id);
1916 
1917  // Add the Identifier type.
1918  bind_array->add(static_cast<uint8_t>(identifier_type));
1919 
1920  // Add the identifier value.
1921  bind_array->add(identifier_begin, identifier_len);
1922 
1923  ConstHostCollection collection;
1924  getHostCollection(stindex, bind_array, exchange, collection, true);
1925 
1926  // Return single record if present, else clear the host.
1927  ConstHostPtr result;
1928  if (!collection.empty())
1929  result = *collection.begin();
1930 
1931  return (result);
1932 }
1933 
1934 std::pair<uint32_t, uint32_t> PgSqlHostDataSourceImpl::getVersion() const {
1936  DHCPSRV_PGSQL_HOST_DB_GET_VERSION);
1937  const char* version_sql = "SELECT version, minor FROM schema_version;";
1938  PgSqlResult r(PQexec(conn_, version_sql));
1939  if(PQresultStatus(r) != PGRES_TUPLES_OK) {
1940  isc_throw(DbOperationError, "unable to execute PostgreSQL statement <"
1941  << version_sql << ">, reason: " << PQerrorMessage(conn_));
1942  }
1943 
1944  uint32_t version;
1945  PgSqlExchange::getColumnValue(r, 0, 0, version);
1946 
1947  uint32_t minor;
1948  PgSqlExchange::getColumnValue(r, 0, 1, minor);
1949 
1950  return (std::make_pair(version, minor));
1951 }
1952 
1953 void
1955  if (is_readonly_) {
1956  isc_throw(ReadOnlyDb, "PostgreSQL host database backend is configured"
1957  " to operate in read only mode");
1958  }
1959 }
1960 
1961 /*********** PgSqlHostDataSource *********************/
1962 
1964 PgSqlHostDataSource(const PgSqlConnection::ParameterMap& parameters)
1965  : impl_(new PgSqlHostDataSourceImpl(parameters)) {
1966 }
1967 
1969  delete impl_;
1970 }
1971 
1972 void
1974  // If operating in read-only mode, throw exception.
1975  impl_->checkReadOnly();
1976 
1977  // Initiate PostgreSQL transaction as we will have to make multiple queries
1978  // to insert host information into multiple tables. If that fails on
1979  // any stage, the transaction will be rolled back by the destructor of
1980  // the PgSqlTransaction class.
1981  PgSqlTransaction transaction(impl_->conn_);
1982 
1983  // Create the PgSQL Bind array for the host
1984  PsqlBindArrayPtr bind_array = impl_->host_exchange_->createBindForSend(host);
1985 
1986  // ... and insert the host.
1987  uint32_t host_id = impl_->addStatement(PgSqlHostDataSourceImpl::INSERT_HOST,
1988  bind_array, true);
1989 
1990  // Insert DHCPv4 options.
1991  ConstCfgOptionPtr cfg_option4 = host->getCfgOption4();
1992  if (cfg_option4) {
1994  cfg_option4, host_id);
1995  }
1996 
1997  // Insert DHCPv6 options.
1998  ConstCfgOptionPtr cfg_option6 = host->getCfgOption6();
1999  if (cfg_option6) {
2001  cfg_option6, host_id);
2002  }
2003 
2004  // Insert IPv6 reservations.
2005  IPv6ResrvRange v6resv = host->getIPv6Reservations();
2006  if (std::distance(v6resv.first, v6resv.second) > 0) {
2007  for (IPv6ResrvIterator resv = v6resv.first; resv != v6resv.second;
2008  ++resv) {
2009  impl_->addResv(resv->second, host_id);
2010  }
2011  }
2012 
2013  // Everything went fine, so explicitly commit the transaction.
2014  transaction.commit();
2015 }
2016 
2017 bool
2019  // If operating in read-only mode, throw exception.
2020  impl_->checkReadOnly();
2021 
2022  if (addr.isV4()) {
2023  PsqlBindArrayPtr bind_array(new PsqlBindArray());
2024  bind_array->add(subnet_id);
2025  bind_array->add(addr);
2027  bind_array));
2028  }
2029 
2030  ConstHostPtr host = get6(subnet_id, addr);
2031  if (!host) {
2032  return (false);
2033  }
2034 
2035  return del6(subnet_id, host->getIdentifierType(), &host->getIdentifier()[0],
2036  host->getIdentifier().size());
2037 }
2038 
2039 bool
2041  const Host::IdentifierType& identifier_type,
2042  const uint8_t* identifier_begin,
2043  const size_t identifier_len) {
2044 
2045  PsqlBindArrayPtr bind_array(new PsqlBindArray());
2046 
2047  // Subnet-id
2048  bind_array->add(subnet_id);
2049 
2050  // identifier-type
2051  bind_array->add(static_cast<uint8_t>(identifier_type));
2052 
2053  // identifier
2054  bind_array->add(identifier_begin, identifier_len);
2055 
2057  bind_array));
2058 }
2059 
2060 bool
2062  const Host::IdentifierType& identifier_type,
2063  const uint8_t* identifier_begin,
2064  const size_t identifier_len) {
2065  PsqlBindArrayPtr bind_array(new PsqlBindArray());
2066 
2067  // Subnet-id
2068  bind_array->add(subnet_id);
2069 
2070  // identifier-type
2071  bind_array->add(static_cast<uint8_t>(identifier_type));
2072 
2073  // identifier
2074  bind_array->add(identifier_begin, identifier_len);
2075 
2077  bind_array));
2078 }
2079 
2082  const uint8_t* identifier_begin,
2083  const size_t identifier_len) const {
2084  // Set up the WHERE clause value
2085  PsqlBindArrayPtr bind_array(new PsqlBindArray());
2086 
2087  // Identifier value.
2088  bind_array->add(identifier_begin, identifier_len);
2089 
2090  // Identifier type.
2091  bind_array->add(static_cast<uint8_t>(identifier_type));
2092 
2093  ConstHostCollection result;
2095  bind_array, impl_->host_ipv46_exchange_,
2096  result, false);
2097  return (result);
2098 }
2099 
2102 
2103  // Set up the WHERE clause value
2104  PsqlBindArrayPtr bind_array(new PsqlBindArray());
2105 
2106  // v4 Reservation address
2107  bind_array->add(address);
2108 
2109  ConstHostCollection result;
2111  impl_->host_exchange_, result, false);
2112 
2113  return (result);
2114 }
2115 
2118  const Host::IdentifierType& identifier_type,
2119  const uint8_t* identifier_begin,
2120  const size_t identifier_len) const {
2121 
2122  return (impl_->getHost(subnet_id, identifier_type, identifier_begin,
2123  identifier_len,
2125  impl_->host_exchange_));
2126 }
2127 
2130  const asiolink::IOAddress& address) const {
2131  if (!address.isV4()) {
2132  isc_throw(BadValue, "PgSqlHostDataSource::get4(id, address) - "
2133  " wrong address type, address supplied is an IPv6 address");
2134  }
2135 
2136  // Set up the WHERE clause value
2137  PsqlBindArrayPtr bind_array(new PsqlBindArray());
2138 
2139  // Add the subnet id
2140  bind_array->add(subnet_id);
2141 
2142  // Add the address
2143  bind_array->add(address);
2144 
2145  ConstHostCollection collection;
2147  bind_array, impl_->host_exchange_, collection,
2148  true);
2149 
2150  // Return single record if present, else clear the host.
2151  ConstHostPtr result;
2152  if (!collection.empty())
2153  result = *collection.begin();
2154 
2155  return (result);
2156 }
2157 
2160  const Host::IdentifierType& identifier_type,
2161  const uint8_t* identifier_begin,
2162  const size_t identifier_len) const {
2163 
2164  return (impl_->getHost(subnet_id, identifier_type, identifier_begin,
2166  impl_->host_ipv6_exchange_));
2167 }
2168 
2171  const uint8_t prefix_len) const {
2173 
2174  // Set up the WHERE clause value
2175  PsqlBindArrayPtr bind_array(new PsqlBindArray());
2176 
2177  // Add the prefix
2178  bind_array->add(prefix);
2179 
2180  // Add the prefix length
2181  bind_array->add(prefix_len);
2182 
2183  ConstHostCollection collection;
2185  bind_array, impl_->host_ipv6_exchange_,
2186  collection, true);
2187 
2188  // Return single record if present, else clear the host.
2189  ConstHostPtr result;
2190  if (!collection.empty()) {
2191  result = *collection.begin();
2192  }
2193 
2194  return (result);
2195 }
2196 
2199  const asiolink::IOAddress& address) const {
2201 
2202  // Set up the WHERE clause value
2203  PsqlBindArrayPtr bind_array(new PsqlBindArray());
2204 
2205  // Add the subnet id
2206  bind_array->add(subnet_id);
2207 
2208  // Add the prefix
2209  bind_array->add(address);
2210 
2211  ConstHostCollection collection;
2213  bind_array, impl_->host_ipv6_exchange_,
2214  collection, true);
2215 
2216  // Return single record if present, else clear the host.
2217  ConstHostPtr result;
2218  if (!collection.empty()) {
2219  result = *collection.begin();
2220  }
2221 
2222  return (result);
2223 }
2224 
2225 // Miscellaneous database methods.
2226 
2227 std::string PgSqlHostDataSource::getName() const {
2228  std::string name = "";
2229  try {
2230  name = impl_->conn_.getParameter("name");
2231  } catch (...) {
2232  // Return an empty name
2233  }
2234  return (name);
2235 }
2236 
2238  return (std::string("Host data source that stores host information"
2239  "in PostgreSQL database"));
2240 }
2241 
2242 std::pair<uint32_t, uint32_t> PgSqlHostDataSource::getVersion() const {
2243  return(impl_->getVersion());
2244 }
2245 
2246 void
2248  // If operating in read-only mode, throw exception.
2249  impl_->checkReadOnly();
2250  impl_->conn_.commit();
2251 }
2252 
2253 void
2255  // If operating in read-only mode, throw exception.
2256  impl_->checkReadOnly();
2257  impl_->conn_.rollback();
2258 }
2259 
2260 }; // end of isc::dhcp namespace
2261 }; // end of isc namespace
RAII wrapper for PostgreSQL Result sets.
Option descriptor.
Definition: cfg_option.h:35
const size_t OID_BOOL
Simple class representing an optional value.
const size_t OID_INT2
boost::shared_ptr< CfgOption > CfgOptionPtr
Non-const pointer.
Definition: cfg_option.h:497
void commit()
Commits transaction.
data::ConstElementPtr getContext() const
Returns const pointer to the user context.
Definition: user_context.h:24
virtual void add(const HostPtr &host)
Adds a new host to the collection.
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
uint8_t getPrefixLen() const
Returns prefix length.
Definition: host.h:140
const size_t OID_INT8
A standard Data module exception that is thrown if a parse error is encountered when constructing an ...
Definition: data.h:43
boost::shared_ptr< Host > HostPtr
Pointer to the Host object.
Definition: host.h:725
boost::shared_ptr< const CfgOption > ConstCfgOptionPtr
Const pointer.
Definition: cfg_option.h:500
#define DHCP6_OPTION_SPACE
Definition: option_space.h:17
boost::shared_ptr< PgSqlHostIPv6Exchange > host_ipv6_exchange_
Pointer to an object representing an exchange which can be used to retrieve hosts,...
bool compareError(const PgSqlResult &r, const char *error_state)
Checks a result set's SQL state against an error state.
boost::shared_ptr< Option > OptionPtr
Definition: option.h:37
void addOption(const PgSqlHostDataSourceImpl::StatementIndex &stindex, const OptionDescriptor &opt_desc, const std::string &opt_space, const OptionalValue< SubnetID > &subnet_id, const HostID &host_id)
Inserts a single DHCP option into the database.
Universe
defines option universe DHCPv4 or DHCPv6
Definition: option.h:67
virtual std::string getName() const
Returns the name of the open database.
boost::shared_ptr< PgSqlHostWithOptionsExchange > host_exchange_
Pointer to the object representing an exchange which can be used to retrieve hosts and DHCPv4 options...
uint64_t addStatement(PgSqlHostDataSourceImpl::StatementIndex stindex, PsqlBindArrayPtr &bind, const bool return_last_id=false)
Executes statements which insert a row into one of the tables.
PgSqlHostDataSource(const db::DatabaseConnection::ParameterMap &parameters)
Constructor.
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)
boost::shared_ptr< PgSqlIPv6ReservationExchange > host_ipv6_reservation_exchange_
Pointer to an object representing an exchange which can be used to insert new IPv6 reservation.
Base class for marshalling data to and from PostgreSQL.
int getRows() const
Returns the number of rows in the result set.
IPv6 reservation for a host.
Definition: host.h:106
const size_t OID_TEXT
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:25
std::pair< uint32_t, uint32_t > getVersion() const
Returns PostgreSQL schema version of the open database.
Exception thrown on failure to open database.
ConstHostPtr getHost(const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len, StatementIndex stindex, boost::shared_ptr< PgSqlHostExchange > exchange) const
Retrieves a host by subnet and client's unique identifier.
void commit()
Commit Transactions.
TaggedStatementArray tagged_statements
Prepared MySQL statements used by the backend to insert and retrieve hosts from the database.
Multiple lease records found where one expected.
Definition: db_exceptions.h:28
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
std::string formatted_value_
Option value in textual (CSV) format.
Definition: cfg_option.h:59
void addResv(const IPv6Resrv &resv, const HostID &id)
Inserts IPv6 Reservation into ipv6_reservation table.
void prepareStatements(const PgSqlTaggedStatement *start_statement, const PgSqlTaggedStatement *end_statement)
Prepare statements.
virtual ConstHostCollection getAll4(const asiolink::IOAddress &address) const
Returns a collection of hosts using the specified IPv4 address.
Definition: edns.h:19
std::pair< IPv6ResrvIterator, IPv6ResrvIterator > IPv6ResrvRange
Definition: host.h:188
const uint32_t PG_SCHEMA_VERSION_MINOR
boost::shared_ptr< PsqlBindArray > PsqlBindArrayPtr
Defines a smart pointer to PsqlBindArray.
void addOptions(const StatementIndex &stindex, const ConstCfgOptionPtr &options_cfg, const uint64_t host_id)
Inserts multiple options into the database.
const int DHCPSRV_DBG_TRACE_DETAIL
Additional information.
Definition: dhcpsrv_log.h:38
std::vector< ConstHostPtr > ConstHostCollection
Collection of the const Host objects.
Definition: host.h:731
bool persistent_
Persistence flag.
Definition: cfg_option.h:44
A generic exception that is thrown when an unexpected error condition occurs.
void rollback()
Rollback Transactions.
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:23
boost::shared_ptr< const Host > ConstHostPtr
Const pointer to the Host object.
Definition: host.h:728
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)
std::string getParameter(const std::string &name) const
Returns value of a connection parameter.
Common PgSql Connector Pool.
virtual ~PgSqlHostDataSource()
Virtual destructor.
Represents a device with IPv4 and/or IPv6 reservations.
Definition: host.h:242
bool configuredReadOnly() const
Convenience method checking if database should be opened with read only access.
Type
Type of the reservation.
Definition: host.h:112
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 del(const SubnetID &subnet_id, const asiolink::IOAddress &addr)
Attempts to delete a host by (subnet-id, address)
#define DHCP4_OPTION_SPACE
Definition: option_space.h:16
IPv6ResrvCollection::const_iterator IPv6ResrvIterator
Definition: host.h:186
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
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...
OptionPtr option_
Option instance.
Definition: cfg_option.h:38
virtual std::string getDescription() const
Returns description of the backend.
Authentication keys.
Definition: host.h:34
This is a base class for exceptions thrown from the DNS library module.
Defines the logger used by the top-level component of kea-dhcp-ddns.
Type getType() const
Returns reservation type.
Definition: host.h:149
void checkStatementError(const PgSqlResult &r, PgSqlTaggedStatement &statement) const
Checks result of the r object.
Implementation of the PgSqlHostDataSource.
const size_t OID_BYTEA
void checkReadOnly() const
Throws exception if database is read only.
boost::shared_ptr< PgSqlOptionExchange > host_option_exchange_
Pointer to an object representing an exchange which can be used to insert DHCPv4 or DHCPv6 option int...
boost::shared_ptr< PgSqlHostIPv6Exchange > host_ipv46_exchange_
Pointer to an object representing an exchange which can be used to retrieve hosts,...
PgSqlConnection conn_
PgSQL connection.
virtual void rollback()
Rollback Transactions.
const size_t OID_INT4
boost::shared_ptr< OptionContainer > OptionContainerPtr
Pointer to the OptionContainer object.
Definition: cfg_option.h:206
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
void getHostCollection(StatementIndex stindex, PsqlBindArrayPtr bind, boost::shared_ptr< PgSqlHostExchange > exchange, ConstHostCollection &result, bool single) const
Creates collection of Host objects with associated information such as IPv6 reservations and/or DHCP ...
Attempt to modify data in read-only database.
Definition: db_exceptions.h:49
boost::shared_ptr< OptionDefinition > OptionDefinitionPtr
Pointer to option definition object.
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.
void openDatabase()
Open Database.
const size_t OID_VARCHAR
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition: dhcpsrv_log.h:56
virtual void commit()
Commit Transactions.
IdentifierType
Type of the host identifier.
Definition: host.h:252
virtual std::pair< uint32_t, uint32_t > getVersion() const
Returns backend version.
int version()
returns Kea hooks version.
static const StatementIndex WRITE_STMTS_BEGIN
Index of first statement performing write to the database.
const uint32_t PG_SCHEMA_VERSION_MAJOR
Define PostgreSQL backend version: 5.0.
uint64_t HostID
HostID (used only when storing in MySQL, PostgreSQL or Cassandra)
Definition: host.h:28
bool delStatement(PgSqlHostDataSourceImpl::StatementIndex stindex, PsqlBindArrayPtr &bind)
Executes statements that delete records.
bool is_readonly_
Indicates if the database is opened in read only mode.
const asiolink::IOAddress & getPrefix() const
Returns prefix for the reservation.
Definition: host.h:135
Exception thrown on failure to execute a database function.
boost::array< TaggedStatement, MySqlHostDataSourceImpl::NUM_STATEMENTS > TaggedStatementArray
Array of tagged statements.
Database duplicate entry error.
Definition: db_exceptions.h:42
uint32_t SubnetID
Unique identifier for a subnet (both v4 and v6)
Definition: lease.h:24
RAII object representing a PostgreSQL transaction.