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
isc::dhcp::OptionContainerPtr
boost::shared_ptr< OptionContainer > OptionContainerPtr
Pointer to the OptionContainer object.
Definition: cfg_option.h:206
isc::dhcp::IPv6Resrv::getType
Type getType() const
Returns reservation type.
Definition: host.h:149
isc::dhcp::OptionBuffer
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:25
optional_value.h
isc::util::OptionalValue
Simple class representing an optional value.
Definition: optional_value.h:55
isc::Unexpected
A generic exception that is thrown when an unexpected error condition occurs.
Definition: exceptions/exceptions.h:153
option_space.h
isc::dhcp::AuthKey
Authentication keys.
Definition: host.h:34
isc::db::OID_VARCHAR
const size_t OID_VARCHAR
Definition: pgsql_connection.h:64
isc::dhcp::ConstCfgOptionPtr
boost::shared_ptr< const CfgOption > ConstCfgOptionPtr
Const pointer.
Definition: cfg_option.h:500
isc::dhcp::OptionDefinitionPtr
boost::shared_ptr< OptionDefinition > OptionDefinitionPtr
Pointer to option definition object.
Definition: option_definition.h:51
isc::db::PgSqlConnection::checkStatementError
void checkStatementError(const PgSqlResult &r, PgSqlTaggedStatement &statement) const
Checks result of the r object.
Definition: pgsql_connection.cc:291
isc::dhcp::Host::IdentifierType
IdentifierType
Type of the host identifier.
Definition: host.h:252
isc::dhcp::TaggedStatementArray
boost::array< TaggedStatement, MySqlHostDataSourceImpl::NUM_STATEMENTS > TaggedStatementArray
Array of tagged statements.
Definition: mysql_host_data_source.cc:2133
version
int version()
returns Kea hooks version.
Definition: high_availability/version.cc:13
isc::db::PgSqlConnection::rollback
void rollback()
Rollback Transactions.
Definition: pgsql_connection.cc:354
isc::db::OID_BOOL
const size_t OID_BOOL
Definition: pgsql_connection.h:58
isc::dhcp::PgSqlHostDataSourceImpl::GET_HOST_DHCPID
@ GET_HOST_DHCPID
Definition: pgsql_host_data_source.cc:1282
isc::dhcp::PgSqlHostDataSource::del
virtual bool del(const SubnetID &subnet_id, const asiolink::IOAddress &addr)
Attempts to delete a host by (subnet-id, address)
Definition: pgsql_host_data_source.cc:2018
isc::db::PgSqlConnection
Common PgSql Connector Pool.
Definition: pgsql_connection.h:299
isc::dhcp::PgSqlHostDataSource::getDescription
virtual std::string getDescription() const
Returns description of the backend.
Definition: pgsql_host_data_source.cc:2237
isc::db
Definition: cql_connection.cc:29
libdhcp++.h
isc::db::OID_INT4
const size_t OID_INT4
Definition: pgsql_connection.h:62
isc::dhcp::PgSqlHostDataSourceImpl::host_exchange_
boost::shared_ptr< PgSqlHostWithOptionsExchange > host_exchange_
Pointer to the object representing an exchange which can be used to retrieve hosts and DHCPv4 options...
Definition: pgsql_host_data_source.cc:1438
isc::dhcp::PgSqlHostDataSource::~PgSqlHostDataSource
virtual ~PgSqlHostDataSource()
Virtual destructor.
Definition: pgsql_host_data_source.cc:1968
isc::dhcp::DHCPSRV_DBG_TRACE_DETAIL
const int DHCPSRV_DBG_TRACE_DETAIL
Additional information.
Definition: dhcpsrv_log.h:38
isc::dhcp::PgSqlHostDataSourceImpl::getHost
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.
Definition: pgsql_host_data_source.cc:1904
isc::dhcp::PgSqlHostDataSource::rollback
virtual void rollback()
Rollback Transactions.
Definition: pgsql_host_data_source.cc:2254
isc::db::PgSqlResult::getRows
int getRows() const
Returns the number of rows in the result set.
Definition: pgsql_connection.h:104
isc::dhcp::PgSqlHostDataSourceImpl::GET_HOST_ADDR
@ GET_HOST_ADDR
Definition: pgsql_host_data_source.cc:1283
isc::dhcp::PgSqlHostDataSource::getAll
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...
Definition: pgsql_host_data_source.cc:2081
isc::dhcp::PgSqlHostDataSourceImpl::is_readonly_
bool is_readonly_
Indicates if the database is opened in read only mode.
Definition: pgsql_host_data_source.cc:1462
isc::dhcp::PgSqlHostDataSourceImpl::host_ipv46_exchange_
boost::shared_ptr< PgSqlHostIPv6Exchange > host_ipv46_exchange_
Pointer to an object representing an exchange which can be used to retrieve hosts,...
Definition: pgsql_host_data_source.cc:1447
isc::db::DatabaseConnection::configuredReadOnly
bool configuredReadOnly() const
Convenience method checking if database should be opened with read only access.
Definition: database_connection.cc:95
DHCP6_OPTION_SPACE
#define DHCP6_OPTION_SPACE
Definition: option_space.h:17
isc::dhcp::Host
Represents a device with IPv4 and/or IPv6 reservations.
Definition: host.h:242
db_exceptions.h
isc::db::ReadOnlyDb
Attempt to modify data in read-only database.
Definition: db_exceptions.h:49
isc::dhcp::PgSqlHostDataSourceImpl::INSERT_V4_HOST_OPTION
@ INSERT_V4_HOST_OPTION
Definition: pgsql_host_data_source.cc:1291
isc::dhcp::HostPtr
boost::shared_ptr< Host > HostPtr
Pointer to the Host object.
Definition: host.h:725
isc::data
Definition: cfg_to_element.h:25
isc::dhcp::PgSqlHostDataSourceImpl::WRITE_STMTS_BEGIN
static const StatementIndex WRITE_STMTS_BEGIN
Index of first statement performing write to the database.
Definition: pgsql_host_data_source.cc:1304
isc::dhcp::PgSqlHostDataSourceImpl::INSERT_HOST
@ INSERT_HOST
Definition: pgsql_host_data_source.cc:1289
isc::dhcp::PgSqlHostDataSourceImpl
Implementation of the PgSqlHostDataSource.
Definition: pgsql_host_data_source.cc:1272
isc::dhcp::PgSqlHostDataSourceImpl::checkReadOnly
void checkReadOnly() const
Throws exception if database is read only.
Definition: pgsql_host_data_source.cc:1954
isc::dhcp::PgSqlHostDataSourceImpl::getHostCollection
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 ...
Definition: pgsql_host_data_source.cc:1877
isc::dhcp::PgSqlHostDataSource::getVersion
virtual std::pair< uint32_t, uint32_t > getVersion() const
Returns backend version.
Definition: pgsql_host_data_source.cc:2242
isc::dhcp::PgSqlHostDataSourceImpl::GET_HOST_SUBID6_DHCPID
@ GET_HOST_SUBID6_DHCPID
Definition: pgsql_host_data_source.cc:1285
isc::db::PgSqlConnection::openDatabase
void openDatabase()
Open Database.
Definition: pgsql_connection.cc:152
isc::db::PG_SCHEMA_VERSION_MAJOR
const uint32_t PG_SCHEMA_VERSION_MAJOR
Define PostgreSQL backend version: 5.0.
Definition: pgsql_connection.h:21
isc::dhcp::OptionDescriptor::persistent_
bool persistent_
Persistence flag.
Definition: cfg_option.h:44
isc::Exception
This is a base class for exceptions thrown from the DNS library module.
Definition: exceptions/exceptions.h:23
isc::dhcp::PgSqlHostDataSourceImpl::delStatement
bool delStatement(PgSqlHostDataSourceImpl::StatementIndex stindex, PsqlBindArrayPtr &bind)
Executes statements that delete records.
Definition: pgsql_host_data_source.cc:1803
isc::data::UserContext::getContext
data::ConstElementPtr getContext() const
Returns const pointer to the user context.
Definition: user_context.h:24
isc::dhcp::IPv6Resrv::getPrefix
const asiolink::IOAddress & getPrefix() const
Returns prefix for the reservation.
Definition: host.h:135
isc::util
Definition: edns.h:19
isc
Defines the logger used by the top-level component of kea-dhcp-ddns.
Definition: agent_parser.cc:144
isc::db::DatabaseConnection::getParameter
std::string getParameter(const std::string &name) const
Returns value of a connection parameter.
Definition: database_connection.cc:28
isc::db::PsqlBindArrayPtr
boost::shared_ptr< PsqlBindArray > PsqlBindArrayPtr
Defines a smart pointer to PsqlBindArray.
Definition: pgsql_exchange.h:193
isc::Exception::what
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
Definition: exceptions/exceptions.cc:32
isc::dhcp::IPv6Resrv::Type
Type
Type of the reservation.
Definition: host.h:112
isc::db::DbOperationError
Exception thrown on failure to execute a database function.
Definition: database_connection.h:36
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition: exceptions/exceptions.h:192
LOG_DEBUG
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
dhcpsrv_log.h
isc::BadValue
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
Definition: exceptions/exceptions.h:132
isc::dhcp::PgSqlHostDataSourceImpl::DEL_HOST_SUBID6_ID
@ DEL_HOST_SUBID6_ID
Definition: pgsql_host_data_source.cc:1295
isc::dhcp::PgSqlHostDataSource::commit
virtual void commit()
Commit Transactions.
Definition: pgsql_host_data_source.cc:2247
isc::dhcp::PgSqlHostDataSourceImpl::GET_HOST_SUBID4_DHCPID
@ GET_HOST_SUBID4_DHCPID
Definition: pgsql_host_data_source.cc:1284
isc::dhcp::PgSqlHostDataSourceImpl::~PgSqlHostDataSourceImpl
~PgSqlHostDataSourceImpl()
Destructor.
Definition: pgsql_host_data_source.cc:1768
isc::db::PgSqlTransaction
RAII object representing a PostgreSQL transaction.
Definition: pgsql_connection.h:251
isc::dhcp::IPv6Resrv
IPv6 reservation for a host.
Definition: host.h:106
isc::dhcp
Definition: ctrl_dhcp4_srv.cc:75
isc::dhcp::PgSqlHostDataSourceImpl::StatementIndex
StatementIndex
Statement Tags.
Definition: pgsql_host_data_source.cc:1281
cfg_option.h
DHCP4_OPTION_SPACE
#define DHCP4_OPTION_SPACE
Definition: option_space.h:16
isc::data::JSONError
A standard Data module exception that is thrown if a parse error is encountered when constructing an ...
Definition: data.h:43
isc::util::OutputBuffer
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
pgsql_host_data_source.h
isc::dhcp::PgSqlHostDataSourceImpl::addOptions
void addOptions(const StatementIndex &stindex, const ConstCfgOptionPtr &options_cfg, const uint64_t host_id)
Inserts multiple options into the database.
Definition: pgsql_host_data_source.cc:1850
isc::dhcp::PgSqlHostDataSource::get6
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.
Definition: pgsql_host_data_source.cc:2159
option_definition.h
isc::db::MultipleRecords
Multiple lease records found where one expected.
Definition: db_exceptions.h:28
isc::dhcp::HostID
uint64_t HostID
HostID (used only when storing in MySQL, PostgreSQL or Cassandra)
Definition: host.h:28
isc::dhcp::PgSqlHostDataSourceImpl::addResv
void addResv(const IPv6Resrv &resv, const HostID &id)
Inserts IPv6 Reservation into ipv6_reservation table.
Definition: pgsql_host_data_source.cc:1830
buffer.h
isc::dhcp::PgSqlHostDataSource::PgSqlHostDataSource
PgSqlHostDataSource(const db::DatabaseConnection::ParameterMap &parameters)
Constructor.
Definition: pgsql_host_data_source.cc:1964
isc::dhcp::PgSqlHostDataSourceImpl::conn_
PgSqlConnection conn_
PgSQL connection.
Definition: pgsql_host_data_source.cc:1459
isc::dhcp::OptionDescriptor::option_
OptionPtr option_
Option instance.
Definition: cfg_option.h:38
isc::dhcp::PgSqlHostDataSourceImpl::host_ipv6_reservation_exchange_
boost::shared_ptr< PgSqlIPv6ReservationExchange > host_ipv6_reservation_exchange_
Pointer to an object representing an exchange which can be used to insert new IPv6 reservation.
Definition: pgsql_host_data_source.cc:1451
isc::dhcp::PgSqlHostDataSourceImpl::addStatement
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.
Definition: pgsql_host_data_source.cc:1772
isc::db::PG_SCHEMA_VERSION_MINOR
const uint32_t PG_SCHEMA_VERSION_MINOR
Definition: pgsql_connection.h:22
isc::db::PgSqlResult
RAII wrapper for PostgreSQL Result sets.
Definition: pgsql_connection.h:85
isc::db::OID_INT8
const size_t OID_INT8
Definition: pgsql_connection.h:60
isc::db::OID_INT2
const size_t OID_INT2
Definition: pgsql_connection.h:61
isc::dhcp::PgSqlHostDataSourceImpl::addOption
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.
Definition: pgsql_host_data_source.cc:1838
isc::db::PgSqlConnection::compareError
bool compareError(const PgSqlResult &r, const char *error_state)
Checks a result set's SQL state against an error state.
Definition: pgsql_connection.cc:283
isc::db::OID_BYTEA
const size_t OID_BYTEA
Definition: pgsql_connection.h:59
isc::dhcp::PgSqlHostDataSource::getName
virtual std::string getName() const
Returns the name of the open database.
Definition: pgsql_host_data_source.cc:2227
isc::db::PgSqlExchange
Base class for marshalling data to and from PostgreSQL.
Definition: pgsql_exchange.h:200
isc::dhcp::PgSqlHostDataSource::add
virtual void add(const HostPtr &host)
Adds a new host to the collection.
Definition: pgsql_host_data_source.cc:1973
isc::dhcp::PgSqlHostDataSourceImpl::GET_HOST_PREFIX
@ GET_HOST_PREFIX
Definition: pgsql_host_data_source.cc:1287
isc::dhcp::ConstHostCollection
std::vector< ConstHostPtr > ConstHostCollection
Collection of the const Host objects.
Definition: host.h:731
isc::db::PgSqlTransaction::commit
void commit()
Commits transaction.
Definition: pgsql_connection.cc:111
isc::db::DuplicateEntry
Database duplicate entry error.
Definition: db_exceptions.h:42
isc::dhcp::OptionPtr
boost::shared_ptr< Option > OptionPtr
Definition: option.h:37
isc::dhcp::ConstHostPtr
boost::shared_ptr< const Host > ConstHostPtr
Const pointer to the Host object.
Definition: host.h:728
isc::dhcp::OptionDescriptor
Option descriptor.
Definition: cfg_option.h:35
isc::dhcp::PgSqlHostDataSourceImpl::host_option_exchange_
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...
Definition: pgsql_host_data_source.cc:1456
isc::dhcp::IPv6Resrv::getPrefixLen
uint8_t getPrefixLen() const
Returns prefix length.
Definition: host.h:140
isc::dhcp::PgSqlHostDataSource::del4
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)
Definition: pgsql_host_data_source.cc:2040
isc::dhcp::CfgOptionPtr
boost::shared_ptr< CfgOption > CfgOptionPtr
Non-const pointer.
Definition: cfg_option.h:497
isc::dhcp::Option::Universe
Universe
defines option universe DHCPv4 or DHCPv6
Definition: option.h:67
isc::dhcp::tagged_statements
TaggedStatementArray tagged_statements
Prepared MySQL statements used by the backend to insert and retrieve hosts from the database.
Definition: mysql_host_data_source.cc:2137
isc::db::PsqlBindArray
Definition: pgsql_exchange.h:44
isc::dhcp::PgSqlHostDataSourceImpl::DEL_HOST_ADDR4
@ DEL_HOST_ADDR4
Definition: pgsql_host_data_source.cc:1293
isc::data::ConstElementPtr
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:23
isc::dhcp::PgSqlHostDataSourceImpl::getVersion
std::pair< uint32_t, uint32_t > getVersion() const
Returns PostgreSQL schema version of the open database.
Definition: pgsql_host_data_source.cc:1934
isc::db::DbOpenError
Exception thrown on failure to open database.
Definition: database_connection.h:29
isc::dhcp::SubnetID
uint32_t SubnetID
Unique identifier for a subnet (both v4 and v6)
Definition: lease.h:24
option.h
isc::dhcp::PgSqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR
@ GET_HOST_SUBID6_ADDR
Definition: pgsql_host_data_source.cc:1288
isc::dhcp::IPv6ResrvIterator
IPv6ResrvCollection::const_iterator IPv6ResrvIterator
Definition: host.h:186
isc::dhcp::Option
Definition: option.h:58
isc::db::PgSqlConnection::prepareStatements
void prepareStatements(const PgSqlTaggedStatement *start_statement, const PgSqlTaggedStatement *end_statement)
Prepare statements.
Definition: pgsql_connection.cc:142
isc::db::OID_TEXT
const size_t OID_TEXT
Definition: pgsql_connection.h:63
isc::dhcp::PgSqlHostDataSourceImpl::GET_HOST_SUBID_ADDR
@ GET_HOST_SUBID_ADDR
Definition: pgsql_host_data_source.cc:1286
isc::dhcp::PgSqlHostDataSourceImpl::host_ipv6_exchange_
boost::shared_ptr< PgSqlHostIPv6Exchange > host_ipv6_exchange_
Pointer to an object representing an exchange which can be used to retrieve hosts,...
Definition: pgsql_host_data_source.cc:1442
isc::dhcp::PgSqlHostDataSourceImpl::INSERT_V6_RESRV
@ INSERT_V6_RESRV
Definition: pgsql_host_data_source.cc:1290
isc::dhcp::OptionDescriptor::formatted_value_
std::string formatted_value_
Option value in textual (CSV) format.
Definition: cfg_option.h:59
isc::dhcp::PgSqlHostDataSourceImpl::DEL_HOST_SUBID4_ID
@ DEL_HOST_SUBID4_ID
Definition: pgsql_host_data_source.cc:1294
isc::dhcp::PgSqlHostDataSourceImpl::INSERT_V6_HOST_OPTION
@ INSERT_V6_HOST_OPTION
Definition: pgsql_host_data_source.cc:1292
LOG_INFO
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
isc::dhcp::dhcpsrv_logger
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition: dhcpsrv_log.h:56
isc::db::PgSqlConnection::commit
void commit()
Commit Transactions.
Definition: pgsql_connection.cc:344
isc::dhcp::IPv6ResrvRange
std::pair< IPv6ResrvIterator, IPv6ResrvIterator > IPv6ResrvRange
Definition: host.h:188
isc::dhcp::PgSqlHostDataSource::get4
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.
Definition: pgsql_host_data_source.cc:2117
isc::dhcp::PgSqlHostDataSource::getAll4
virtual ConstHostCollection getAll4(const asiolink::IOAddress &address) const
Returns a collection of hosts using the specified IPv4 address.
Definition: pgsql_host_data_source.cc:2101
isc::dhcp::PgSqlHostDataSource::del6
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)
Definition: pgsql_host_data_source.cc:2061