123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076 |
- // Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
- //
- // This Source Code Form is subject to the terms of the Mozilla Public
- // License, v. 2.0. If a copy of the MPL was not distributed with this
- // file, You can obtain one at http://mozilla.org/MPL/2.0/.
- #include <config.h>
- #include <dhcp/libdhcp++.h>
- #include <dhcp/option.h>
- #include <dhcp/option_definition.h>
- #include <dhcp/option_space.h>
- #include <dhcpsrv/db_exceptions.h>
- #include <dhcpsrv/cfg_option.h>
- #include <dhcpsrv/dhcpsrv_log.h>
- #include <dhcpsrv/pgsql_host_data_source.h>
- #include <dhcpsrv/db_exceptions.h>
- #include <util/buffer.h>
- #include <util/optional_value.h>
- #include <boost/algorithm/string/split.hpp>
- #include <boost/algorithm/string/classification.hpp>
- #include <boost/array.hpp>
- #include <boost/pointer_cast.hpp>
- #include <boost/static_assert.hpp>
- #include <stdint.h>
- #include <string>
- using namespace isc;
- using namespace isc::asiolink;
- using namespace isc::dhcp;
- using namespace isc::util;
- using namespace std;
- namespace {
- /// @brief Maximum length of option value.
- /// The maximum size of the raw option data that may be read from the
- /// database.
- const size_t OPTION_VALUE_MAX_LEN = 4096;
- /// @brief Numeric value representing last supported identifier.
- ///
- /// This value is used to validate whether the identifier type stored in
- /// a database is within bounds. of supported identifiers.
- const uint8_t MAX_IDENTIFIER_TYPE = static_cast<uint8_t>(Host::IDENT_CIRCUIT_ID);
- /// @brief Maximum length of DHCP identifier value.
- const size_t DHCP_IDENTIFIER_MAX_LEN = 128;
- /// @brief This class provides mechanisms for sending and retrieving
- /// information from the 'hosts' table.
- ///
- /// This class is used to insert and retrieve entries from the 'hosts' table.
- /// The queries used with this class do not retrieve IPv6 reservations or
- /// options associated with a host to minimize impact on performance. Other
- /// classes derived from @ref PgSqlHostExchange should be used to retrieve
- /// information about IPv6 reservations and options.
- class PgSqlHostExchange : public PgSqlExchange {
- private:
- /// @brief Column numbers for each column in the hosts table.
- /// These are used for both retrieving data and for looking up
- /// column labels for logging. Note that their numeric order
- /// MUST match that of the column order in the hosts table.
- static const int HOST_ID_COL = 0;
- static const int DHCP_IDENTIFIER_COL = 1;
- static const int DHCP_IDENTIFIER_TYPE_COL = 2;
- static const int DHCP4_SUBNET_ID_COL = 3;
- static const int DHCP6_SUBNET_ID_COL = 4;
- static const int IPV4_ADDRESS_COL = 5;
- static const int HOSTNAME_COL = 6;
- static const int DHCP4_CLIENT_CLASSES_COL = 7;
- static const int DHCP6_CLIENT_CLASSES_COL = 8;
- static const int DHCP4_NEXT_SERVER_COL = 9;
- static const int DHCP4_SERVER_HOSTNAME_COL = 10;
- static const int DHCP4_BOOT_FILE_NAME_COL = 11;
- /// @brief Number of columns returned for SELECT queries send by this class.
- static const size_t HOST_COLUMNS = 12;
- public:
- /// @brief Constructor
- ///
- /// @param additional_columns_num This value is set by the derived classes
- /// to indicate how many additional columns will be returned by SELECT
- /// queries performed by the derived class. This constructor will allocate
- /// resources for these columns, e.g. binding table, error indicators.
- PgSqlHostExchange(const size_t additional_columns_num = 0)
- : PgSqlExchange(HOST_COLUMNS + additional_columns_num) {
- // Set the column names for use by this class. This only comprises
- // names used by the PgSqlHostExchange class. Derived classes will
- // need to set names for the columns they use. Currently these are
- // only used for logging purposes.
- columns_[HOST_ID_COL] = "host_id";
- columns_[DHCP_IDENTIFIER_COL] = "dhcp_identifier";
- columns_[DHCP_IDENTIFIER_TYPE_COL] = "dhcp_identifier_type";
- columns_[DHCP4_SUBNET_ID_COL] = "dhcp4_subnet_id";
- columns_[DHCP6_SUBNET_ID_COL] = "dhcp6_subnet_id";
- columns_[IPV4_ADDRESS_COL] = "ipv4_address";
- columns_[HOSTNAME_COL] = "hostname";
- columns_[DHCP4_CLIENT_CLASSES_COL] = "dhcp4_client_classes";
- columns_[DHCP6_CLIENT_CLASSES_COL] = "dhcp6_client_classes";
- columns_[DHCP4_NEXT_SERVER_COL] = "dhcp4_next_server";
- columns_[DHCP4_SERVER_HOSTNAME_COL] = "dhcp4_server_hostname";
- columns_[DHCP4_BOOT_FILE_NAME_COL] = "dhcp4_boot_file_name";
- BOOST_STATIC_ASSERT(11 < HOST_COLUMNS);
- };
- /// @brief Virtual destructor.
- virtual ~PgSqlHostExchange() {
- }
- /// @brief Reinitializes state information
- ///
- /// This function should be called in between statement executions.
- /// Deriving classes should invoke this method as well as be reset
- /// all of their own stateful values.
- virtual void clear() {
- host_.reset();
- };
- /// @brief Returns index of the first uninitialized column name.
- ///
- /// This method is called by the derived classes to determine which
- /// column indexes are available for the derived classes within a
- /// binding array, error array and column names. This method
- /// determines the first available index by searching the first
- /// empty value within the columns_ vector. Previously we relied on
- /// the fixed values set for each class, but this was hard to maintain
- /// when new columns were added to the SELECT queries. It required
- /// modifying indexes in all derived classes.
- ///
- /// Derived classes must call this method in their constructors and
- /// use returned value as an index for the first column used by the
- /// derived class and increment this value for each subsequent column.
- size_t findAvailColumn() const {
- std::vector<std::string>::const_iterator empty_column =
- std::find(columns_.begin(), columns_.end(), std::string());
- return (std::distance(columns_.begin(), empty_column));
- }
- /// @brief Returns value of host id in the given row.
- ///
- /// This method is used to "look ahead" at the host_id in a row
- /// without having to call retrieveHost()
- HostID getHostId(const PgSqlResult& r, int row) {
- HostID host_id;
- getColumnValue(r, row, HOST_ID_COL, host_id);
- return (host_id);
- }
- /// @brief Populate a bind array from a host
- ///
- /// Constructs a PsqlBindArray for sending data stored in a Host object
- /// to the database.
- ///
- /// @param host Host object to be added to the database.
- /// None of the fields in the host reservation are modified -
- /// the host data is only read.
- ///
- /// @return pointer to newly constructed bind_array containing the
- /// bound values extracted from host
- ///
- /// @throw DbOperationError if bind_array cannot be populated.
- PsqlBindArrayPtr
- createBindForSend(const HostPtr& host) {
- if (!host) {
- isc_throw(BadValue, "createBindForSend:: host object is NULL");
- }
- // Store the host to ensure bound values remain in scope
- host_ = host;
- // Bind the host data to the array
- PsqlBindArrayPtr bind_array(new PsqlBindArray());
- try {
- // host_id : is auto_incremented skip it
- // dhcp_identifier : BYTEA NOT NULL
- bind_array->add(host->getIdentifier());
- // dhcp_identifier_type : SMALLINT NOT NULL
- bind_array->add(host->getIdentifierType());
- // dhcp4_subnet_id : INT NULL
- bind_array->add(host->getIPv4SubnetID());
- // dhcp6_subnet_id : INT NULL
- bind_array->add(host->getIPv6SubnetID());
- // ipv4_address : BIGINT NULL
- bind_array->add((host->getIPv4Reservation()));
- // hostname : VARCHAR(255) NULL
- bind_array->add(host->getHostname());
- // dhcp4_client_classes : VARCHAR(255) NULL
- // Override default separator to not include space after comma.
- bind_array->addTempString(host->getClientClasses4().toText(","));
- // dhcp6_client_classes : VARCHAR(255) NULL
- bind_array->addTempString(host->getClientClasses6().toText(","));
- // dhcp4_next_server : BIGINT NULL
- bind_array->add((host->getNextServer()));
- // dhcp4_server_hostname : VARCHAR(64)
- bind_array->add(host->getServerHostname());
- // dhcp4_boot_file_name : VARCHAR(128)
- bind_array->add(host->getBootFileName());
- } catch (const std::exception& ex) {
- host_.reset();
- isc_throw(DbOperationError,
- "Could not create bind array from Host: "
- << host->getHostname() << ", reason: " << ex.what());
- }
- return (bind_array);
- };
- /// @brief Processes one row of data fetched from a database.
- ///
- /// The processed data must contain host id, which uniquely identifies a
- /// host. This method creates a host and inserts it to the hosts collection
- /// only if the last inserted host has a different host id. This prevents
- /// adding duplicated hosts to the collection, assuming that processed
- /// rows are primarily ordered by host id column.
- ///
- /// This method must be overriden in the derived classes to also
- /// retrieve IPv6 reservations and DHCP options associated with a host.
- ///
- /// @param [out] hosts Collection of hosts to which a new host created
- /// from the processed data should be inserted.
- virtual void processRowData(ConstHostCollection& hosts,
- const PgSqlResult& r, int row) {
- // Peek at the host id , so we can skip it if we already have it
- // This lets us avoid constructing a copy of host for each
- // of its sub-rows (options, etc...)
- HostID row_host_id = getHostId(r, row);
- // Add new host only if there are no hosts or the host id of the
- // most recently added host is different than the host id of the
- // currently processed host.
- if (hosts.empty() || row_host_id != hosts.back()->getHostId()) {
- HostPtr host = retrieveHost(r, row, row_host_id);
- hosts.push_back(host);
- }
- }
- /// @brief Creates a Host object from a given row in a result set.
- ///
- /// @param r result set containing one or more rows from the hosts table
- /// @param row index within the result set of the row to process
- /// @param peeked_host_id if the caller has peeked ahead at the row's
- /// host_id, it can be passed in here to avoid fetching it from the row
- /// a second time.
- ///
- /// @return HostPtr to the newly created Host object
- /// @throw DbOperationError if the host cannot be created.
- HostPtr retrieveHost(const PgSqlResult& r, int row,
- const HostID& peeked_host_id = 0) {
- // If the caller peeked ahead at the host_id use that, otherwise
- // read it from the row.
- HostID host_id = (peeked_host_id ? peeked_host_id : getHostId(r,row));
- // dhcp_identifier : BYTEA NOT NULL
- uint8_t identifier_value[DHCP_IDENTIFIER_MAX_LEN];
- size_t identifier_len;
- convertFromBytea(r, row, DHCP_IDENTIFIER_COL, identifier_value,
- sizeof(identifier_value), identifier_len);
- // dhcp_identifier_type : SMALLINT NOT NULL
- uint8_t type;
- getColumnValue(r, row, DHCP_IDENTIFIER_TYPE_COL, type);
- if (type > MAX_IDENTIFIER_TYPE) {
- isc_throw(BadValue, "invalid dhcp identifier type returned: "
- << static_cast<int>(type));
- }
- Host::IdentifierType identifier_type =
- static_cast<Host::IdentifierType>(type);
- // dhcp4_subnet_id : INT NULL
- uint32_t subnet_id(0);
- if (!isColumnNull(r, row, DHCP4_SUBNET_ID_COL)) {
- getColumnValue(r, row, DHCP4_SUBNET_ID_COL, subnet_id);
- }
- SubnetID dhcp4_subnet_id = static_cast<SubnetID>(subnet_id);
- // dhcp6_subnet_id : INT NULL
- subnet_id = 0;
- if (!isColumnNull(r, row, DHCP6_SUBNET_ID_COL)) {
- getColumnValue(r, row, DHCP6_SUBNET_ID_COL, subnet_id);
- }
- SubnetID dhcp6_subnet_id = static_cast<SubnetID>(subnet_id);
- // ipv4_address : BIGINT NULL
- uint32_t addr4(0);
- if (!isColumnNull(r, row, IPV4_ADDRESS_COL)) {
- getColumnValue(r, row, IPV4_ADDRESS_COL, addr4);
- }
- isc::asiolink::IOAddress ipv4_reservation(addr4);
- // hostname : VARCHAR(255) NULL
- std::string hostname;
- if (!isColumnNull(r, row, HOSTNAME_COL)) {
- getColumnValue(r, row, HOSTNAME_COL, hostname);
- }
- // dhcp4_client_classes : VARCHAR(255) NULL
- std::string dhcp4_client_classes;
- if (!isColumnNull(r, row, DHCP4_CLIENT_CLASSES_COL)) {
- getColumnValue(r, row, DHCP4_CLIENT_CLASSES_COL, dhcp4_client_classes);
- }
- // dhcp6_client_classes : VARCHAR(255) NULL
- std::string dhcp6_client_classes;
- if (!isColumnNull(r, row, DHCP6_CLIENT_CLASSES_COL)) {
- getColumnValue(r, row, DHCP6_CLIENT_CLASSES_COL, dhcp6_client_classes);
- }
- // dhcp4_next_server : BIGINT NULL
- uint32_t dhcp4_next_server_as_uint32(0);
- if (!isColumnNull(r, row, DHCP4_NEXT_SERVER_COL)) {
- getColumnValue(r, row, DHCP4_NEXT_SERVER_COL, dhcp4_next_server_as_uint32);
- }
- isc::asiolink::IOAddress dhcp4_next_server(dhcp4_next_server_as_uint32);
- // dhcp4_server_hostname : VARCHAR(64)
- std::string dhcp4_server_hostname;
- if (!isColumnNull(r, row, DHCP4_SERVER_HOSTNAME_COL)) {
- getColumnValue(r, row, DHCP4_SERVER_HOSTNAME_COL, dhcp4_server_hostname);
- }
- // dhcp4_boot_file_name : VARCHAR(128)
- std::string dhcp4_boot_file_name;
- if (!isColumnNull(r, row, DHCP4_BOOT_FILE_NAME_COL)) {
- getColumnValue(r, row, DHCP4_BOOT_FILE_NAME_COL, dhcp4_boot_file_name);
- }
- // Finally, attempt to create the new host.
- HostPtr host;
- try {
- host.reset(new Host(identifier_value, identifier_len,
- identifier_type, dhcp4_subnet_id,
- dhcp6_subnet_id, ipv4_reservation, hostname,
- dhcp4_client_classes, dhcp6_client_classes,
- dhcp4_next_server, dhcp4_server_hostname,
- dhcp4_boot_file_name));
- host->setHostId(host_id);
- } catch (const isc::Exception& ex) {
- isc_throw(DbOperationError, "Could not create host: " << ex.what());
- }
- return(host);
- };
- protected:
- /// Pointer to Host object holding information to be inserted into
- /// Hosts table. This is used to retain scope.
- HostPtr host_;
- };
- /// @brief Extends base exchange class with ability to retrieve DHCP options
- /// from the 'dhcp4_options' and 'dhcp6_options' tables.
- ///
- /// This class provides means to retrieve both DHCPv4 and DHCPv6 options
- /// along with the host information. It is not used to retrieve IPv6
- /// reservations. The following types of queries are supported:
- /// - SELECT ? FROM hosts LEFT JOIN dhcp4_options LEFT JOIN dhcp6_options ...
- /// - SELECT ? FROM hosts LEFT JOIN dhcp4_options ...
- /// - SELECT ? FROM hosts LEFT JOIN dhcp6_options ...
- class PgSqlHostWithOptionsExchange : public PgSqlHostExchange {
- private:
- /// @brief Number of columns holding DHCPv4 or DHCPv6 option information.
- static const size_t OPTION_COLUMNS = 6;
- /// @brief Receives DHCPv4 or DHCPv6 options information from the
- /// dhcp4_options or dhcp6_options tables respectively.
- ///
- /// The PgSqlHostWithOptionsExchange class holds two respective instances
- /// of this class, one for receiving DHCPv4 options, one for receiving
- /// DHCPv6 options.
- ///
- /// The following are the basic functions of this class:
- /// - bind class members to specific columns in MySQL binding tables,
- /// - set DHCP options specific column names,
- /// - create instances of options retrieved from the database.
- ///
- /// The reason for isolating those functions in a separate C++ class is
- /// to prevent code duplication for handling DHCPv4 and DHCPv6 options.
- class OptionProcessor {
- public:
- /// @brief Constructor.
- ///
- /// @param universe V4 or V6. The type of the options' instances
- /// created by this class depends on this parameter.
- /// @param start_column Index of the first column to be used by this
- /// class.
- OptionProcessor(const Option::Universe& universe,
- const size_t start_column)
- : universe_(universe), start_column_(start_column),
- option_id_index_(start_column), code_index_(start_column_ + 1),
- value_index_(start_column_ + 2),
- formatted_value_index_(start_column_ + 3),
- space_index_(start_column_ + 4),
- persistent_index_(start_column_ + 5),
- most_recent_option_id_(0) {
- }
- /// @brief Reinitializes state information
- ///
- /// This function should be called prior to processing a fetched
- /// set of options.
- void clear() {
- most_recent_option_id_ = 0;
- }
- /// @brief Creates instance of the currently processed option.
- ///
- /// This method detects if the currently processed option is a new
- /// instance. It makes its determination by comparing the identifier
- /// of the currently processed option, with the most recently processed
- /// option. If the current value is greater than the id of the recently
- /// processed option it is assumed that the processed row holds new
- /// option information. In such case the option instance is created and
- /// inserted into the configuration passed as argument.
- ///
- /// This logic is necessary to deal with result sets made from multiple
- /// left joins which contain duplicated data. For instance queries
- /// returning both v4 and v6 options for a host would generate result
- /// sets similar to this:
- /// @code
- ///
- /// row 0: host-1 v4-opt-1 v6-opt-1
- /// row 1: host-1 v4-opt-1 v6-opt-2
- /// row 2: host-1 v4-opt-1 v6-opt-3
- /// row 4: host-1 v4-opt-2 v6-opt-1
- /// row 5: host-1 v4-opt-2 v6-opt-2
- /// row 6: host-1 v4-opt-2 v6-opt-3
- /// row 7: host-2 v4-opt-1 v6-opt-1
- /// row 8: host-2 v4-opt-2 v6-opt-1
- /// :
- /// @endcode
- ///
- /// @param cfg Pointer to the configuration object into which new
- /// option instances should be inserted.
- /// @param r result set containing one or more rows from a dhcp
- /// options table.
- /// @param row index within the result set of the row to process
- void retrieveOption(const CfgOptionPtr& cfg, const PgSqlResult& r,
- int row) {
- // If the option id on this row is NULL, then there's no
- // option of this type (4/6) on this row to fetch, so bail.
- if (PgSqlExchange::isColumnNull(r, row, option_id_index_)) {
- return;
- }
- // option_id: INT
- uint64_t option_id;
- PgSqlExchange::getColumnValue(r, row, option_id_index_, option_id);
- // The row option id must be greater than id if the most recent
- // option because they are ordered by option id. Otherwise
- // we assume that we have already processed this option.
- if (most_recent_option_id_ >= option_id) {
- return;
- }
- // Remember current option id as the most recent processed one. We
- // will be comparing it with option ids in subsequent rows.
- most_recent_option_id_ = option_id;
- // code: SMALLINT NOT NULL
- uint16_t code;
- PgSqlExchange::getColumnValue(r, row, code_index_, code);
- // value: BYTEA
- uint8_t value[OPTION_VALUE_MAX_LEN];
- size_t value_len(0);
- if (!isColumnNull(r, row, value_index_)) {
- PgSqlExchange::convertFromBytea(r, row, value_index_, value,
- sizeof(value), value_len);
- }
- // formatted_value: TEXT
- std::string formatted_value;
- if (!isColumnNull(r, row, formatted_value_index_)) {
- PgSqlExchange::getColumnValue(r, row, formatted_value_index_,
- formatted_value);
- }
- // space: VARCHAR(128)
- std::string space;
- if (!isColumnNull(r, row, space_index_)) {
- PgSqlExchange::getColumnValue(r, row, space_index_, space);
- }
- // If empty or null space provided, use a default top level space.
- if (space.empty()) {
- space = (universe_ == Option::V4 ? "dhcp4" : "dhcp6");
- }
- // persistent: BOOL default false
- bool persistent;
- PgSqlExchange::getColumnValue(r, row, persistent_index_,
- persistent);
- // Options are held in a binary or textual format in the database.
- // This is similar to having an option specified in a server
- // configuration file. Such option is converted to appropriate C++
- // class, using option definition. Thus, we need to find the
- // option definition for this option code and option space.
- // If the option space is a standard DHCPv4 or DHCPv6 option space,
- // this is most likely a standard option, for which we have a
- // definition created within libdhcp++.
- OptionDefinitionPtr def = LibDHCP::getOptionDef(space, code);
- // Otherwise, we may check if this an option encapsulated within the
- // vendor space.
- if (!def && (space != DHCP4_OPTION_SPACE) &&
- (space != DHCP6_OPTION_SPACE)) {
- uint32_t vendor_id = LibDHCP::optionSpaceToVendorId(space);
- if (vendor_id > 0) {
- def = LibDHCP::getVendorOptionDef(universe_, vendor_id,
- code);
- }
- }
- // In all other cases, we use runtime option definitions, which
- // should be also registered within the libdhcp++.
- if (!def) {
- def = LibDHCP::getRuntimeOptionDef(space, code);
- }
- OptionPtr option;
- if (!def) {
- // If no definition found, we use generic option type.
- OptionBuffer buf(value, value + value_len);
- option.reset(new Option(universe_, code, buf.begin(),
- buf.end()));
- } else {
- // The option value may be specified in textual or binary format
- // in the database. If formatted_value is empty, the binary
- // format is used. Depending on the format we use a different
- // variant of the optionFactory function.
- if (formatted_value.empty()) {
- OptionBuffer buf(value, value + value_len);
- option = def->optionFactory(universe_, code, buf.begin(),
- buf.end());
- } else {
- // Spit the value specified in comma separated values
- // format.
- std::vector<std::string> split_vec;
- boost::split(split_vec, formatted_value,
- boost::is_any_of(","));
- option = def->optionFactory(universe_, code, split_vec);
- }
- }
- OptionDescriptor desc(option, persistent, formatted_value);
- cfg->add(desc, space);
- }
- /// @brief Specify column names.
- ///
- /// @param [out] columns Reference to a vector holding names of option
- /// specific columns.
- void setColumnNames(std::vector<std::string>& columns) {
- columns[option_id_index_] = "option_id";
- columns[code_index_] = "code";
- columns[value_index_] = "value";
- columns[formatted_value_index_] = "formatted_value";
- columns[space_index_] = "space";
- columns[persistent_index_] = "persistent";
- }
- private:
- /// @brief Universe: V4 or V6.
- Option::Universe universe_;
- /// @brief Index of first column used by this class.
- size_t start_column_;
- //@}
- /// @name Indexes of the specific columns
- //@{
- /// @brief Option id
- size_t option_id_index_;
- /// @brief Code
- size_t code_index_;
- /// @brief Value
- size_t value_index_;
- /// @brief Formatted value
- size_t formatted_value_index_;
- /// @brief Space
- size_t space_index_;
- /// @brief Persistent
- size_t persistent_index_;
- //@}
- /// @brief Option id for last processed row.
- uint64_t most_recent_option_id_;
- };
- /// @brief Pointer to the @ref OptionProcessor class.
- typedef boost::shared_ptr<OptionProcessor> OptionProcessorPtr;
- public:
- /// @brief DHCP option types to be fetched from the database.
- ///
- /// Supported types are:
- /// - Only DHCPv4 options,
- /// - Only DHCPv6 options,
- /// - Both DHCPv4 and DHCPv6 options.
- enum FetchedOptions {
- DHCP4_ONLY,
- DHCP6_ONLY,
- DHCP4_AND_DHCP6
- };
- /// @brief Constructor.
- ///
- /// @param fetched_options Specifies if DHCPv4, DHCPv6 or both should
- /// be fetched from the database for a host.
- /// @param additional_columns_num Number of additional columns for which
- /// resources should be allocated, e.g. binding table, column names etc.
- /// This parameter should be set to a non zero value by derived classes to
- /// allocate resources for the columns supported by derived classes.
- PgSqlHostWithOptionsExchange(const FetchedOptions& fetched_options,
- const size_t additional_columns_num = 0)
- : PgSqlHostExchange(getRequiredColumnsNum(fetched_options)
- + additional_columns_num),
- opt_proc4_(), opt_proc6_() {
- // Create option processor for DHCPv4 options, if required.
- if ((fetched_options == DHCP4_ONLY) ||
- (fetched_options == DHCP4_AND_DHCP6)) {
- opt_proc4_.reset(new OptionProcessor(Option::V4,
- findAvailColumn()));
- opt_proc4_->setColumnNames(columns_);
- }
- // Create option processor for DHCPv6 options, if required.
- if ((fetched_options == DHCP6_ONLY) ||
- (fetched_options == DHCP4_AND_DHCP6)) {
- opt_proc6_.reset(new OptionProcessor(Option::V6,
- findAvailColumn()));
- opt_proc6_->setColumnNames(columns_);
- }
- }
- /// @brief Clears state information
- ///
- /// This function should be called in between statement executions.
- /// Deriving classes should invoke this method as well as be reset
- /// all of their own stateful values.
- virtual void clear() {
- PgSqlHostExchange::clear();
- if (opt_proc4_) {
- opt_proc4_->clear();
- }
- if (opt_proc6_) {
- opt_proc6_->clear();
- }
- }
- /// @brief Processes the current row.
- ///
- /// The fetched row includes both host information and DHCP option
- /// information. Because the SELECT queries use one or more LEFT JOIN
- /// clauses, the result set may contain duplicated host or options
- /// entries. This method detects duplicated information and discards such
- /// entries.
- ///
- /// @param [out] hosts Container holding parsed hosts and options.
- virtual void processRowData(ConstHostCollection& hosts,
- const PgSqlResult& r, int row) {
- HostPtr current_host;
- if (hosts.empty()) {
- // Must be the first one, fetch it.
- current_host = retrieveHost(r, row);
- hosts.push_back(current_host);
- } else {
- // Peek at the host id so we can skip it if we already have
- // this host. This lets us avoid retrieving the host needlessly
- // for each of its sub-rows (options, etc...).
- HostID row_host_id = getHostId(r, row);
- current_host = boost::const_pointer_cast<Host>(hosts.back());
- // if the row's host id is greater than the one we've been
- // working on we're starting a new host, so fetch it.
- if (row_host_id > current_host->getHostId()) {
- current_host = retrieveHost(r, row, row_host_id);
- hosts.push_back(current_host);
- }
- }
- // Parse DHCPv4 options if required to do so.
- if (opt_proc4_) {
- CfgOptionPtr cfg = current_host->getCfgOption4();
- opt_proc4_->retrieveOption(cfg, r, row);
- }
- // Parse DHCPv6 options if required to do so.
- if (opt_proc6_) {
- CfgOptionPtr cfg = current_host->getCfgOption6();
- opt_proc6_->retrieveOption(cfg, r, row);
- }
- }
- private:
- /// @brief Returns a number of columns required to retrieve option data.
- ///
- /// Depending if we need DHCPv4/DHCPv6 options only, or both DHCPv4 and
- /// DHCPv6 a different number of columns is required in the binding array.
- /// This method returns the number of required columns, according to the
- /// value of @c fetched_columns passed in the constructor.
- ///
- /// @param fetched_columns A value which specifies whether DHCPv4, DHCPv6 or
- /// both types of options should be retrieved.
- ///
- /// @return Number of required columns.
- static size_t getRequiredColumnsNum(const FetchedOptions& fetched_options) {
- return (fetched_options == DHCP4_AND_DHCP6 ? 2 * OPTION_COLUMNS :
- OPTION_COLUMNS);
- }
- /// @brief Pointer to DHCPv4 options processor.
- ///
- /// If this object is NULL, the DHCPv4 options are not fetched.
- OptionProcessorPtr opt_proc4_;
- /// @brief Pointer to DHCPv6 options processor.
- ///
- /// If this object is NULL, the DHCPv6 options are not fetched.
- OptionProcessorPtr opt_proc6_;
- };
- /// @brief This class provides mechanisms for sending and retrieving
- /// host information, DHCPv4 options, DHCPv6 options and IPv6 reservations.
- ///
- /// This class extends the @ref PgSqlHostWithOptionsExchange class with the
- /// mechanisms to retrieve IPv6 reservations. This class is used in situations
- /// when it is desired to retrieve DHCPv6 specific information about the host
- /// (DHCPv6 options and reservations), or entire information about the host
- /// (DHCPv4 options, DHCPv6 options and reservations). The following are the
- /// queries used with this class:
- /// - SELECT ? FROM hosts LEFT JOIN dhcp4_options LEFT JOIN dhcp6_options
- /// LEFT JOIN ipv6_reservations ...
- /// - SELECT ? FROM hosts LEFT JOIN dhcp6_options LEFT JOIN ipv6_reservations ..
- class PgSqlHostIPv6Exchange : public PgSqlHostWithOptionsExchange {
- private:
- /// @brief Number of columns holding IPv6 reservation information.
- static const size_t RESERVATION_COLUMNS = 5;
- public:
- /// @brief Constructor.
- ///
- /// Apart from initializing the base class data structures it also
- /// initializes values representing IPv6 reservation information.
- PgSqlHostIPv6Exchange(const FetchedOptions& fetched_options)
- : PgSqlHostWithOptionsExchange(fetched_options, RESERVATION_COLUMNS),
- reservation_id_index_(findAvailColumn()),
- address_index_(reservation_id_index_ + 1),
- prefix_len_index_(reservation_id_index_ + 2),
- type_index_(reservation_id_index_ + 3),
- iaid_index_(reservation_id_index_ + 4),
- most_recent_reservation_id_(0) {
- // Provide names of additional columns returned by the queries.
- columns_[reservation_id_index_] = "reservation_id";
- columns_[address_index_] = "address";
- columns_[prefix_len_index_] = "prefix_len";
- columns_[type_index_] = "type";
- columns_[iaid_index_] = "dhcp6_iaid";
- BOOST_STATIC_ASSERT(4 < RESERVATION_COLUMNS);
- }
- /// @brief Reinitializes state information
- ///
- /// This function should be called in between statement executions.
- /// Deriving classes should invoke this method as well as be reset
- /// all of their own stateful values.
- void clear() {
- PgSqlHostWithOptionsExchange::clear();
- most_recent_reservation_id_ = 0;
- }
- /// @brief Returns reservation id from the row.
- ///
- /// @return Reservation id or 0 if no reservation data is fetched.
- uint64_t getReservationId(const PgSqlResult& r, int row) const {
- uint64_t resv_id = 0;
- if (!isColumnNull(r, row, reservation_id_index_)) {
- getColumnValue(r, row, reservation_id_index_, resv_id);
- }
- return (resv_id);
- };
- /// @brief Creates IPv6 reservation from the data contained in the
- /// currently processed row.
- ///
- /// Called after the MYSQL_BIND array created by createBindForReceive().
- ///
- /// @return IPv6Resrv object (containing IPv6 address or prefix reservation)
- IPv6Resrv retrieveReservation(const PgSqlResult& r, int row) {
- // type: SMALLINT NOT NULL
- uint16_t tmp;
- getColumnValue(r, row, type_index_, tmp);
- // Convert it to IPv6 Reservation type (0 = IA_NA, 2 = IA_PD)
- IPv6Resrv::Type resv_type;
- switch (tmp) {
- case 0:
- resv_type = IPv6Resrv::TYPE_NA;
- break;
- case 2:
- resv_type = IPv6Resrv::TYPE_PD;
- break;
- default:
- isc_throw(BadValue,
- "invalid IPv6 reservation type returned: "
- << tmp << ". Only 0 or 2 are allowed.");
- }
- // address VARCHAR(39) NOT NULL
- isc::asiolink::IOAddress address(getIPv6Value(r, row, address_index_));
- // prefix_len: SMALLINT NOT NULL
- uint16_t prefix_len;
- getColumnValue(r, row, prefix_len_index_, prefix_len);
- // @todo once we support populating iaid
- // iaid: INT
- // int iaid;
- // getColumnValue(r, row, iaid_index_, iaid);
- // Create the reservation.
- IPv6Resrv reservation(resv_type, IOAddress(address), prefix_len);
- return (reservation);
- };
- /// @brief Processes one row of data fetched from a database.
- ///
- /// The processed data must contain host id, which uniquely identifies a
- /// host. This method creates a host and inserts it to the hosts collection
- /// only if the last inserted host has a different host id. This prevents
- /// adding duplicated hosts to the collection, assuming that processed
- /// rows are primarily ordered by host id column.
- ///
- /// Depending on the value of the @c fetched_options specified in the
- /// constructor, this method also parses options returned as a result
- /// of SELECT queries.
- ///
- /// For any returned row which contains IPv6 reservation information it
- /// checks if the reservation is not a duplicate of previously parsed
- /// reservation and appends the IPv6Resrv object into the host object
- /// if the parsed row contains new reservation information.
- ///
- /// @param [out] hosts Collection of hosts to which a new host created
- /// from the processed data should be inserted.
- /// @param r result set containing one or more rows of fetched data
- /// @param row index within the result set of the row to process
- virtual void processRowData(ConstHostCollection& hosts,
- const PgSqlResult& r, int row) {
- // Call parent class to fetch host information and options.
- PgSqlHostWithOptionsExchange::processRowData(hosts, r, row);
- // Shouldn't happen but just in case
- if (hosts.empty()) {
- isc_throw(Unexpected, "no host information while retrieving"
- " IPv6 reservation");
- }
- // If we have reservation id we havent' seen yet, retrieve the
- // the reservation, adding it to the current host
- uint64_t reservation_id = getReservationId(r, row);
- if (reservation_id && (reservation_id > most_recent_reservation_id_)) {
- HostPtr host = boost::const_pointer_cast<Host>(hosts.back());
- host->addReservation(retrieveReservation(r, row));
- most_recent_reservation_id_ = reservation_id;
- }
- }
- private:
- /// @name Indexes of columns holding information about IPv6 reservations.
- //@{
- /// @brief Index of reservation_id column.
- size_t reservation_id_index_;
- /// @brief Index of address column.
- size_t address_index_;
- /// @brief Index of prefix_len column.
- size_t prefix_len_index_;
- /// @brief Index of type column.
- size_t type_index_;
- /// @brief Index of IAID column.
- size_t iaid_index_;
- //@}
- /// @brief Reservation id for last processed row.
- uint64_t most_recent_reservation_id_;
- };
- /// @brief This class is used for storing IPv6 reservations in a MySQL database.
- ///
- /// This class is only used to insert IPv6 reservations into the
- /// ipv6_reservations table. It is not used to retrieve IPv6 reservations. To
- /// retrieve IPv6 reservation the @ref PgSqlIPv6HostExchange class should be
- /// used instead.
- ///
- /// When a new IPv6 reservation is inserted into the database, an appropriate
- /// host must be defined in the hosts table. An attempt to insert IPv6
- /// reservation for non-existing host will result in failure.
- class PgSqlIPv6ReservationExchange : public PgSqlExchange {
- private:
- /// @brief Set number of columns for ipv6_reservation table.
- static const size_t RESRV_COLUMNS = 6;
- public:
- /// @brief Constructor
- ///
- /// Initialize class members representing a single IPv6 reservation.
- PgSqlIPv6ReservationExchange()
- : PgSqlExchange(RESRV_COLUMNS),
- resv_(IPv6Resrv::TYPE_NA, asiolink::IOAddress("::"), 128) {
- // Set the column names (for error messages)
- columns_[0] = "host_id";
- columns_[1] = "address";
- columns_[2] = "prefix_len";
- columns_[3] = "type";
- columns_[4] = "dhcp6_iaid";
- BOOST_STATIC_ASSERT(5 < RESRV_COLUMNS);
- }
- /// @brief Populate a bind array representing an IPv6 reservation
- ///
- /// Constructs a PsqlBindArray for an IPv6 reservation to the database.
- ///
- /// @param resv The IPv6 reservation to be added to the database.
- /// None of the fields in the reservation are modified -
- /// @param host_id ID of the host to which this reservation belongs.
- ///
- /// @return pointer to newly constructed bind_array containing the
- /// bound values extracted the IPv6 reservation
- ///
- /// @throw DbOperationError if bind_array cannot be populated.
- PsqlBindArrayPtr createBindForSend(const IPv6Resrv& resv,
- const HostID& host_id) {
- // Store the values to ensure they remain valid.
- // Technically we don't need this, as currently all the values
- // are converted to strings and stored by the bind array.
- resv_ = resv;
- PsqlBindArrayPtr bind_array(new PsqlBindArray());
- try {
- // address VARCHAR(39) NOT NULL
- bind_array->add(resv.getPrefix());
- // prefix_len: SMALLINT NOT NULL
- bind_array->add(resv.getPrefixLen());
- // type: SMALLINT NOT NULL
- // See lease6_types table for values (0 = IA_NA, 2 = IA_PD)
- uint16_t type = resv.getType() == IPv6Resrv::TYPE_NA ? 0 : 2;
- bind_array->add(type);
- // dhcp6_iaid: INT UNSIGNED
- /// @todo: We don't support iaid in the IPv6Resrv yet.
- bind_array->addNull();
- // host_id: BIGINT NOT NULL
- bind_array->add(host_id);
- } catch (const std::exception& ex) {
- isc_throw(DbOperationError,
- "Could not create bind array from IPv6 Reservation: "
- << resv_.toText() << ", reason: " << ex.what());
- }
- return (bind_array);
- }
- private:
- /// @brief Object holding reservation being sent to the database.
- IPv6Resrv resv_;
- };
- /// @brief This class is used for inserting options into a database.
- ///
- /// This class supports inserting both DHCPv4 and DHCPv6 options.
- class PgSqlOptionExchange : public PgSqlExchange {
- private:
- static const int OPTION_ID_COL = 0;
- static const int CODE_COL = 1;
- static const int VALUE_COL = 2;
- static const int FORMATTED_VALUE_COL = 3;
- static const int SPACE_COL = 4;
- static const int PERSISTENT_COL = 5;
- static const int DHCP_CLIENT_CLASS_COL = 6;
- static const int DHCP_SUBNET_ID_COL = 7;
- static const int HOST_ID_COL = 8;
- static const int SCOPE_ID_COL = 9;
- /// @brief Number of columns in the tables holding options.
- static const size_t OPTION_COLUMNS = 10;
- public:
- /// @brief Constructor.
- PgSqlOptionExchange()
- : PgSqlExchange(OPTION_COLUMNS), value_(),
- value_len_(0), option_() {
- columns_[OPTION_ID_COL] = "option_id";
- columns_[CODE_COL] = "code";
- columns_[VALUE_COL] = "value";
- columns_[FORMATTED_VALUE_COL] = "formatted_value";
- columns_[SPACE_COL] = "space";
- columns_[PERSISTENT_COL] = "persistent";
- columns_[DHCP_CLIENT_CLASS_COL] = "dhcp_client_class";
- columns_[DHCP_SUBNET_ID_COL] = "dhcp_subnet_id";
- columns_[HOST_ID_COL] = "host_id";
- columns_[SCOPE_ID_COL] = "scope_id";
- BOOST_STATIC_ASSERT(9 < OPTION_COLUMNS);
- }
- /// @brief Creates binding array to insert option data into database.
- ///
- /// @param opt_desc option descriptor of the option to write
- /// @param opt_space name of the option space to which the option belongs
- /// @param host_id host id of the host to which the option belongs
- ///
- /// @return pointer to newly constructed bind_array containing the
- /// bound values extracted from host
- PsqlBindArrayPtr
- createBindForSend(const OptionDescriptor& opt_desc,
- const std::string& opt_space,
- const HostID& host_id) {
- // Hold pointer to the option to make sure it remains valid until
- // we complete a query.
- option_ = opt_desc.option_;
- // Create the bind-array
- PsqlBindArrayPtr bind_array(new PsqlBindArray());
- try {
- // option_id: is auto_incremented so skip it
- // code: SMALLINT UNSIGNED NOT NULL
- bind_array->add(option_->getType());
- // value: BYTEA NULL
- if (opt_desc.formatted_value_.empty() &&
- (opt_desc.option_->len() > opt_desc.option_->getHeaderLen())) {
- // The formatted_value is empty and the option value is
- // non-empty so we need to prepare on-wire format for the
- // option and store it in the database as a BYTEA.
- OutputBuffer buf(opt_desc.option_->len());
- opt_desc.option_->pack(buf);
- const char* buf_ptr = static_cast<const char*>(buf.getData());
- value_.assign(buf_ptr + opt_desc.option_->getHeaderLen(),
- buf_ptr + buf.getLength());
- value_len_ = value_.size();
- bind_array->add(value_);
- } else {
- // No value or formatted_value specified. In this case, the
- // value BYTEA should be NULL.
- bind_array->addNull(PsqlBindArray::BINARY_FMT);
- }
- // formatted_value: TEXT NULL,
- if (!opt_desc.formatted_value_.empty()) {
- bind_array->addTempString(opt_desc.formatted_value_);
- } else {
- bind_array->addNull();
- }
- // space: VARCHAR(128) NULL
- if (!opt_space.empty()) {
- bind_array->addTempString(opt_space);
- } else {
- bind_array->addNull();
- }
- // persistent: BOOLEAN DEFAULT false
- bind_array->add(opt_desc.persistent_);
- // host_id: INT NULL
- if (!host_id) {
- isc_throw(BadValue, "host_id cannot be null");
- }
- bind_array->add(host_id);
- } catch (const std::exception& ex) {
- isc_throw(DbOperationError,
- "Could not create bind array for inserting DHCP "
- "host option: " << option_->toText() << ", reason: "
- << ex.what());
- }
- return (bind_array);
- }
- private:
- /// @brief Option value as binary.
- std::vector<uint8_t> value_;
- /// @brief Option value length.
- size_t value_len_;
- /// @brief Pointer to currently parsed option.
- OptionPtr option_;
- };
- } // end of anonymous namespace
- namespace isc {
- namespace dhcp {
- /// @brief Implementation of the @ref PgSqlHostDataSource.
- class PgSqlHostDataSourceImpl {
- public:
- /// @brief Statement Tags
- ///
- /// The contents of the enum are indexes into the list of SQL statements.
- /// It is assumed that the order is such that the indices of statements
- /// reading the database are less than those of statements modifying the
- /// database.
- enum StatementIndex {
- GET_HOST_DHCPID, // Gets hosts by host identifier
- GET_HOST_ADDR, // Gets hosts by IPv4 address
- GET_HOST_SUBID4_DHCPID, // Gets host by IPv4 SubnetID, HW address/DUID
- GET_HOST_SUBID6_DHCPID, // Gets host by IPv6 SubnetID, HW address/DUID
- GET_HOST_SUBID_ADDR, // Gets host by IPv4 SubnetID and IPv4 address
- GET_HOST_PREFIX, // Gets host by IPv6 prefix
- GET_HOST_SUBID6_ADDR, // Gets host by IPv6 SubnetID and IPv6 prefix
- GET_VERSION, // Obtain version number
- INSERT_HOST, // Insert new host to collection
- INSERT_V6_RESRV, // Insert v6 reservation
- INSERT_V4_HOST_OPTION, // Insert DHCPv4 option
- INSERT_V6_HOST_OPTION, // Insert DHCPv6 option
- NUM_STATEMENTS // Number of statements
- };
- /// @brief Index of first statement performing write to the database.
- ///
- /// This value is used to mark border line between queries and other
- /// statements and statements performing write operation on the database,
- /// such as INSERT, DELETE, UPDATE.
- static const StatementIndex WRITE_STMTS_BEGIN = INSERT_HOST;
- /// @brief Constructor.
- ///
- /// This constructor opens database connection and initializes prepared
- /// statements used in the queries.
- PgSqlHostDataSourceImpl(const PgSqlConnection::ParameterMap& parameters);
- /// @brief Destructor.
- ~PgSqlHostDataSourceImpl();
- /// @brief Executes statements which insert a row into one of the tables.
- ///
- /// @param stindex Index of a statement being executed.
- /// @param bind Vector of MYSQL_BIND objects to be used when making the
- /// query.
- /// @param return_last_id flag indicating whether or not the insert
- /// returns the primary key of from the row inserted via " RETURNING
- /// <primary key> as pid" clause on the INSERT statement. The RETURNING
- /// clause causes the INSERT to return a result set that should consist
- /// of a single row with one column, the value of the primary key.
- /// Defaults to false.
- ///
- /// @return 0 if return_last_id is false, otherwise it returns the
- /// the value in the result set in the first col of the first row.
- ///
- /// @throw isc::dhcp::DuplicateEntry Database throws duplicate entry error
- uint64_t addStatement(PgSqlHostDataSourceImpl::StatementIndex stindex,
- PsqlBindArrayPtr& bind,
- const bool return_last_id = false);
- /// @brief Inserts IPv6 Reservation into ipv6_reservation table.
- ///
- /// @param resv IPv6 Reservation to be added
- /// @param id ID of a host owning this reservation
- void addResv(const IPv6Resrv& resv, const HostID& id);
- /// @brief Inserts a single DHCP option into the database.
- ///
- /// @param stindex Index of a statement being executed.
- /// @param opt_desc Option descriptor holding information about an option
- /// to be inserted into the database.
- /// @param opt_space Option space name.
- /// @param subnet_id Subnet identifier.
- /// @param host_id Host identifier.
- void addOption(const PgSqlHostDataSourceImpl::StatementIndex& stindex,
- const OptionDescriptor& opt_desc,
- const std::string& opt_space,
- const OptionalValue<SubnetID>& subnet_id,
- const HostID& host_id);
- /// @brief Inserts multiple options into the database.
- ///
- /// @param stindex Index of a statement being executed.
- /// @param options_cfg An object holding a collection of options to be
- /// inserted into the database.
- /// @param host_id Host identifier retrieved using @c mysql_insert_id.
- void addOptions(const StatementIndex& stindex,
- const ConstCfgOptionPtr& options_cfg,
- const uint64_t host_id);
- /// @brief Creates collection of @ref Host objects with associated
- /// information such as IPv6 reservations and/or DHCP options.
- ///
- /// This method performs a query which returns host information from
- /// the 'hosts' table. The query may also use LEFT JOIN clause to
- /// retrieve information from other tables, e.g. ipv6_reservations,
- /// dhcp4_options and dhcp6_options.
- /// Whether IPv6 reservations and/or options are assigned to the
- /// @ref Host objects depends on the type of the exchange object.
- ///
- /// @param stindex Statement index.
- /// @param bind Pointer to an array of MySQL bindings.
- /// @param exchange Pointer to the exchange object used for the
- /// particular query.
- /// @param [out] result Reference to the collection of hosts returned.
- /// @param single A boolean value indicating if a single host is
- /// expected to be returned, or multiple hosts.
- void getHostCollection(StatementIndex stindex, PsqlBindArrayPtr bind,
- boost::shared_ptr<PgSqlHostExchange> exchange,
- ConstHostCollection& result, bool single) const;
- /// @brief Retrieves a host by subnet and client's unique identifier.
- ///
- /// This method is used by both PgSqlHostDataSource::get4 and
- /// PgSqlHostDataSource::get6 methods.
- ///
- /// @param subnet_id Subnet identifier.
- /// @param identifier_type Identifier type.
- /// @param identifier_begin Pointer to a beginning of a buffer containing
- /// an identifier.
- /// @param identifier_len Identifier length.
- /// @param stindex Statement index.
- /// @param exchange Pointer to the exchange object used for the
- /// particular query.
- ///
- /// @return Pointer to const instance of Host or null pointer if
- /// no host found.
- 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;
- /// @brief Throws exception if database is read only.
- ///
- /// This method should be called by the methods which write to the
- /// database. If the backend is operating in read-only mode this
- /// method will throw exception.
- ///
- /// @throw DbReadOnly if backend is operating in read only mode.
- void checkReadOnly() const;
- /// @brief Returns PostgreSQL schema version of the open database
- ///
- /// @return Version number stored in the database, as a pair of unsigned
- /// integers. "first" is the major version number, "second" the
- /// minor number.
- ///
- /// @throw isc::dhcp::DbOperationError An operation on the open database
- /// has failed.
- std::pair<uint32_t, uint32_t> getVersion() const;
- /// @brief Pointer to the object representing an exchange which
- /// can be used to retrieve hosts and DHCPv4 options.
- boost::shared_ptr<PgSqlHostWithOptionsExchange> host_exchange_;
- /// @brief Pointer to an object representing an exchange which can
- /// be used to retrieve hosts, DHCPv6 options and IPv6 reservations.
- boost::shared_ptr<PgSqlHostIPv6Exchange> host_ipv6_exchange_;
- /// @brief Pointer to an object representing an exchange which can
- /// be used to retrieve hosts, DHCPv4 and DHCPv6 options, and
- /// IPv6 reservations using a single query.
- boost::shared_ptr<PgSqlHostIPv6Exchange> host_ipv46_exchange_;
- /// @brief Pointer to an object representing an exchange which can
- /// be used to insert new IPv6 reservation.
- boost::shared_ptr<PgSqlIPv6ReservationExchange> host_ipv6_reservation_exchange_;
- /// @brief Pointer to an object representing an exchange which can
- /// be used to insert DHCPv4 or DHCPv6 option into dhcp4_options
- /// or dhcp6_options table.
- boost::shared_ptr<PgSqlOptionExchange> host_option_exchange_;
- /// @brief MySQL connection
- PgSqlConnection conn_;
- /// @brief Indicates if the database is opened in read only mode.
- bool is_readonly_;
- };
- namespace {
- /// @brief Array of tagged statements.
- typedef boost::array<PgSqlTaggedStatement, PgSqlHostDataSourceImpl::NUM_STATEMENTS>
- TaggedStatementArray;
- /// @brief Prepared PosgreSQL statements used by the backend to insert and
- /// retrieve reservation data from the database.
- TaggedStatementArray tagged_statements = { {
- // PgSqlHostDataSourceImpl::GET_HOST_DHCPID
- // Retrieves host information, IPv6 reservations and both DHCPv4 and
- // DHCPv6 options associated with the host. The LEFT JOIN clause is used
- // to retrieve information from 4 different tables using a single query.
- // Hence, this query returns multiple rows for a single host.
- {2,
- { OID_BYTEA, OID_INT2 },
- "get_host_dhcpid",
- "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
- " h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, "
- " h.hostname, h.dhcp4_client_classes, h.dhcp6_client_classes, "
- " h.dhcp4_next_server, h.dhcp4_server_hostname, h.dhcp4_boot_file_name, "
- " o4.option_id, o4.code, o4.value, o4.formatted_value, o4.space, "
- " o4.persistent, "
- " o6.option_id, o6.code, o6.value, o6.formatted_value, o6.space, "
- " o6.persistent, "
- " r.reservation_id, r.address, r.prefix_len, r.type, r.dhcp6_iaid "
- "FROM hosts AS h "
- "LEFT JOIN dhcp4_options AS o4 ON h.host_id = o4.host_id "
- "LEFT JOIN dhcp6_options AS o6 ON h.host_id = o6.host_id "
- "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
- "WHERE dhcp_identifier = $1 AND dhcp_identifier_type = $2 "
- "ORDER BY h.host_id, o4.option_id, o6.option_id, r.reservation_id"
- },
- // PgSqlHostDataSourceImpl::GET_HOST_ADDR
- // Retrieves host information along with the DHCPv4 options associated with
- // it. Left joining the dhcp4_options table results in multiple rows being
- // returned for the same host. The host is retrieved by IPv4 address.
- {1,
- { OID_INT8 }, "get_host_addr",
- "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
- " h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
- " h.dhcp4_client_classes, h.dhcp6_client_classes, "
- " h.dhcp4_next_server, h.dhcp4_server_hostname, h.dhcp4_boot_file_name, "
- " o.option_id, o.code, o.value, o.formatted_value, o.space, o.persistent "
- "FROM hosts AS h "
- "LEFT JOIN dhcp4_options AS o ON h.host_id = o.host_id "
- "WHERE ipv4_address = $1 "
- "ORDER BY h.host_id, o.option_id"
- },
- //PgSqlHostDataSourceImpl::GET_HOST_SUBID4_DHCPID
- // Retrieves host information and DHCPv4 options using subnet identifier
- // and client's identifier. Left joining the dhcp4_options table results in
- // multiple rows being returned for the same host.
- {3,
- { OID_INT4, OID_INT2, OID_BYTEA },
- "get_host_subid4_dhcpid",
- "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
- " h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
- " h.dhcp4_client_classes, h.dhcp6_client_classes, "
- " h.dhcp4_next_server, h.dhcp4_server_hostname, h.dhcp4_boot_file_name, "
- " o.option_id, o.code, o.value, o.formatted_value, o.space, o.persistent "
- "FROM hosts AS h "
- "LEFT JOIN dhcp4_options AS o ON h.host_id = o.host_id "
- "WHERE h.dhcp4_subnet_id = $1 AND h.dhcp_identifier_type = $2 "
- " AND h.dhcp_identifier = $3 "
- "ORDER BY h.host_id, o.option_id"
- },
- // PgSqlHostDataSourceImpl::GET_HOST_SUBID6_DHCPID
- // Retrieves host information, IPv6 reservations and DHCPv6 options
- // associated with a host. The number of rows returned is a multiplication
- // of number of IPv6 reservations and DHCPv6 options.
- {3,
- { OID_INT4, OID_INT2, OID_BYTEA },
- "get_host_subid6_dhcpid",
- "SELECT h.host_id, h.dhcp_identifier, "
- " h.dhcp_identifier_type, h.dhcp4_subnet_id, "
- " h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
- " h.dhcp4_client_classes, h.dhcp6_client_classes, "
- " h.dhcp4_next_server, h.dhcp4_server_hostname, h.dhcp4_boot_file_name, "
- " o.option_id, o.code, o.value, o.formatted_value, o.space, "
- " o.persistent, "
- " r.reservation_id, r.address, r.prefix_len, r.type, r.dhcp6_iaid "
- "FROM hosts AS h "
- "LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id "
- "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
- "WHERE h.dhcp6_subnet_id = $1 AND h.dhcp_identifier_type = $2 "
- " AND h.dhcp_identifier = $3 "
- "ORDER BY h.host_id, o.option_id, r.reservation_id"
- },
- //PgSqlHostDataSourceImpl::GET_HOST_SUBID_ADDR
- // Retrieves host information and DHCPv4 options for the host using subnet
- // identifier and IPv4 reservation. Left joining the dhcp4_options table
- // results in multiple rows being returned for the host. The number of
- // rows depends on the number of options defined for the host.
- {2,
- { OID_INT4, OID_INT8 },
- "get_host_subid_addr",
- "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
- " h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
- " h.dhcp4_client_classes, h.dhcp6_client_classes, "
- " h.dhcp4_next_server, h.dhcp4_server_hostname, h.dhcp4_boot_file_name, "
- " o.option_id, o.code, o.value, o.formatted_value, o.space, o.persistent "
- "FROM hosts AS h "
- "LEFT JOIN dhcp4_options AS o ON h.host_id = o.host_id "
- "WHERE h.dhcp4_subnet_id = $1 AND h.ipv4_address = $2 "
- "ORDER BY h.host_id, o.option_id"
- },
- // PgSqlHostDataSourceImpl::GET_HOST_PREFIX
- // Retrieves host information, IPv6 reservations and DHCPv6 options
- // associated with a host using prefix and prefix length. This query
- // returns host information for a single host. However, multiple rows
- // are returned due to left joining IPv6 reservations and DHCPv6 options.
- // The number of rows returned is multiplication of number of existing
- // IPv6 reservations and DHCPv6 options.
- {2,
- { OID_VARCHAR, OID_INT2 },
- "get_host_prefix",
- "SELECT h.host_id, h.dhcp_identifier, "
- " h.dhcp_identifier_type, h.dhcp4_subnet_id, "
- " h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
- " h.dhcp4_client_classes, h.dhcp6_client_classes, "
- " h.dhcp4_next_server, h.dhcp4_server_hostname, h.dhcp4_boot_file_name, "
- " o.option_id, o.code, o.value, o.formatted_value, o.space, "
- " o.persistent, "
- " r.reservation_id, r.address, r.prefix_len, r.type, "
- " r.dhcp6_iaid "
- "FROM hosts AS h "
- "LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id "
- "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
- "WHERE h.host_id = "
- " (SELECT host_id FROM ipv6_reservations "
- " WHERE address = $1 AND prefix_len = $2) "
- "ORDER BY h.host_id, o.option_id, r.reservation_id"
- },
- // PgSqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR
- // Retrieves host information, IPv6 reservations and DHCPv6 options
- // associated with a host using IPv6 subnet id and prefix. This query
- // returns host information for a single host. However, multiple rows
- // are returned due to left joining IPv6 reservations and DHCPv6 options.
- // The number of rows returned is multiplication of number of existing
- // IPv6 reservations and DHCPv6 options.
- {2,
- { OID_INT4, OID_VARCHAR },
- "get_host_subid6_addr",
- "SELECT h.host_id, h.dhcp_identifier, "
- " h.dhcp_identifier_type, h.dhcp4_subnet_id, "
- " h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
- " h.dhcp4_client_classes, h.dhcp6_client_classes, "
- " h.dhcp4_next_server, h.dhcp4_server_hostname, h.dhcp4_boot_file_name, "
- " o.option_id, o.code, o.value, o.formatted_value, o.space, "
- " o.persistent, "
- " r.reservation_id, r.address, r.prefix_len, r.type, "
- " r.dhcp6_iaid "
- "FROM hosts AS h "
- "LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id "
- "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
- "WHERE h.dhcp6_subnet_id = $1 AND r.address = $2 "
- "ORDER BY h.host_id, o.option_id, r.reservation_id"
- },
- // PgSqlHostDataSourceImpl::GET_VERSION
- // Retrieves MySQL schema version.
- {0,
- { OID_NONE },
- "get_version",
- "SELECT version, minor FROM schema_version"
- },
- // PgSqlHostDataSourceImpl::INSERT_HOST
- // Inserts a host into the 'hosts' table. Returns the inserted host id.
- {11,
- { OID_BYTEA, OID_INT2,
- OID_INT4, OID_INT4, OID_INT8, OID_VARCHAR,
- OID_VARCHAR, OID_VARCHAR },
- "insert_host",
- "INSERT INTO hosts(dhcp_identifier, dhcp_identifier_type, "
- " dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
- " dhcp4_client_classes, dhcp6_client_classes, "
- " dhcp4_next_server, dhcp4_server_hostname, dhcp4_boot_file_name) "
- "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING host_id"
- },
- //PgSqlHostDataSourceImpl::INSERT_V6_RESRV
- // Inserts a single IPv6 reservation into 'reservations' table.
- {5,
- { OID_VARCHAR, OID_INT2, OID_INT4, OID_INT4, OID_INT4 },
- "insert_v6_resrv",
- "INSERT INTO ipv6_reservations(address, prefix_len, type, "
- " dhcp6_iaid, host_id) "
- "VALUES ($1, $2, $3, $4, $5)"
- },
- // PgSqlHostDataSourceImpl::INSERT_V4_HOST_OPTION
- // Inserts a single DHCPv4 option into 'dhcp4_options' table.
- // Using fixed scope_id = 3, which associates an option with host.
- {6,
- { OID_INT2, OID_BYTEA, OID_TEXT,
- OID_VARCHAR, OID_BOOL, OID_INT8},
- "insert_v4_host_option",
- "INSERT INTO dhcp4_options(code, value, formatted_value, space, "
- " persistent, host_id, scope_id) "
- "VALUES ($1, $2, $3, $4, $5, $6, 3)"
- },
- // PgSqlHostDataSourceImpl::INSERT_V6_HOST_OPTION
- // Inserts a single DHCPv6 option into 'dhcp6_options' table.
- // Using fixed scope_id = 3, which associates an option with host.
- {6,
- { OID_INT2, OID_BYTEA, OID_TEXT,
- OID_VARCHAR, OID_BOOL, OID_INT8},
- "insert_v6_host_option",
- "INSERT INTO dhcp6_options(code, value, formatted_value, space, "
- " persistent, host_id, scope_id) "
- "VALUES ($1, $2, $3, $4, $5, $6, 3)"
- }
- }
- };
- }; // end anonymous namespace
- PgSqlHostDataSourceImpl::
- PgSqlHostDataSourceImpl(const PgSqlConnection::ParameterMap& parameters)
- : host_exchange_(new PgSqlHostWithOptionsExchange(PgSqlHostWithOptionsExchange::DHCP4_ONLY)),
- host_ipv6_exchange_(new PgSqlHostIPv6Exchange(PgSqlHostWithOptionsExchange::DHCP6_ONLY)),
- host_ipv46_exchange_(new PgSqlHostIPv6Exchange(PgSqlHostWithOptionsExchange::
- DHCP4_AND_DHCP6)),
- host_ipv6_reservation_exchange_(new PgSqlIPv6ReservationExchange()),
- host_option_exchange_(new PgSqlOptionExchange()),
- conn_(parameters),
- is_readonly_(false) {
- // Open the database.
- conn_.openDatabase();
- conn_.prepareStatements(tagged_statements.begin(),
- tagged_statements.begin() + WRITE_STMTS_BEGIN);
- // Check if the backend is explicitly configured to operate with
- // read only access to the database.
- is_readonly_ = conn_.configuredReadOnly();
- // If we are using read-write mode for the database we also prepare
- // statements for INSERTS etc.
- if (!is_readonly_) {
- conn_.prepareStatements(tagged_statements.begin() + WRITE_STMTS_BEGIN,
- tagged_statements.end());
- } else {
- LOG_INFO(dhcpsrv_logger, DHCPSRV_PGSQL_HOST_DB_READONLY);
- }
- }
- PgSqlHostDataSourceImpl::~PgSqlHostDataSourceImpl() {
- }
- uint64_t
- PgSqlHostDataSourceImpl::addStatement(StatementIndex stindex,
- PsqlBindArrayPtr& bind_array,
- const bool return_last_id) {
- uint64_t last_id = 0;
- PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
- tagged_statements[stindex].nbparams,
- &bind_array->values_[0],
- &bind_array->lengths_[0],
- &bind_array->formats_[0], 0));
- int s = PQresultStatus(r);
- if (s != PGRES_COMMAND_OK) {
- // Failure: check for the special case of duplicate entry.
- if (conn_.compareError(r, PgSqlConnection::DUPLICATE_KEY)) {
- isc_throw(DuplicateEntry, "Database duplicate entry error");
- }
- // Connection determines if the error is fatal or not, and
- // throws the appropriate exception
- conn_.checkStatementError(r, tagged_statements[stindex]);
- }
- if (return_last_id) {
- PgSqlExchange::getColumnValue(r, 0, 0, last_id);
- }
- return (last_id);
- }
- void
- PgSqlHostDataSourceImpl::addResv(const IPv6Resrv& resv,
- const HostID& id) {
- PsqlBindArrayPtr bind_array;
- bind_array = host_ipv6_reservation_exchange_->createBindForSend(resv, id);
- addStatement(INSERT_V6_RESRV, bind_array);
- }
- void
- PgSqlHostDataSourceImpl::addOption(const StatementIndex& stindex,
- const OptionDescriptor& opt_desc,
- const std::string& opt_space,
- const OptionalValue<SubnetID>&,
- const HostID& id) {
- PsqlBindArrayPtr bind_array;
- bind_array = host_option_exchange_->createBindForSend(opt_desc, opt_space,
- id);
- addStatement(stindex, bind_array);
- }
- void
- PgSqlHostDataSourceImpl::addOptions(const StatementIndex& stindex,
- const ConstCfgOptionPtr& options_cfg,
- const uint64_t host_id) {
- // Get option space names and vendor space names and combine them within a
- // single list.
- std::list<std::string> option_spaces = options_cfg->getOptionSpaceNames();
- std::list<std::string> vendor_spaces = options_cfg->getVendorIdsSpaceNames();
- option_spaces.insert(option_spaces.end(), vendor_spaces.begin(),
- vendor_spaces.end());
- // For each option space retrieve all options and insert them into the
- // database.
- for (std::list<std::string>::const_iterator space = option_spaces.begin();
- space != option_spaces.end(); ++space) {
- OptionContainerPtr options = options_cfg->getAll(*space);
- if (options && !options->empty()) {
- for (OptionContainer::const_iterator opt = options->begin();
- opt != options->end(); ++opt) {
- addOption(stindex, *opt, *space, OptionalValue<SubnetID>(),
- host_id);
- }
- }
- }
- }
- void
- PgSqlHostDataSourceImpl::
- getHostCollection(StatementIndex stindex, PsqlBindArrayPtr bind_array,
- boost::shared_ptr<PgSqlHostExchange> exchange,
- ConstHostCollection& result, bool single) const {
- exchange->clear();
- PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
- tagged_statements[stindex].nbparams,
- &bind_array->values_[0],
- &bind_array->lengths_[0],
- &bind_array->formats_[0], 0));
- conn_.checkStatementError(r, tagged_statements[stindex]);
- int rows = r.getRows();
- for(int row = 0; row < rows; ++row) {
- exchange->processRowData(result, r, row);
- if (single && result.size() > 1) {
- isc_throw(MultipleRecords, "multiple records were found in the "
- "database where only one was expected for query "
- << tagged_statements[stindex].name);
- }
- }
- }
- ConstHostPtr
- PgSqlHostDataSourceImpl::
- 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 {
- // Set up the WHERE clause value
- PsqlBindArrayPtr bind_array(new PsqlBindArray());
- // Add the subnet id.
- bind_array->add(subnet_id);
- // Add the Identifier type.
- bind_array->add(static_cast<uint8_t>(identifier_type));
- // Add the identifier value.
- bind_array->add(identifier_begin, identifier_len);
- ConstHostCollection collection;
- getHostCollection(stindex, bind_array, exchange, collection, true);
- // Return single record if present, else clear the host.
- ConstHostPtr result;
- if (!collection.empty())
- result = *collection.begin();
- return (result);
- }
- std::pair<uint32_t, uint32_t> PgSqlHostDataSourceImpl::getVersion() const {
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
- DHCPSRV_PGSQL_HOST_DB_GET_VERSION);
- PgSqlResult r(PQexecPrepared(conn_, "get_version", 0, NULL, NULL, NULL, 0));
- conn_.checkStatementError(r, tagged_statements[GET_VERSION]);
- uint32_t version;
- PgSqlExchange::getColumnValue(r, 0, 0, version);
- uint32_t minor;
- PgSqlExchange::getColumnValue(r, 0, 0, minor);
- return (std::make_pair(version, minor));
- }
- void
- PgSqlHostDataSourceImpl::checkReadOnly() const {
- if (is_readonly_) {
- isc_throw(ReadOnlyDb, "PostgreSQL host database backend is configured"
- " to operate in read only mode");
- }
- }
- /*********** PgSqlHostDataSource *********************/
- PgSqlHostDataSource::
- PgSqlHostDataSource(const PgSqlConnection::ParameterMap& parameters)
- : impl_(new PgSqlHostDataSourceImpl(parameters)) {
- }
- PgSqlHostDataSource::~PgSqlHostDataSource() {
- delete impl_;
- }
- void
- PgSqlHostDataSource::add(const HostPtr& host) {
- // If operating in read-only mode, throw exception.
- impl_->checkReadOnly();
- // Initiate PostgreSQL transaction as we will have to make multiple queries
- // to insert host information into multiple tables. If that fails on
- // any stage, the transaction will be rolled back by the destructor of
- // the PgSqlTransaction class.
- PgSqlTransaction transaction(impl_->conn_);
- // Create the MYSQL_BIND array for the host
- PsqlBindArrayPtr bind_array = impl_->host_exchange_->createBindForSend(host);
- // ... and insert the host.
- uint32_t host_id = impl_->addStatement(PgSqlHostDataSourceImpl::INSERT_HOST,
- bind_array, true);
- // Insert DHCPv4 options.
- ConstCfgOptionPtr cfg_option4 = host->getCfgOption4();
- if (cfg_option4) {
- impl_->addOptions(PgSqlHostDataSourceImpl::INSERT_V4_HOST_OPTION,
- cfg_option4, host_id);
- }
- // Insert DHCPv6 options.
- ConstCfgOptionPtr cfg_option6 = host->getCfgOption6();
- if (cfg_option6) {
- impl_->addOptions(PgSqlHostDataSourceImpl::INSERT_V6_HOST_OPTION,
- cfg_option6, host_id);
- }
- // Insert IPv6 reservations.
- IPv6ResrvRange v6resv = host->getIPv6Reservations();
- if (std::distance(v6resv.first, v6resv.second) > 0) {
- for (IPv6ResrvIterator resv = v6resv.first; resv != v6resv.second;
- ++resv) {
- impl_->addResv(resv->second, host_id);
- }
- }
- // Everything went fine, so explicitly commit the transaction.
- transaction.commit();
- }
- ConstHostCollection
- PgSqlHostDataSource::getAll(const HWAddrPtr& hwaddr,
- const DuidPtr& duid) const {
- if (duid){
- return (getAll(Host::IDENT_DUID, &duid->getDuid()[0],
- duid->getDuid().size()));
- } else if (hwaddr) {
- return (getAll(Host::IDENT_HWADDR,
- &hwaddr->hwaddr_[0],
- hwaddr->hwaddr_.size()));
- }
- return (ConstHostCollection());
- }
- ConstHostCollection
- PgSqlHostDataSource::getAll(const Host::IdentifierType& identifier_type,
- const uint8_t* identifier_begin,
- const size_t identifier_len) const {
- // Set up the WHERE clause value
- PsqlBindArrayPtr bind_array(new PsqlBindArray());
- // Identifier value.
- bind_array->add(identifier_begin, identifier_len);
- // Identifier type.
- bind_array->add(static_cast<uint8_t>(identifier_type));
- ConstHostCollection result;
- impl_->getHostCollection(PgSqlHostDataSourceImpl::GET_HOST_DHCPID,
- bind_array, impl_->host_ipv46_exchange_,
- result, false);
- return (result);
- }
- ConstHostCollection
- PgSqlHostDataSource::getAll4(const asiolink::IOAddress& address) const {
- // Set up the WHERE clause value
- PsqlBindArrayPtr bind_array(new PsqlBindArray());
- // v4 Reservation address
- bind_array->add(address);
- ConstHostCollection result;
- impl_->getHostCollection(PgSqlHostDataSourceImpl::GET_HOST_ADDR, bind_array,
- impl_->host_exchange_, result, false);
- return (result);
- }
- ConstHostPtr
- PgSqlHostDataSource::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
- const DuidPtr& duid) const {
- /// @todo: Rethink the logic in BaseHostDataSource::get4(subnet, hwaddr, duid)
- if (hwaddr && duid) {
- isc_throw(BadValue, "MySQL host data source get4() called with both"
- " hwaddr and duid, only one of them is allowed");
- }
- if (!hwaddr && !duid) {
- isc_throw(BadValue, "MySQL host data source get4() called with "
- "neither hwaddr or duid specified, one of them is required");
- }
- // Choosing one of the identifiers
- if (hwaddr) {
- return (get4(subnet_id, Host::IDENT_HWADDR, &hwaddr->hwaddr_[0],
- hwaddr->hwaddr_.size()));
- } else if (duid) {
- return (get4(subnet_id, Host::IDENT_DUID, &duid->getDuid()[0],
- duid->getDuid().size()));
- }
- return (ConstHostPtr());
- }
- ConstHostPtr
- PgSqlHostDataSource::get4(const SubnetID& subnet_id,
- const Host::IdentifierType& identifier_type,
- const uint8_t* identifier_begin,
- const size_t identifier_len) const {
- return (impl_->getHost(subnet_id, identifier_type, identifier_begin,
- identifier_len,
- PgSqlHostDataSourceImpl::GET_HOST_SUBID4_DHCPID,
- impl_->host_exchange_));
- }
- ConstHostPtr
- PgSqlHostDataSource::get4(const SubnetID& subnet_id,
- const asiolink::IOAddress& address) const {
- if (!address.isV4()) {
- isc_throw(BadValue, "PgSqlHostDataSource::get4(id, address) - "
- " wrong address type, address supplied is an IPv6 address");
- }
- // Set up the WHERE clause value
- PsqlBindArrayPtr bind_array(new PsqlBindArray());
- // Add the subnet id
- bind_array->add(subnet_id);
- // Add the address
- bind_array->add(address);
- ConstHostCollection collection;
- impl_->getHostCollection(PgSqlHostDataSourceImpl::GET_HOST_SUBID_ADDR,
- bind_array, impl_->host_exchange_, collection,
- true);
- // Return single record if present, else clear the host.
- ConstHostPtr result;
- if (!collection.empty())
- result = *collection.begin();
- return (result);
- }
- ConstHostPtr
- PgSqlHostDataSource::get6(const SubnetID& subnet_id, const DuidPtr& duid,
- const HWAddrPtr& hwaddr) const {
- /// @todo: Rethink the logic in BaseHostDataSource::get6(subnet, hwaddr, duid)
- if (hwaddr && duid) {
- isc_throw(BadValue, "MySQL host data source get6() called with both"
- " hwaddr and duid, only one of them is allowed");
- }
- if (!hwaddr && !duid) {
- isc_throw(BadValue, "MySQL host data source get6() called with "
- "neither hwaddr or duid specified, one of them is required");
- }
- // Choosing one of the identifiers
- if (hwaddr) {
- return (get6(subnet_id, Host::IDENT_HWADDR, &hwaddr->hwaddr_[0],
- hwaddr->hwaddr_.size()));
- } else if (duid) {
- return (get6(subnet_id, Host::IDENT_DUID, &duid->getDuid()[0],
- duid->getDuid().size()));
- }
- return (ConstHostPtr());
- }
- ConstHostPtr
- PgSqlHostDataSource::get6(const SubnetID& subnet_id,
- const Host::IdentifierType& identifier_type,
- const uint8_t* identifier_begin,
- const size_t identifier_len) const {
- return (impl_->getHost(subnet_id, identifier_type, identifier_begin,
- identifier_len, PgSqlHostDataSourceImpl::GET_HOST_SUBID6_DHCPID,
- impl_->host_ipv6_exchange_));
- }
- ConstHostPtr
- PgSqlHostDataSource::get6(const asiolink::IOAddress& prefix,
- const uint8_t prefix_len) const {
- /// @todo: Check that prefix is v6 address, not v4.
- // Set up the WHERE clause value
- PsqlBindArrayPtr bind_array(new PsqlBindArray());
- // Add the prefix
- bind_array->add(prefix);
- // Add the prefix length
- bind_array->add(prefix_len);
- ConstHostCollection collection;
- impl_->getHostCollection(PgSqlHostDataSourceImpl::GET_HOST_PREFIX,
- bind_array, impl_->host_ipv6_exchange_,
- collection, true);
- // Return single record if present, else clear the host.
- ConstHostPtr result;
- if (!collection.empty()) {
- result = *collection.begin();
- }
- return (result);
- }
- ConstHostPtr
- PgSqlHostDataSource::get6(const SubnetID& subnet_id,
- const asiolink::IOAddress& address) const {
- /// @todo: Check that prefix is v6 address, not v4.
- // Set up the WHERE clause value
- PsqlBindArrayPtr bind_array(new PsqlBindArray());
- // Add the subnet id
- bind_array->add(subnet_id);
- // Add the prefix
- bind_array->add(address);
- ConstHostCollection collection;
- impl_->getHostCollection(PgSqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR,
- bind_array, impl_->host_ipv6_exchange_,
- collection, true);
- // Return single record if present, else clear the host.
- ConstHostPtr result;
- if (!collection.empty()) {
- result = *collection.begin();
- }
- return (result);
- }
- // Miscellaneous database methods.
- std::string PgSqlHostDataSource::getName() const {
- std::string name = "";
- try {
- name = impl_->conn_.getParameter("name");
- } catch (...) {
- // Return an empty name
- }
- return (name);
- }
- std::string PgSqlHostDataSource::getDescription() const {
- return (std::string("Host data source that stores host information"
- "in PostgreSQL database"));
- }
- std::pair<uint32_t, uint32_t> PgSqlHostDataSource::getVersion() const {
- return(impl_->getVersion());
- }
- void
- PgSqlHostDataSource::commit() {
- // If operating in read-only mode, throw exception.
- impl_->checkReadOnly();
- impl_->conn_.commit();
- }
- void
- PgSqlHostDataSource::rollback() {
- // If operating in read-only mode, throw exception.
- impl_->checkReadOnly();
- impl_->conn_.rollback();
- }
- }; // end of isc::dhcp namespace
- }; // end of isc namespace
|