1 // Copyright (C) 2018-2021 Internet Systems Consortium, Inc. ("ISC")
2 // Copyright (C) 2016-2017 Deutsche Telekom AG.
3 //
4 // Author: Andrei Pavel <andrei.pavel@qualitance.com>
5 //
6 // Licensed under the Apache License, Version 2.0 (the "License");
7 // you may not use this file except in compliance with the License.
8 // You may obtain a copy of the License at
9 //
10 //           http://www.apache.org/licenses/LICENSE-2.0
11 //
12 // Unless required by applicable law or agreed to in writing, software
13 // distributed under the License is distributed on an "AS IS" BASIS,
14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 // See the License for the specific language governing permissions and
16 // limitations under the License.
17 
18 #include <config.h>
19 
20 #include <cql/cql_exchange.h>
21 #include <database/db_exceptions.h>
22 #include <dhcpsrv/cql_host_data_source.h>
23 #include <dhcp/duid.h>
24 #include <dhcp/libdhcp++.h>
25 #include <dhcp/option.h>
26 #include <dhcp/option_definition.h>
27 #include <dhcpsrv/cfg_option.h>
28 #include <dhcpsrv/cfgmgr.h>
29 #include <dhcpsrv/dhcpsrv_log.h>
30 #include <util/buffer.h>
31 #include <util/hash.h>
32 #include <util/optional.h>
33 #include <asiolink/io_address.h>
34 
35 #include <stdint.h>       // for uint64_t
36 
37 #include <boost/algorithm/string/classification.hpp>  // for boost::is_any_of
38 #include <boost/algorithm/string/split.hpp>           // for split
39 #include <boost/assert.hpp>                           // for BOOST_ASSERT
40 #include <boost/unordered_map.hpp>                    // for std::unordered_map
41 
42 #include <iosfwd>  // for size_t, std::stringstream
43 #include <memory>  // for std::unique_ptr
44 #include <string>  // for std::string
45 
46 using namespace isc::asiolink;
47 using namespace isc::db;
48 using namespace isc::dhcp;
49 using namespace isc::util;
50 using namespace isc::data;
51 
52 namespace {
53 
54 /// @brief Host identifier consisting of DUID or hardware address
55 typedef std::vector<uint8_t> HostIdentifier;
56 
57 /// @brief key for HostMap containing objects which uniquely identify a
58 ///     host: host identifier, host identifier type, subnets for IPv4 and IPv6
59 ///     and the IPv4 reservation
60 typedef std::
61     tuple<HostIdentifier, Host::IdentifierType, SubnetID, SubnetID, IOAddress> HostKey;
62 
63 /// @brief Identifies components of the host key
64 enum HostKeyComponent {
65     HOST_IDENTIFIER,
66     HOST_IDENTIFIER_TYPE,
67     IPv4_SUBNET_ID,
68     IPv6_SUBNET_ID,
69     IPv4_RESERVATION
70 };
71 
72 /// @brief Map used to merge reservations and options into a single host on
73 ///     retrieve from database
74 typedef std::unordered_map<HostKey, HostPtr, boost::hash<HostKey>> HostMap;
75 
76 /// @brief A pair of (host key and a pointer to a host)
77 typedef std::pair<HostKey, HostPtr> HostPair;
78 
79 /// @brief Wrapper used to specify option space alongside option descriptor
80 struct OptionWrapper {
OptionWrapper__anond1ec75dc0111::OptionWrapper81     OptionWrapper(OptionDescriptorPtr option_descriptor, std::string option_space)
82         : option_descriptor_(option_descriptor), option_space_(option_space) {
83     }
84     OptionDescriptorPtr option_descriptor_;
85     std::string option_space_;
86 };
87 
88 /// @brief Numeric value representing the last supported identifier. This value
89 ///     is used to validate whether the identifier type stored in a database is
90 ///     within bounds of supported identifiers.
91 static constexpr cass_int32_t MAX_IDENTIFIER_TYPE = static_cast<cass_int32_t>(Host::IDENT_FLEX);
92 
93 /// @{
94 /// @brief Invalid values in the Cassandra database
95 static constexpr char NULL_DHCP4_SERVER_HOSTNAME[] = "";
96 static constexpr char NULL_DHCP4_BOOT_FILE_NAME[] = "";
97 static constexpr char NULL_USER_CONTEXT[] = "";
98 static constexpr char NULL_RESERVED_IPV6_PREFIX_ADDRESS[] = "::";
99 static constexpr cass_int32_t NULL_RESERVED_IPV6_PREFIX_LENGTH = 0;
100 static constexpr cass_int32_t NULL_RESERVED_IPV6_PREFIX_ADDRESS_TYPE = -1;
101 static constexpr cass_int32_t NULL_IAID = -1;
102 static constexpr cass_int32_t NULL_OPTION_UNIVERSE = -1;
103 static constexpr cass_int32_t NULL_OPTION_CODE = -1;
104 static const CassBlob NULL_OPTION_VALUE = CassBlob();
105 static constexpr char NULL_OPTION_FORMATTED_VALUE[] = "";
106 static constexpr char NULL_OPTION_SPACE[] = "";
107 static constexpr cass_bool_t NULL_OPTION_IS_PERSISTENT = cass_false;
108 static constexpr char NULL_OPTION_CLIENT_CLASS[] = "";
109 static constexpr cass_int32_t NULL_OPTION_SUBNET_ID = -1;
110 static constexpr char NULL_OPTION_USER_CONTEXT[] = "";
111 static constexpr cass_int32_t NULL_OPTION_SCOPE_ID = -1;
112 /// @}
113 
114 /// @brief Invalid reservation used to check for an invalid IPv6Resrv formed
115 ///     from database values.
116 static const IPv6Resrv NULL_IPV6_RESERVATION =
117     IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("::"), 128);
118 
119 }  // namespace
120 
121 namespace isc {
122 namespace dhcp {
123 
124 /// @brief Provides mechanisms for sending and retrieving data from the
125 ///     hosts table.
126 class CqlHostExchange : public virtual CqlExchange {
127 public:
128     /// @brief Constructor
129     ///
130     /// Specifies table columns.
131     CqlHostExchange();
132 
133     /// @brief Virtual destructor.
134     virtual ~CqlHostExchange();
135 
136     /// @brief Binds member variables to data array to receive @ref Host data.
137     ///
138     /// Creates a bind array to receive @ref Host data from the Cassandra
139     /// database. After data is successfully received, @ref retrieve() can be
140     /// called to retrieve the @ref Host object. Called in @ref
141     /// db::CqlExchange::executeSelect().
142     ///
143     /// @param data array of objects representing data being retrieved
144     /// @param statement_tag prepared statement being executed; defaults to an
145     ///     invalid statement
146     virtual void
147     createBindForSelect(AnyArray& data, StatementTag statement_tag = NULL) override;
148 
149     /// @brief Sets the exchange members with data of @ref Host.
150     ///
151     /// Fills in the members of the exchange with data from @ref Host object.
152     ///
153     /// @param host @ref Host object being modified in the Cassandra database
154     /// @param subnet_id identifier of the subnet to which the host belongs
155     /// @param reservation IPv6 reservation belonging to the host
156     /// @param option_space option space
157     /// @param option_descriptor structure used to hold option information
158     void prepareExchange(const HostPtr& host,
159                          const Optional<SubnetID>& subnet_id,
160                          const IPv6Resrv* const reservation,
161                          const std::string& option_space,
162                          const OptionDescriptor& option_descriptor);
163 
164     /// @brief Binds @ref Host to data array to send data to the Cassandra
165     ///     database.
166     ///
167     /// Fills in the bind array for sending data stored in the @ref Host object
168     /// to the database.
169     ///
170     /// @param host @ref Host object being added to the Cassandra database
171     /// @param subnet_id identifier of the subnet to which the host belongs
172     /// @param reservation IPv6 reservation belonging to the host
173     /// @param option_space option space
174     /// @param option_descriptor structure used to hold option information
175     /// @param statement_tag tag of the statement being executed
176     /// @param data array being filled with data from to the Host object
177     void createBindForMutation(const HostPtr& host,
178                                const Optional<SubnetID>& subnet_id,
179                                const IPv6Resrv* const reservation,
180                                const std::string& option_space,
181                                const OptionDescriptor& option_descriptor,
182                                StatementTag statement_tag,
183                                AnyArray& data);
184 
185     /// @brief Binds @ref Host to data array to send data to the Cassandra
186     ///     database.
187     ///
188     /// Fills in the bind array for sending data stored in the @ref Host object
189     /// to the database.
190     ///
191     /// @param host @ref Host object being deleted from the Cassandra database
192     /// @param subnet_id identifier of the subnet to which the host belongs
193     /// @param reservation IPv6 reservation belonging to the host
194     /// @param option_space option space
195     /// @param option_descriptor structure used to hold option information
196     /// @param statement_tag tag of the statement being executed
197     /// @param data array being filled with data from to the Host object
198     void createBindForDelete(const HostPtr& host,
199                              const Optional<SubnetID>& subnet_id,
200                              const IPv6Resrv* const reservation,
201                              const std::string& option_space,
202                              const OptionDescriptor& option_descriptor,
203                              StatementTag statement_tag,
204                              AnyArray& data);
205 
206     /// @brief Create unique hash for storage in table id.
207     ///
208     /// Hash function used for creating a pseudo-unique hash from member
209     /// values which uniquely determine an entry in the table. Uses FNV-1a
210     /// on 64 bits.
211     ///
212     /// The primary key clustering column aggregates: host_identifier,
213     /// host_identifier_type, host_ipv4_subnet_id, host_ipv6_subnet_id,
214     /// host_ipv4_address, reserved_ipv6_prefix_address,
215     /// reserved_ipv6_prefix_length, option_code, option_space.
216     uint64_t hashIntoId() const;
217 
218     /// @brief Create unique key for storage in table key.
219     ///
220     /// The primary key partition key aggregates: host_identifier,
221     /// host_identifier_type, host_ipv4_subnet_id, host_ipv6_subnet_id,
222     /// host_ipv4_address
223     uint64_t hashIntoKey() const;
224 
225     /// @brief Create unique key string for a host.
226     ///
227     /// The primary key partition key aggregates: host_identifier,
228     /// host_identifier_type, host_ipv4_subnet_id, host_ipv6_subnet_id,
229     /// host_ipv4_address
230     std::string hostKey() const;
231 
232     /// @brief Copy received data into Host object
233     ///
234     /// Copies information about the host into a newly created @ref Host object
235     /// Called in @ref executeSelect after @ref createBindForSelect().
236     ///
237     /// @return Host Pointer to a @ref HostPtr object holding a pointer to the
238     /// @ref Host object returned.
239     virtual boost::any retrieve() override;
240 
241     /// @brief Creates IPv6 reservation from the data contained in the
242     ///     currently processed row.
243     ///
244     /// Called after createBindForSelect().
245     ///
246     /// @return IPv6Resrv object (containing IPv6 address or prefix reservation)
247     const IPv6Resrv retrieveReservation() const;
248 
249     /// @brief Retrieves option from members.
250     ///
251     /// Called after createBindForSelect().
252     ///
253     /// @return OptionDescriptorPtr object (containing the option from the
254     /// database)
255     const OptionWrapper retrieveOption() const;
256 
257     /// @brief Statement tags
258     /// @{
259     // Inserts all parameters belonging to any reservation from a single host.
260     static constexpr StatementTag INSERT_HOST = "INSERT_HOST";
261 
262     // Retrieves host information, IPv6 reservations and both IPv4 and IPv6
263     // options associated with it.
264     static constexpr StatementTag GET_HOST = "GET_HOST";
265 
266     // Retrieves host information, IPv6 reservations and both IPv4 and IPv6
267     // options associated with it.
268     static constexpr StatementTag GET_HOST_BY_HOST_ID = "GET_HOST_BY_HOST_ID";
269 
270     // Retrieves host information along with the IPv4 options associated
271     // with it.
272     static constexpr StatementTag GET_HOST_BY_IPV4_ADDRESS = "GET_HOST_BY_IPV4_ADDRESS";
273 
274     // Retrieves host information and IPv4 options using subnet identifier
275     // and client's identifier (i.e. hardware address or DUID).
276     static constexpr StatementTag GET_HOST_BY_IPV4_SUBNET_ID_AND_HOST_ID =
277         "GET_HOST_BY_IPV4_SUBNET_ID_AND_HOST_ID";
278 
279     // Retrieves host information; IPv6 reservations and IPv6 options
280     // associated with it using subnet identifier and client's
281     // identifier (i.e. hardware address or DUID).
282     static constexpr StatementTag GET_HOST_BY_IPV6_SUBNET_ID_AND_HOST_ID =
283         "GET_HOST_BY_IPV6_SUBNET_ID_AND_HOST_ID";
284 
285     // Retrieves host information and IPv4 options for the host using subnet
286     // identifier and IPv4 reservation.
287     static constexpr StatementTag GET_HOST_BY_IPV4_SUBNET_ID_AND_ADDRESS =
288         "GET_HOST_BY_IPV4_SUBNET_ID_AND_ADDRESS";
289 
290     // Retrieves host information, IPv6 reservations and IPv6 options
291     // associated with it using prefix and prefix length. The query returns
292     // host information for a single host. However, multiple rows are returned
293     // due to left joining IPv6 reservations and IPv6 options. The number of
294     // rows returned is multiplication of number of existing IPv6 reservations
295     // and IPv6 options.
296     static constexpr StatementTag GET_HOST_BY_IPV6_PREFIX = "GET_HOST_BY_IPV6_PREFIX";
297 
298     // Retrieves host information and IPv6 options for the host using subnet
299     // identifier and IPv6 reservation.
300     static constexpr StatementTag GET_HOST_BY_IPV6_SUBNET_ID_AND_ADDRESS =
301         "GET_HOST_BY_IPV6_SUBNET_ID_AND_ADDRESS";
302 
303     // Deletes a host reservation.
304     static constexpr StatementTag DELETE_HOST =
305         "DELETE_HOST";
306 
307     // Retrieves host information along with the IPv4 options associated
308     // with it using a subnet identifier.
309     static constexpr StatementTag GET_HOST_BY_IPV4_SUBNET_ID =
310         "GET_HOST_BY_IPV4_SUBNET_ID";
311 
312     // Retrieves host information; IPv6 reservations and IPv6 options
313     // associated with a host using subnet identifier.
314     static constexpr StatementTag GET_HOST_BY_IPV6_SUBNET_ID =
315         "GET_HOST_BY_IPV6_SUBNET_ID";
316 
317     // Retrieves host information, IPv6 reservations and both IPv4 and IPv6
318     // options associated with it using hostname.
319     static constexpr StatementTag GET_HOST_BY_HOST_NAME =
320         "GET_HOST_BY_HOST_NAME";
321 
322     // Retrieves host information along with the IPv4 options associated
323     // with it using hostname and subnet identifier.
324     static constexpr StatementTag GET_HOST_BY_HOST_NAME_AND_IPV4_SUBNET_ID =
325         "GET_HOST_BY_HOST_NAME_AND_IPV4_SUBNET_ID";
326 
327     // Retrieves host information; IPv6 reservations and IPv6 options
328     // associated with it using hostname and subnet identifier.
329     static constexpr StatementTag GET_HOST_BY_HOST_NAME_AND_IPV6_SUBNET_ID =
330         "GET_HOST_BY_HOST_NAME_AND_IPV6_SUBNET_ID";
331 
332     // Retrieves host information along with the IPv4 options associated
333     // with it using a subnet identifier from first host (paging).
334     static constexpr StatementTag GET_HOST_BY_IPV4_SUBNET_ID_LIMIT =
335         "GET_HOST_BY_IPV4_SUBNET_ID_LIMIT";
336 
337     // Retrieves host information along with the IPv4 options associated
338     // with it using a subnet identifier from next host (paging).
339     static constexpr StatementTag GET_HOST_BY_IPV4_SUBNET_ID_NEXT_KEY =
340         "GET_HOST_BY_IPV4_SUBNET_ID_NEXT_KEY";
341 
342     // Retrieves host information along with the IPv4 options associated
343     // with it using a subnet identifier from host with a limit (paging).
344     static constexpr StatementTag GET_HOST_BY_IPV4_SUBNET_ID_PAGE =
345         "GET_HOST_BY_IPV4_SUBNET_ID_PAGE";
346 
347     // Retrieves host information; IPv6 reservations and IPv6 options
348     // associated with it using subnet identifier from first host (paging).
349     static constexpr StatementTag GET_HOST_BY_IPV6_SUBNET_ID_LIMIT =
350         "GET_HOST_BY_IPV6_SUBNET_ID_LIMIT";
351 
352     // Retrieves host information; IPv6 reservations and IPv6 options
353     // associated with it using subnet identifier from next host (paging).
354     static constexpr StatementTag GET_HOST_BY_IPV6_SUBNET_ID_NEXT_KEY =
355         "GET_HOST_BY_IPV6_SUBNET_ID_NEXT_KEY";
356 
357     // Retrieves host information; IPv6 reservations and IPv6 options
358     // associated with it using subnet identifier from host with a limit
359     // (paging).
360     static constexpr StatementTag GET_HOST_BY_IPV6_SUBNET_ID_PAGE =
361         "GET_HOST_BY_IPV6_SUBNET_ID_PAGE";
362 
363     // Retrieves host information; reservations and options associated
364     // with it from first host (paging).
365     static constexpr StatementTag GET_HOST_LIMIT = "GET_HOST_LIMIT";
366 
367     // Retrieves host information; reservations and options associated
368     // with it from host (paging).
369     static constexpr StatementTag GET_HOST_KEY = "GET_HOST_KEY";
370 
371     // Retrieves host information; reservations and options associated
372     // with it from next host (paging).
373     static constexpr StatementTag GET_HOST_NEXT_KEY = "GET_HOST_NEXT_KEY";
374 
375     // Retrieves host information; reservations and options associated
376     // with it from host with a limit (paging).
377     static constexpr StatementTag GET_HOST_PAGE = "GET_HOST_PAGE";
378     /// @}
379 
380     /// @brief Cassandra statements
381     static StatementMap tagged_statements_;
382 
383 private:
384     /// Pointer to Host object holding information being inserted into database.
385     HostPtr host_;
386 
387     /// @brief Primary key. Partition key. Aggregates: host_identifier,
388     /// host_identifier_type, host_ipv4_subnet_id host_ipv6_subnet_id,
389     /// host_ipv4_address
390     cass_int64_t key_;
391 
392     /// @brief Primary key. Clustering key. Aggregates: host_identifier,
393     /// host_identifier_type, reserved_ipv6_prefix_address,
394     /// reserved_ipv6_prefix_length, option_code, option_space.
395     cass_int64_t id_;
396 
397     /// @brief Client's identifier (e.g. DUID, HW address) in binary format
398     CassBlob host_identifier_;
399 
400     /// @brief Type of the identifier in the host_identifier_
401     /// This value corresponds to the @ref Host::IdentifierType value.
402     cass_int32_t host_identifier_type_;
403 
404     /// @brief IPv4 subnet identifier
405     cass_int32_t host_ipv4_subnet_id_;
406 
407     /// @brief IPv6 subnet identifier
408     cass_int32_t host_ipv6_subnet_id_;
409 
410     /// @brief Reserved IPv4 address
411     cass_int32_t host_ipv4_address_;
412 
413     /// @brief Next server address (siaddr).
414     cass_int32_t host_ipv4_next_server_;
415 
416     /// @brief Server hostname (sname).
417     std::string host_ipv4_server_hostname_;
418 
419     /// @brief Boot file name (file).
420     std::string host_ipv4_boot_file_name_;
421 
422     /// @brief Key for authentication
423     std::string auth_key_;
424 
425     /// @brief Name reserved for the host
426     std::string hostname_;
427 
428     /// @brief Lower case name reserved for the host
429     std::string lower_case_hostname_;
430 
431     /// @brief User context
432     std::string user_context_;
433 
434     /// @brief A string holding comma separated list of IPv4 client classes
435     std::string host_ipv4_client_classes_;
436 
437     /// @brief A string holding comma separated list of IPv6 client classes
438     std::string host_ipv6_client_classes_;
439 
440     /// @brief Address belonging to the reserved IPv6 prefix
441     std::string reserved_ipv6_prefix_address_;
442 
443     /// @brief Length of the reserved IPv6 prefix
444     cass_int32_t reserved_ipv6_prefix_length_;
445 
446     /// @brief Reserver IPv6 prefix type
447     /// This value corresponds to the @ref Host::IdentifierType value.
448     cass_int32_t reserved_ipv6_prefix_address_type_;
449 
450     /// @brief The reservation's IAID
451     cass_int32_t iaid_;
452 
453     /// @brief Version of DHCP (i.e. 0 for DHCPv4 and 1 for DHCPv6) to which the
454     ///     option belongs.
455     cass_int32_t option_universe_;
456 
457     /// @brief Option code
458     cass_int32_t option_code_;
459 
460     /// @brief Option value
461     CassBlob option_value_;
462 
463     /// @brief The textual value of an option
464     std::string option_formatted_value_;
465 
466     /// @brief Option space name
467     std::string option_space_;
468 
469     /// @brief Flag indicating if option is always sent or only on request
470     cass_bool_t option_is_persistent_;
471 
472     /// @brief Option client class
473     std::string option_client_class_;
474 
475     /// @brief Subnet identifier
476     cass_int32_t option_subnet_id_;
477 
478     /// @brief Buffer holding textual user context of an option.
479     std::string option_user_context_;
480 
481     /// @brief Option scope id
482     cass_int32_t option_scope_id_;
483 };  // CqlHostExchange
484 
485 constexpr StatementTag CqlHostExchange::INSERT_HOST;
486 constexpr StatementTag CqlHostExchange::GET_HOST;
487 constexpr StatementTag CqlHostExchange::GET_HOST_BY_HOST_ID;
488 constexpr StatementTag CqlHostExchange::GET_HOST_BY_IPV4_ADDRESS;
489 constexpr StatementTag CqlHostExchange::GET_HOST_BY_IPV4_SUBNET_ID_AND_HOST_ID;
490 constexpr StatementTag CqlHostExchange::GET_HOST_BY_IPV6_SUBNET_ID_AND_HOST_ID;
491 constexpr StatementTag CqlHostExchange::GET_HOST_BY_IPV4_SUBNET_ID_AND_ADDRESS;
492 constexpr StatementTag CqlHostExchange::GET_HOST_BY_IPV6_PREFIX;
493 constexpr StatementTag CqlHostExchange::GET_HOST_BY_IPV6_SUBNET_ID_AND_ADDRESS;
494 constexpr StatementTag CqlHostExchange::GET_HOST_BY_IPV4_SUBNET_ID;
495 constexpr StatementTag CqlHostExchange::GET_HOST_BY_IPV6_SUBNET_ID;
496 constexpr StatementTag CqlHostExchange::GET_HOST_BY_HOST_NAME;
497 constexpr StatementTag CqlHostExchange::GET_HOST_BY_HOST_NAME_AND_IPV4_SUBNET_ID;
498 constexpr StatementTag CqlHostExchange::GET_HOST_BY_HOST_NAME_AND_IPV6_SUBNET_ID;
499 constexpr StatementTag CqlHostExchange::GET_HOST_BY_IPV4_SUBNET_ID_LIMIT;
500 constexpr StatementTag CqlHostExchange::GET_HOST_BY_IPV6_SUBNET_ID_LIMIT;
501 constexpr StatementTag CqlHostExchange::GET_HOST_BY_IPV4_SUBNET_ID_NEXT_KEY;
502 constexpr StatementTag CqlHostExchange::GET_HOST_BY_IPV6_SUBNET_ID_NEXT_KEY;
503 constexpr StatementTag CqlHostExchange::GET_HOST_BY_IPV4_SUBNET_ID_PAGE;
504 constexpr StatementTag CqlHostExchange::GET_HOST_BY_IPV6_SUBNET_ID_PAGE;
505 constexpr StatementTag CqlHostExchange::GET_HOST_LIMIT;
506 constexpr StatementTag CqlHostExchange::GET_HOST_NEXT_KEY;
507 constexpr StatementTag CqlHostExchange::GET_HOST_KEY;
508 constexpr StatementTag CqlHostExchange::GET_HOST_PAGE;
509 constexpr StatementTag CqlHostExchange::DELETE_HOST;
510 
511 StatementMap CqlHostExchange::tagged_statements_ = {
512     {INSERT_HOST,
513      {INSERT_HOST,
514       "INSERT INTO hosts ( "
515       "key, "
516       "id, "
517       "host_identifier, "
518       "host_identifier_type, "
519       "host_ipv4_subnet_id, "
520       "host_ipv6_subnet_id, "
521       "host_ipv4_address, "
522       "host_ipv4_next_server, "
523       "host_ipv4_server_hostname, "
524       "host_ipv4_boot_file_name, "
525       "auth_key, "
526       "hostname, "
527       "lower_case_hostname, "
528       "user_context, "
529       "host_ipv4_client_classes, "
530       "host_ipv6_client_classes, "
531       "reserved_ipv6_prefix_address, "
532       "reserved_ipv6_prefix_length, "
533       "reserved_ipv6_prefix_address_type, "
534       "iaid, "
535       "option_universe, "
536       "option_code, "
537       "option_value, "
538       "option_formatted_value, "
539       "option_space, "
540       "option_is_persistent, "
541       "option_client_class, "
542       "option_subnet_id, "
543       "option_user_context, "
544       "option_scope_id "
545       ") VALUES ( "
546       // key
547       "?, "
548       // id
549       "?, "
550       // host
551       "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, "
552       // denormalized reservation, option
553       "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? "
554       ") "
555       "IF NOT EXISTS "
556      }},
557 
558     {GET_HOST,
559      {GET_HOST,
560       "SELECT "
561       "key, "
562       "id, "
563       "host_identifier, "
564       "host_identifier_type, "
565       "host_ipv4_subnet_id, "
566       "host_ipv6_subnet_id, "
567       "host_ipv4_address, "
568       "host_ipv4_next_server, "
569       "host_ipv4_server_hostname, "
570       "host_ipv4_boot_file_name, "
571       "auth_key, "
572       "hostname, "
573       "user_context, "
574       "host_ipv4_client_classes, "
575       "host_ipv6_client_classes, "
576       "reserved_ipv6_prefix_address, "
577       "reserved_ipv6_prefix_length, "
578       "reserved_ipv6_prefix_address_type, "
579       "iaid, "
580       "option_universe, "
581       "option_code, "
582       "option_value, "
583       "option_formatted_value, "
584       "option_space, "
585       "option_is_persistent, "
586       "option_client_class, "
587       "option_subnet_id, "
588       "option_user_context, "
589       "option_scope_id "
590       "FROM hosts "
591      }},
592 
593     {GET_HOST_BY_HOST_ID,
594      {GET_HOST_BY_HOST_ID,
595       "SELECT "
596       "key, "
597       "id, "
598       "host_identifier, "
599       "host_identifier_type, "
600       "host_ipv4_subnet_id, "
601       "host_ipv6_subnet_id, "
602       "host_ipv4_address, "
603       "host_ipv4_next_server, "
604       "host_ipv4_server_hostname, "
605       "host_ipv4_boot_file_name, "
606       "auth_key, "
607       "hostname, "
608       "user_context, "
609       "host_ipv4_client_classes, "
610       "host_ipv6_client_classes, "
611       "reserved_ipv6_prefix_address, "
612       "reserved_ipv6_prefix_length, "
613       "reserved_ipv6_prefix_address_type, "
614       "iaid, "
615       "option_universe, "
616       "option_code, "
617       "option_value, "
618       "option_formatted_value, "
619       "option_space, "
620       "option_is_persistent, "
621       "option_client_class, "
622       "option_subnet_id, "
623       "option_user_context, "
624       "option_scope_id "
625       "FROM hosts "
626       "WHERE host_identifier = ? "
627       "AND host_identifier_type = ? "
628       "ALLOW FILTERING "
629      }},
630 
631     {GET_HOST_BY_IPV4_ADDRESS,
632      {GET_HOST_BY_IPV4_ADDRESS,
633       "SELECT "
634       "key, "
635       "id, "
636       "host_identifier, "
637       "host_identifier_type, "
638       "host_ipv4_subnet_id, "
639       "host_ipv6_subnet_id, "
640       "host_ipv4_address, "
641       "host_ipv4_next_server, "
642       "host_ipv4_server_hostname, "
643       "host_ipv4_boot_file_name, "
644       "auth_key, "
645       "hostname, "
646       "user_context, "
647       "host_ipv4_client_classes, "
648       "host_ipv6_client_classes, "
649       "reserved_ipv6_prefix_address, "
650       "reserved_ipv6_prefix_length, "
651       "reserved_ipv6_prefix_address_type, "
652       "iaid, "
653       "option_universe, "
654       "option_code, "
655       "option_value, "
656       "option_formatted_value, "
657       "option_space, "
658       "option_is_persistent, "
659       "option_client_class, "
660       "option_subnet_id, "
661       "option_user_context, "
662       "option_scope_id "
663       "FROM hosts "
664       "WHERE host_ipv4_address = ? "
665       "ALLOW FILTERING "
666      }},
667 
668     {GET_HOST_BY_IPV4_SUBNET_ID_AND_HOST_ID,
669      {GET_HOST_BY_IPV4_SUBNET_ID_AND_HOST_ID,
670       "SELECT "
671       "key, "
672       "id, "
673       "host_identifier, "
674       "host_identifier_type, "
675       "host_ipv4_subnet_id, "
676       "host_ipv6_subnet_id, "
677       "host_ipv4_address, "
678       "host_ipv4_next_server, "
679       "host_ipv4_server_hostname, "
680       "host_ipv4_boot_file_name, "
681       "auth_key, "
682       "hostname, "
683       "user_context, "
684       "host_ipv4_client_classes, "
685       "host_ipv6_client_classes, "
686       "reserved_ipv6_prefix_address, "
687       "reserved_ipv6_prefix_length, "
688       "reserved_ipv6_prefix_address_type, "
689       "iaid, "
690       "option_universe, "
691       "option_code, "
692       "option_value, "
693       "option_formatted_value, "
694       "option_space, "
695       "option_is_persistent, "
696       "option_client_class, "
697       "option_subnet_id, "
698       "option_user_context, "
699       "option_scope_id "
700       "FROM hosts "
701       "WHERE host_ipv4_subnet_id = ? "
702       "AND host_identifier = ? "
703       "AND host_identifier_type = ? "
704       "ALLOW FILTERING "
705      }},
706 
707     {GET_HOST_BY_IPV6_SUBNET_ID_AND_HOST_ID,
708      {GET_HOST_BY_IPV6_SUBNET_ID_AND_HOST_ID,
709       "SELECT "
710       "key, "
711       "id, "
712       "host_identifier, "
713       "host_identifier_type, "
714       "host_ipv4_subnet_id, "
715       "host_ipv6_subnet_id, "
716       "host_ipv4_address, "
717       "host_ipv4_next_server, "
718       "host_ipv4_server_hostname, "
719       "host_ipv4_boot_file_name, "
720       "auth_key, "
721       "hostname, "
722       "user_context, "
723       "host_ipv4_client_classes, "
724       "host_ipv6_client_classes, "
725       "reserved_ipv6_prefix_address, "
726       "reserved_ipv6_prefix_length, "
727       "reserved_ipv6_prefix_address_type, "
728       "iaid, "
729       "option_universe, "
730       "option_code, "
731       "option_value, "
732       "option_formatted_value, "
733       "option_space, "
734       "option_is_persistent, "
735       "option_client_class, "
736       "option_subnet_id, "
737       "option_user_context, "
738       "option_scope_id "
739       "FROM hosts "
740       "WHERE host_ipv6_subnet_id = ? "
741       "AND host_identifier = ? "
742       "AND host_identifier_type = ? "
743       "ALLOW FILTERING "
744      }},
745 
746     {GET_HOST_BY_IPV4_SUBNET_ID_AND_ADDRESS,
747      {GET_HOST_BY_IPV4_SUBNET_ID_AND_ADDRESS,
748       "SELECT "
749       "key, "
750       "id, "
751       "host_identifier, "
752       "host_identifier_type, "
753       "host_ipv4_subnet_id, "
754       "host_ipv6_subnet_id, "
755       "host_ipv4_address, "
756       "host_ipv4_next_server, "
757       "host_ipv4_server_hostname, "
758       "host_ipv4_boot_file_name, "
759       "auth_key, "
760       "hostname, "
761       "user_context, "
762       "host_ipv4_client_classes, "
763       "host_ipv6_client_classes, "
764       "reserved_ipv6_prefix_address, "
765       "reserved_ipv6_prefix_length, "
766       "reserved_ipv6_prefix_address_type, "
767       "iaid, "
768       "option_universe, "
769       "option_code, "
770       "option_value, "
771       "option_formatted_value, "
772       "option_space, "
773       "option_is_persistent, "
774       "option_client_class, "
775       "option_subnet_id, "
776       "option_user_context, "
777       "option_scope_id "
778       "FROM hosts "
779       "WHERE host_ipv4_subnet_id = ? "
780       "AND host_ipv4_address = ? "
781       "ALLOW FILTERING "
782      }},
783 
784     {GET_HOST_BY_IPV6_PREFIX,
785      {GET_HOST_BY_IPV6_PREFIX,
786       "SELECT "
787       "key, "
788       "id, "
789       "host_identifier, "
790       "host_identifier_type, "
791       "host_ipv4_subnet_id, "
792       "host_ipv6_subnet_id, "
793       "host_ipv4_address, "
794       "host_ipv4_next_server, "
795       "host_ipv4_server_hostname, "
796       "host_ipv4_boot_file_name, "
797       "auth_key, "
798       "hostname, "
799       "user_context, "
800       "host_ipv4_client_classes, "
801       "host_ipv6_client_classes, "
802       "reserved_ipv6_prefix_address, "
803       "reserved_ipv6_prefix_length, "
804       "reserved_ipv6_prefix_address_type, "
805       "iaid, "
806       "option_universe, "
807       "option_code, "
808       "option_value, "
809       "option_formatted_value, "
810       "option_space, "
811       "option_is_persistent, "
812       "option_client_class, "
813       "option_subnet_id, "
814       "option_user_context, "
815       "option_scope_id "
816       "FROM hosts "
817       "WHERE reserved_ipv6_prefix_address = ? "
818       "AND reserved_ipv6_prefix_length = ? "
819       "ALLOW FILTERING "
820      }},
821 
822     {GET_HOST_BY_IPV6_SUBNET_ID_AND_ADDRESS,
823      {GET_HOST_BY_IPV6_SUBNET_ID_AND_ADDRESS,
824       "SELECT "
825       "key, "
826       "id, "
827       "host_identifier, "
828       "host_identifier_type, "
829       "host_ipv4_subnet_id, "
830       "host_ipv6_subnet_id, "
831       "host_ipv4_address, "
832       "host_ipv4_next_server, "
833       "host_ipv4_server_hostname, "
834       "host_ipv4_boot_file_name, "
835       "auth_key, "
836       "hostname, "
837       "user_context, "
838       "host_ipv4_client_classes, "
839       "host_ipv6_client_classes, "
840       "reserved_ipv6_prefix_address, "
841       "reserved_ipv6_prefix_length, "
842       "reserved_ipv6_prefix_address_type, "
843       "iaid, "
844       "option_universe, "
845       "option_code, "
846       "option_value, "
847       "option_formatted_value, "
848       "option_space, "
849       "option_is_persistent, "
850       "option_client_class, "
851       "option_subnet_id, "
852       "option_user_context, "
853       "option_scope_id "
854       "FROM hosts "
855       "WHERE host_ipv6_subnet_id = ? "
856       "AND reserved_ipv6_prefix_address = ? "
857       "ALLOW FILTERING "
858      }},
859 
860     {GET_HOST_BY_IPV4_SUBNET_ID,
861      {GET_HOST_BY_IPV4_SUBNET_ID,
862       "SELECT "
863       "key, "
864       "id, "
865       "host_identifier, "
866       "host_identifier_type, "
867       "host_ipv4_subnet_id, "
868       "host_ipv6_subnet_id, "
869       "host_ipv4_address, "
870       "host_ipv4_next_server, "
871       "host_ipv4_server_hostname, "
872       "host_ipv4_boot_file_name, "
873       "auth_key, "
874       "hostname, "
875       "user_context, "
876       "host_ipv4_client_classes, "
877       "host_ipv6_client_classes, "
878       "reserved_ipv6_prefix_address, "
879       "reserved_ipv6_prefix_length, "
880       "reserved_ipv6_prefix_address_type, "
881       "iaid, "
882       "option_universe, "
883       "option_code, "
884       "option_value, "
885       "option_formatted_value, "
886       "option_space, "
887       "option_is_persistent, "
888       "option_client_class, "
889       "option_subnet_id, "
890       "option_user_context, "
891       "option_scope_id "
892       "FROM hosts "
893       "WHERE host_ipv4_subnet_id = ? "
894       "ALLOW FILTERING "
895      }},
896 
897     {GET_HOST_BY_IPV6_SUBNET_ID,
898      {GET_HOST_BY_IPV6_SUBNET_ID,
899       "SELECT "
900       "key, "
901       "id, "
902       "host_identifier, "
903       "host_identifier_type, "
904       "host_ipv4_subnet_id, "
905       "host_ipv6_subnet_id, "
906       "host_ipv4_address, "
907       "host_ipv4_next_server, "
908       "host_ipv4_server_hostname, "
909       "host_ipv4_boot_file_name, "
910       "auth_key, "
911       "hostname, "
912       "user_context, "
913       "host_ipv4_client_classes, "
914       "host_ipv6_client_classes, "
915       "reserved_ipv6_prefix_address, "
916       "reserved_ipv6_prefix_length, "
917       "reserved_ipv6_prefix_address_type, "
918       "iaid, "
919       "option_universe, "
920       "option_code, "
921       "option_value, "
922       "option_formatted_value, "
923       "option_space, "
924       "option_is_persistent, "
925       "option_client_class, "
926       "option_subnet_id, "
927       "option_user_context, "
928       "option_scope_id "
929       "FROM hosts "
930       "WHERE host_ipv6_subnet_id = ? "
931       "ALLOW FILTERING "
932      }},
933 
934     {GET_HOST_BY_HOST_NAME,
935      {GET_HOST_BY_HOST_NAME,
936       "SELECT "
937       "key, "
938       "id, "
939       "host_identifier, "
940       "host_identifier_type, "
941       "host_ipv4_subnet_id, "
942       "host_ipv6_subnet_id, "
943       "host_ipv4_address, "
944       "host_ipv4_next_server, "
945       "host_ipv4_server_hostname, "
946       "host_ipv4_boot_file_name, "
947       "auth_key, "
948       "hostname, "
949       "user_context, "
950       "host_ipv4_client_classes, "
951       "host_ipv6_client_classes, "
952       "reserved_ipv6_prefix_address, "
953       "reserved_ipv6_prefix_length, "
954       "reserved_ipv6_prefix_address_type, "
955       "iaid, "
956       "option_universe, "
957       "option_code, "
958       "option_value, "
959       "option_formatted_value, "
960       "option_space, "
961       "option_is_persistent, "
962       "option_client_class, "
963       "option_subnet_id, "
964       "option_user_context, "
965       "option_scope_id "
966       "FROM hosts "
967       "WHERE lower_case_hostname = ? "
968       "ALLOW FILTERING "
969      }},
970 
971     {GET_HOST_BY_HOST_NAME_AND_IPV4_SUBNET_ID,
972      {GET_HOST_BY_HOST_NAME_AND_IPV4_SUBNET_ID,
973       "SELECT "
974       "key, "
975       "id, "
976       "host_identifier, "
977       "host_identifier_type, "
978       "host_ipv4_subnet_id, "
979       "host_ipv6_subnet_id, "
980       "host_ipv4_address, "
981       "host_ipv4_next_server, "
982       "host_ipv4_server_hostname, "
983       "host_ipv4_boot_file_name, "
984       "auth_key, "
985       "hostname, "
986       "user_context, "
987       "host_ipv4_client_classes, "
988       "host_ipv6_client_classes, "
989       "reserved_ipv6_prefix_address, "
990       "reserved_ipv6_prefix_length, "
991       "reserved_ipv6_prefix_address_type, "
992       "iaid, "
993       "option_universe, "
994       "option_code, "
995       "option_value, "
996       "option_formatted_value, "
997       "option_space, "
998       "option_is_persistent, "
999       "option_client_class, "
1000       "option_subnet_id, "
1001       "option_user_context, "
1002       "option_scope_id "
1003       "FROM hosts "
1004       "WHERE lower_case_hostname = ? "
1005       "AND host_ipv4_subnet_id = ? "
1006       "ALLOW FILTERING "
1007      }},
1008 
1009     {GET_HOST_BY_HOST_NAME_AND_IPV6_SUBNET_ID,
1010      {GET_HOST_BY_HOST_NAME_AND_IPV6_SUBNET_ID,
1011       "SELECT "
1012       "key, "
1013       "id, "
1014       "host_identifier, "
1015       "host_identifier_type, "
1016       "host_ipv4_subnet_id, "
1017       "host_ipv6_subnet_id, "
1018       "host_ipv4_address, "
1019       "host_ipv4_next_server, "
1020       "host_ipv4_server_hostname, "
1021       "host_ipv4_boot_file_name, "
1022       "auth_key, "
1023       "hostname, "
1024       "user_context, "
1025       "host_ipv4_client_classes, "
1026       "host_ipv6_client_classes, "
1027       "reserved_ipv6_prefix_address, "
1028       "reserved_ipv6_prefix_length, "
1029       "reserved_ipv6_prefix_address_type, "
1030       "iaid, "
1031       "option_universe, "
1032       "option_code, "
1033       "option_value, "
1034       "option_formatted_value, "
1035       "option_space, "
1036       "option_is_persistent, "
1037       "option_client_class, "
1038       "option_subnet_id, "
1039       "option_user_context, "
1040       "option_scope_id "
1041       "FROM hosts "
1042       "WHERE lower_case_hostname = ? "
1043       "AND host_ipv6_subnet_id = ? "
1044       "ALLOW FILTERING "
1045      }},
1046 
1047     {GET_HOST_BY_IPV4_SUBNET_ID_LIMIT,
1048      {GET_HOST_BY_IPV4_SUBNET_ID_LIMIT,
1049       "SELECT "
1050       "key, "
1051       "id, "
1052       "host_identifier, "
1053       "host_identifier_type, "
1054       "host_ipv4_subnet_id, "
1055       "host_ipv6_subnet_id, "
1056       "host_ipv4_address, "
1057       "host_ipv4_next_server, "
1058       "host_ipv4_server_hostname, "
1059       "host_ipv4_boot_file_name, "
1060       "auth_key, "
1061       "hostname, "
1062       "user_context, "
1063       "host_ipv4_client_classes, "
1064       "host_ipv6_client_classes, "
1065       "reserved_ipv6_prefix_address, "
1066       "reserved_ipv6_prefix_length, "
1067       "reserved_ipv6_prefix_address_type, "
1068       "iaid, "
1069       "option_universe, "
1070       "option_code, "
1071       "option_value, "
1072       "option_formatted_value, "
1073       "option_space, "
1074       "option_is_persistent, "
1075       "option_client_class, "
1076       "option_subnet_id, "
1077       "option_user_context, "
1078       "option_scope_id "
1079       "FROM hosts "
1080       "WHERE host_ipv4_subnet_id = ? "
1081       "LIMIT 1 "
1082       "ALLOW FILTERING "
1083      }},
1084 
1085     {GET_HOST_BY_IPV4_SUBNET_ID_NEXT_KEY,
1086      {GET_HOST_BY_IPV4_SUBNET_ID_NEXT_KEY,
1087       "SELECT "
1088       "key, "
1089       "id, "
1090       "host_identifier, "
1091       "host_identifier_type, "
1092       "host_ipv4_subnet_id, "
1093       "host_ipv6_subnet_id, "
1094       "host_ipv4_address, "
1095       "host_ipv4_next_server, "
1096       "host_ipv4_server_hostname, "
1097       "host_ipv4_boot_file_name, "
1098       "auth_key, "
1099       "hostname, "
1100       "user_context, "
1101       "host_ipv4_client_classes, "
1102       "host_ipv6_client_classes, "
1103       "reserved_ipv6_prefix_address, "
1104       "reserved_ipv6_prefix_length, "
1105       "reserved_ipv6_prefix_address_type, "
1106       "iaid, "
1107       "option_universe, "
1108       "option_code, "
1109       "option_value, "
1110       "option_formatted_value, "
1111       "option_space, "
1112       "option_is_persistent, "
1113       "option_client_class, "
1114       "option_subnet_id, "
1115       "option_user_context, "
1116       "option_scope_id "
1117       "FROM hosts "
1118       "WHERE host_ipv4_subnet_id = ? "
1119       "AND TOKEN(key) > TOKEN(?) "
1120       "LIMIT 1 "
1121       "ALLOW FILTERING "
1122      }},
1123 
1124     {GET_HOST_BY_IPV4_SUBNET_ID_PAGE,
1125      {GET_HOST_BY_IPV4_SUBNET_ID_PAGE,
1126       "SELECT "
1127       "key, "
1128       "id, "
1129       "host_identifier, "
1130       "host_identifier_type, "
1131       "host_ipv4_subnet_id, "
1132       "host_ipv6_subnet_id, "
1133       "host_ipv4_address, "
1134       "host_ipv4_next_server, "
1135       "host_ipv4_server_hostname, "
1136       "host_ipv4_boot_file_name, "
1137       "auth_key, "
1138       "hostname, "
1139       "user_context, "
1140       "host_ipv4_client_classes, "
1141       "host_ipv6_client_classes, "
1142       "reserved_ipv6_prefix_address, "
1143       "reserved_ipv6_prefix_length, "
1144       "reserved_ipv6_prefix_address_type, "
1145       "iaid, "
1146       "option_universe, "
1147       "option_code, "
1148       "option_value, "
1149       "option_formatted_value, "
1150       "option_space, "
1151       "option_is_persistent, "
1152       "option_client_class, "
1153       "option_subnet_id, "
1154       "option_user_context, "
1155       "option_scope_id "
1156       "FROM hosts "
1157       "WHERE host_ipv4_subnet_id = ? "
1158       "AND id = ? "
1159       "LIMIT 1 "
1160       "ALLOW FILTERING "
1161      }},
1162 
1163     {GET_HOST_BY_IPV6_SUBNET_ID_LIMIT,
1164      {GET_HOST_BY_IPV6_SUBNET_ID_LIMIT,
1165       "SELECT "
1166       "key, "
1167       "id, "
1168       "host_identifier, "
1169       "host_identifier_type, "
1170       "host_ipv4_subnet_id, "
1171       "host_ipv6_subnet_id, "
1172       "host_ipv4_address, "
1173       "host_ipv4_next_server, "
1174       "host_ipv4_server_hostname, "
1175       "host_ipv4_boot_file_name, "
1176       "auth_key, "
1177       "hostname, "
1178       "user_context, "
1179       "host_ipv4_client_classes, "
1180       "host_ipv6_client_classes, "
1181       "reserved_ipv6_prefix_address, "
1182       "reserved_ipv6_prefix_length, "
1183       "reserved_ipv6_prefix_address_type, "
1184       "iaid, "
1185       "option_universe, "
1186       "option_code, "
1187       "option_value, "
1188       "option_formatted_value, "
1189       "option_space, "
1190       "option_is_persistent, "
1191       "option_client_class, "
1192       "option_subnet_id, "
1193       "option_user_context, "
1194       "option_scope_id "
1195       "FROM hosts "
1196       "WHERE host_ipv6_subnet_id = ? "
1197       "LIMIT 1 "
1198       "ALLOW FILTERING "
1199      }},
1200 
1201     {GET_HOST_BY_IPV6_SUBNET_ID_NEXT_KEY,
1202      {GET_HOST_BY_IPV6_SUBNET_ID_NEXT_KEY,
1203       "SELECT "
1204       "key, "
1205       "id, "
1206       "host_identifier, "
1207       "host_identifier_type, "
1208       "host_ipv4_subnet_id, "
1209       "host_ipv6_subnet_id, "
1210       "host_ipv4_address, "
1211       "host_ipv4_next_server, "
1212       "host_ipv4_server_hostname, "
1213       "host_ipv4_boot_file_name, "
1214       "auth_key, "
1215       "hostname, "
1216       "user_context, "
1217       "host_ipv4_client_classes, "
1218       "host_ipv6_client_classes, "
1219       "reserved_ipv6_prefix_address, "
1220       "reserved_ipv6_prefix_length, "
1221       "reserved_ipv6_prefix_address_type, "
1222       "iaid, "
1223       "option_universe, "
1224       "option_code, "
1225       "option_value, "
1226       "option_formatted_value, "
1227       "option_space, "
1228       "option_is_persistent, "
1229       "option_client_class, "
1230       "option_subnet_id, "
1231       "option_user_context, "
1232       "option_scope_id "
1233       "FROM hosts "
1234       "WHERE host_ipv6_subnet_id = ? "
1235       "AND TOKEN(key) > TOKEN(?) "
1236       "LIMIT 1 "
1237       "ALLOW FILTERING "
1238      }},
1239 
1240     {GET_HOST_BY_IPV6_SUBNET_ID_PAGE,
1241      {GET_HOST_BY_IPV6_SUBNET_ID_PAGE,
1242       "SELECT "
1243       "key, "
1244       "id, "
1245       "host_identifier, "
1246       "host_identifier_type, "
1247       "host_ipv4_subnet_id, "
1248       "host_ipv6_subnet_id, "
1249       "host_ipv4_address, "
1250       "host_ipv4_next_server, "
1251       "host_ipv4_server_hostname, "
1252       "host_ipv4_boot_file_name, "
1253       "auth_key, "
1254       "hostname, "
1255       "user_context, "
1256       "host_ipv4_client_classes, "
1257       "host_ipv6_client_classes, "
1258       "reserved_ipv6_prefix_address, "
1259       "reserved_ipv6_prefix_length, "
1260       "reserved_ipv6_prefix_address_type, "
1261       "iaid, "
1262       "option_universe, "
1263       "option_code, "
1264       "option_value, "
1265       "option_formatted_value, "
1266       "option_space, "
1267       "option_is_persistent, "
1268       "option_client_class, "
1269       "option_subnet_id, "
1270       "option_user_context, "
1271       "option_scope_id "
1272       "FROM hosts "
1273       "WHERE host_ipv6_subnet_id = ? "
1274       "AND id = ? "
1275       "LIMIT 1 "
1276       "ALLOW FILTERING "
1277      }},
1278 
1279     {GET_HOST_LIMIT,
1280      {GET_HOST_LIMIT,
1281       "SELECT "
1282       "key, "
1283       "id, "
1284       "host_identifier, "
1285       "host_identifier_type, "
1286       "host_ipv4_subnet_id, "
1287       "host_ipv6_subnet_id, "
1288       "host_ipv4_address, "
1289       "host_ipv4_next_server, "
1290       "host_ipv4_server_hostname, "
1291       "host_ipv4_boot_file_name, "
1292       "auth_key, "
1293       "hostname, "
1294       "user_context, "
1295       "host_ipv4_client_classes, "
1296       "host_ipv6_client_classes, "
1297       "reserved_ipv6_prefix_address, "
1298       "reserved_ipv6_prefix_length, "
1299       "reserved_ipv6_prefix_address_type, "
1300       "iaid, "
1301       "option_universe, "
1302       "option_code, "
1303       "option_value, "
1304       "option_formatted_value, "
1305       "option_space, "
1306       "option_is_persistent, "
1307       "option_client_class, "
1308       "option_subnet_id, "
1309       "option_user_context, "
1310       "option_scope_id "
1311       "FROM hosts "
1312       "LIMIT 1 "
1313       "ALLOW FILTERING "
1314      }},
1315 
1316     {GET_HOST_NEXT_KEY,
1317      {GET_HOST_NEXT_KEY,
1318       "SELECT "
1319       "key, "
1320       "id, "
1321       "host_identifier, "
1322       "host_identifier_type, "
1323       "host_ipv4_subnet_id, "
1324       "host_ipv6_subnet_id, "
1325       "host_ipv4_address, "
1326       "host_ipv4_next_server, "
1327       "host_ipv4_server_hostname, "
1328       "host_ipv4_boot_file_name, "
1329       "auth_key, "
1330       "hostname, "
1331       "user_context, "
1332       "host_ipv4_client_classes, "
1333       "host_ipv6_client_classes, "
1334       "reserved_ipv6_prefix_address, "
1335       "reserved_ipv6_prefix_length, "
1336       "reserved_ipv6_prefix_address_type, "
1337       "iaid, "
1338       "option_universe, "
1339       "option_code, "
1340       "option_value, "
1341       "option_formatted_value, "
1342       "option_space, "
1343       "option_is_persistent, "
1344       "option_client_class, "
1345       "option_subnet_id, "
1346       "option_user_context, "
1347       "option_scope_id "
1348       "FROM hosts "
1349       "WHERE TOKEN(key) > TOKEN(?) "
1350       "LIMIT 1 "
1351       "ALLOW FILTERING "
1352      }},
1353 
1354     {GET_HOST_KEY,
1355      {GET_HOST_KEY,
1356       "SELECT "
1357       "key, "
1358       "id, "
1359       "host_identifier, "
1360       "host_identifier_type, "
1361       "host_ipv4_subnet_id, "
1362       "host_ipv6_subnet_id, "
1363       "host_ipv4_address, "
1364       "host_ipv4_next_server, "
1365       "host_ipv4_server_hostname, "
1366       "host_ipv4_boot_file_name, "
1367       "auth_key, "
1368       "hostname, "
1369       "user_context, "
1370       "host_ipv4_client_classes, "
1371       "host_ipv6_client_classes, "
1372       "reserved_ipv6_prefix_address, "
1373       "reserved_ipv6_prefix_length, "
1374       "reserved_ipv6_prefix_address_type, "
1375       "iaid, "
1376       "option_universe, "
1377       "option_code, "
1378       "option_value, "
1379       "option_formatted_value, "
1380       "option_space, "
1381       "option_is_persistent, "
1382       "option_client_class, "
1383       "option_subnet_id, "
1384       "option_user_context, "
1385       "option_scope_id "
1386       "FROM hosts "
1387       "WHERE key = ? "
1388       "ALLOW FILTERING "
1389      }},
1390 
1391     {GET_HOST_PAGE,
1392      {GET_HOST_PAGE,
1393       "SELECT "
1394       "key, "
1395       "id, "
1396       "host_identifier, "
1397       "host_identifier_type, "
1398       "host_ipv4_subnet_id, "
1399       "host_ipv6_subnet_id, "
1400       "host_ipv4_address, "
1401       "host_ipv4_next_server, "
1402       "host_ipv4_server_hostname, "
1403       "host_ipv4_boot_file_name, "
1404       "auth_key, "
1405       "hostname, "
1406       "user_context, "
1407       "host_ipv4_client_classes, "
1408       "host_ipv6_client_classes, "
1409       "reserved_ipv6_prefix_address, "
1410       "reserved_ipv6_prefix_length, "
1411       "reserved_ipv6_prefix_address_type, "
1412       "iaid, "
1413       "option_universe, "
1414       "option_code, "
1415       "option_value, "
1416       "option_formatted_value, "
1417       "option_space, "
1418       "option_is_persistent, "
1419       "option_client_class, "
1420       "option_subnet_id, "
1421       "option_user_context, "
1422       "option_scope_id "
1423       "FROM hosts "
1424       "WHERE id = ? "
1425       "LIMIT 1 "
1426       "ALLOW FILTERING "
1427      }},
1428 
1429     {DELETE_HOST,
1430      {DELETE_HOST,
1431       "DELETE FROM hosts WHERE key = ? AND id = ? "
1432       "IF EXISTS "
1433      }}
1434 };
1435 
CqlHostExchange()1436 CqlHostExchange::CqlHostExchange()
1437     : host_(NULL), id_(0), host_identifier_type_(0), host_ipv4_subnet_id_(0),
1438       host_ipv6_subnet_id_(0), host_ipv4_address_(0), host_ipv4_next_server_(0),
1439       host_ipv4_server_hostname_(NULL_DHCP4_SERVER_HOSTNAME),
1440       host_ipv4_boot_file_name_(NULL_DHCP4_BOOT_FILE_NAME),
1441       auth_key_(""),
1442       user_context_(NULL_USER_CONTEXT),
1443       reserved_ipv6_prefix_length_(NULL_RESERVED_IPV6_PREFIX_LENGTH),
1444       reserved_ipv6_prefix_address_type_(NULL_RESERVED_IPV6_PREFIX_ADDRESS_TYPE),
1445       iaid_(NULL_IAID), option_universe_(NULL_OPTION_UNIVERSE),
1446       option_code_(NULL_OPTION_CODE),
1447       option_is_persistent_(NULL_OPTION_IS_PERSISTENT),
1448       option_subnet_id_(NULL_OPTION_SUBNET_ID),
1449       option_user_context_(NULL_OPTION_USER_CONTEXT),
1450       option_scope_id_(NULL_OPTION_SCOPE_ID) {
1451 }
1452 
~CqlHostExchange()1453 CqlHostExchange::~CqlHostExchange() {
1454 }
1455 
1456 void
createBindForSelect(AnyArray & data,StatementTag)1457 CqlHostExchange::createBindForSelect(AnyArray& data, StatementTag /* not used */) {
1458     // Start with a fresh array.
1459     data.clear();
1460 
1461     // key: bigint
1462     data.add(&key_);
1463     // id: bigint
1464     data.add(&id_);
1465     // host_identifier: blob
1466     data.add(&host_identifier_);
1467     // host_identifier_type: int
1468     data.add(&host_identifier_type_);
1469     // host_ipv4_subnet_id: int
1470     data.add(&host_ipv4_subnet_id_);
1471     // host_ipv6_subnet_id: int
1472     data.add(&host_ipv6_subnet_id_);
1473     // host_ipv4_address: int
1474     data.add(&host_ipv4_address_);
1475     // host_ipv4_next_server: int
1476     data.add(&host_ipv4_next_server_);
1477     // host_ipv4_server_hostname: text
1478     data.add(&host_ipv4_server_hostname_);
1479     // host_ipv4_boot_file_name: text
1480     data.add(&host_ipv4_boot_file_name_);
1481     // auth_key: text
1482     data.add(&auth_key_);
1483     // hostname: text
1484     data.add(&hostname_);
1485     // user_context: text
1486     data.add(&user_context_);
1487     // host_ipv4_client_classes: text
1488     data.add(&host_ipv4_client_classes_);
1489     // host_ipv6_client_classes: text
1490     data.add(&host_ipv6_client_classes_);
1491     /// @brief Denormalized reservation columns
1492     /// @{
1493     // reserved_ipv6_prefix_address: text
1494     data.add(&reserved_ipv6_prefix_address_);
1495     // reserved_ipv6_prefix_length: int
1496     data.add(&reserved_ipv6_prefix_length_);
1497     // reserved_ipv6_prefix_address_type: int
1498     data.add(&reserved_ipv6_prefix_address_type_);
1499     // iaid: int
1500     data.add(&iaid_);
1501     /// @}
1502     /// @brief Denormalized option columns
1503     /// @{
1504     // option_universe: int
1505     data.add(&option_universe_);
1506     // option_code: int
1507     data.add(&option_code_);
1508     // option_value: blob
1509     data.add(&option_value_);
1510     // option_formatted_value: text
1511     data.add(&option_formatted_value_);
1512     // option_space: text
1513     data.add(&option_space_);
1514     // option_is_persistent: boolean
1515     data.add(&option_is_persistent_);
1516     // option_client_class: text
1517     data.add(&option_client_class_);
1518     // option_subnet_id: int
1519     data.add(&option_subnet_id_);
1520     // option_user_context: text
1521     data.add(&option_user_context_);
1522     // option_scope_id: int
1523     data.add(&option_scope_id_);
1524     /// @}
1525 }
1526 
1527 void
prepareExchange(const HostPtr & host,const Optional<SubnetID> & subnet_id,const IPv6Resrv * const reservation,const std::string & option_space,const OptionDescriptor & option_descriptor)1528 CqlHostExchange::prepareExchange(const HostPtr& host,
1529                                  const Optional<SubnetID>& subnet_id,
1530                                  const IPv6Resrv* const reservation,
1531                                  const std::string& option_space,
1532                                  const OptionDescriptor& option_descriptor) {
1533 
1534     // Store host object to ensure it remains valid.
1535     host_ = host;
1536 
1537     // Set up the structures for the various components of the host
1538     // structure.
1539     try {
1540         // host_identifier: blob
1541         // Convert from std::vector<uint8_t> to
1542         // std::vector<cass_byte_t>.
1543         HostIdentifier host_identifier = host->getIdentifier();
1544         host_identifier_ = CassBlob(host_identifier.begin(), host_identifier.end());
1545         if (host_identifier_.size() > DUID::MAX_DUID_LEN) {
1546             isc_throw(BadValue, "CqlHostExchange::prepareExchange(): host identifier "
1547                       << host_identifier_.data() << " of length " << host_identifier_.size()
1548                       << " is greater than allowed of " << DUID::MAX_DUID_LEN);
1549         }
1550 
1551         // host_identifier_type: tinyint
1552         host_identifier_type_ = static_cast<cass_int32_t>(host->getIdentifierType());
1553         if (host_identifier_type_ > MAX_IDENTIFIER_TYPE) {
1554             isc_throw(BadValue, "CqlHostExchange::prepareExchange(): invalid "
1555                       "host identifier type returned: " << host_identifier_type_);
1556         }
1557 
1558         // host_ipv4_subnet_id: int
1559         host_ipv4_subnet_id_ = static_cast<cass_int32_t>(host->getIPv4SubnetID());
1560 
1561         // host_ipv6_subnet_id: int
1562         host_ipv6_subnet_id_ = static_cast<cass_int32_t>(host->getIPv6SubnetID());
1563 
1564         // host_ipv4_address: int
1565         host_ipv4_address_ = static_cast<cass_int32_t>(host->getIPv4Reservation().toUint32());
1566 
1567         // host_ipv4_next_server: int
1568         host_ipv4_next_server_ = static_cast<cass_int32_t>(host->getNextServer().toUint32());
1569 
1570         // host_ipv4_server_hostname: text
1571         host_ipv4_server_hostname_ = host->getServerHostname();
1572 
1573         // host_ipv4_boot_file_name: text
1574         host_ipv4_boot_file_name_ = host->getBootFileName();
1575 
1576         // auth_key: varchar
1577         auth_key_ = host->getKey().toText();
1578 
1579         // hostname: text
1580         hostname_ = host->getHostname();
1581         if (hostname_.size() > HOSTNAME_MAX_LEN) {
1582             isc_throw(BadValue, "CqlHostExchange::prepareExchange(): hostname "
1583                       << hostname_ << " of length " << hostname_.size()
1584                       << " is greater than allowed of " << HOSTNAME_MAX_LEN);
1585         }
1586 
1587         // lower_case_hostname: text
1588         lower_case_hostname_ = host->getLowerHostname();
1589         if (lower_case_hostname_.size() > HOSTNAME_MAX_LEN) {
1590             // Should never happen...
1591             isc_throw(BadValue, "CqlHostExchange::prepareExchange(): lower "
1592                       "case hostname " << lower_case_hostname_ << " of length "
1593                       << lower_case_hostname_.size()
1594                       << " is greater than allowed of " << HOSTNAME_MAX_LEN);
1595         }
1596 
1597         // user_context: text
1598         ConstElementPtr ctx = host->getContext();
1599         if (ctx) {
1600             user_context_ = ctx->str();
1601         } else {
1602             user_context_ = NULL_USER_CONTEXT;
1603         }
1604 
1605         // host_ipv4_client_classes: text
1606         host_ipv4_client_classes_ = host->getClientClasses4().toText(",");
1607         if (host_ipv4_client_classes_.size() > CLIENT_CLASSES_MAX_LEN) {
1608             isc_throw(BadValue, "CqlHostExchange::prepareExchange(): "
1609                       "IPv4 client classes " << host_ipv4_client_classes_ << " of length "
1610                       << host_ipv4_client_classes_.size() << " is greater than allowed of "
1611                       << CLIENT_CLASSES_MAX_LEN);
1612         }
1613 
1614         // host_ipv6_client_classes: text
1615         host_ipv6_client_classes_ = host->getClientClasses6().toText(",");
1616         if (host_ipv6_client_classes_.size() > CLIENT_CLASSES_MAX_LEN) {
1617             isc_throw(BadValue, "CqlHostExchange::prepareExchange(): "
1618                       "IPv6 client classes " << host_ipv6_client_classes_ << " of length "
1619                       << host_ipv6_client_classes_.size() << " is greater than allowed of "
1620                       << CLIENT_CLASSES_MAX_LEN);
1621         }
1622 
1623         if (reservation == NULL) {
1624             // reserved_ipv6_prefix_address: text
1625             reserved_ipv6_prefix_address_ = NULL_RESERVED_IPV6_PREFIX_ADDRESS;
1626             // reserved_ipv6_prefix_length: int
1627             reserved_ipv6_prefix_length_ = NULL_RESERVED_IPV6_PREFIX_LENGTH;
1628             // reserved_ipv6_prefix_address_type: int
1629             reserved_ipv6_prefix_address_type_ = NULL_RESERVED_IPV6_PREFIX_ADDRESS_TYPE;
1630             iaid_ = NULL_IAID;
1631         } else {
1632             // reserved_ipv6_prefix_address: text
1633             reserved_ipv6_prefix_address_ = reservation->getPrefix().toText();
1634 
1635             // reserved_ipv6_prefix_length: int
1636             reserved_ipv6_prefix_length_ = static_cast<cass_int32_t>(reservation->getPrefixLen());
1637 
1638             // reserved_ipv6_prefix_address_type: int
1639             reserved_ipv6_prefix_address_type_ =
1640                 reservation->getType() == IPv6Resrv::TYPE_NA ? 0 : 2;
1641 
1642             // iaid: int
1643             /// @todo: We don't support iaid in the IPv6Resrv yet.
1644             iaid_ = 0;
1645         }
1646 
1647         if (option_descriptor.option_ == NULL) {
1648             option_universe_ = NULL_OPTION_UNIVERSE;
1649             option_code_ = NULL_OPTION_CODE;
1650             option_value_ = NULL_OPTION_VALUE;
1651             option_formatted_value_ = NULL_OPTION_FORMATTED_VALUE;
1652             option_space_ = NULL_OPTION_SPACE;
1653             option_is_persistent_ = NULL_OPTION_IS_PERSISTENT;
1654             option_client_class_ = NULL_OPTION_CLIENT_CLASS;
1655             option_subnet_id_ = NULL_OPTION_SUBNET_ID;
1656             option_user_context_ = NULL_OPTION_USER_CONTEXT;
1657             option_scope_id_ = NULL_OPTION_SCOPE_ID;
1658         } else {
1659             // option_universe: int
1660             option_universe_ = option_descriptor.option_->getUniverse();
1661 
1662             // option_code: int
1663             option_code_ = option_descriptor.option_->getType();
1664 
1665             // option_value: blob
1666             // option_formatted_value: text
1667             if (option_descriptor.formatted_value_.empty()) {
1668                 if (option_descriptor.option_->len() >
1669                     option_descriptor.option_->getHeaderLen()) {
1670                     // The formatted_value is empty and the option value
1671                     // is not empty so we need to prepare on-wire format
1672                     // for the option and store it in the database as a
1673                     // blob.
1674                     OutputBuffer buffer(option_descriptor.option_->len());
1675                     option_descriptor.option_->pack(buffer);
1676                     const char* buffer_ptr = static_cast<const char*>(buffer.getData());
1677                     option_value_.assign(buffer_ptr + option_descriptor.option_->getHeaderLen(),
1678                                          buffer_ptr + buffer.getLength());
1679                 } else {
1680                     option_value_.clear();
1681                 }
1682                 option_formatted_value_.clear();
1683             } else {
1684                 option_value_.clear();
1685                 option_formatted_value_ = option_descriptor.formatted_value_;
1686             }
1687 
1688             // option_space: text
1689             option_space_ = option_space;
1690 
1691             // option_is_persistent: boolean
1692             option_is_persistent_ = option_descriptor.persistent_ ? cass_true : cass_false;
1693 
1694             // option_client_class: text
1695             /// @todo Assign actual value to client class string. See #5503.
1696             option_client_class_.clear();
1697 
1698             // option_subnet_id: int
1699             if (!subnet_id.unspecified()) {
1700                 option_subnet_id_ = subnet_id;
1701             } else {
1702                 option_subnet_id_ = 0;
1703             }
1704 
1705             // option_user_context: text
1706             ConstElementPtr ctx = option_descriptor.getContext();
1707             if (ctx) {
1708                 option_user_context_ = ctx->str();
1709             } else {
1710                 option_user_context_ = NULL_OPTION_USER_CONTEXT;
1711             }
1712 
1713             // option_scope_id: int
1714             // Using fixed scope_id = 3, which associates an option with host.
1715             option_scope_id_ = 3;
1716         }
1717 
1718         // id: bigint
1719         id_ = static_cast<cass_int64_t>(hashIntoId());
1720 
1721         // key: bigint
1722         key_ = static_cast<cass_int64_t>(hashIntoKey());
1723     } catch (const Exception& ex) {
1724         isc_throw(DbOperationError,
1725                   "CqlHostExchange::prepareExchange(): "
1726                   "could not copy data from host "
1727                   << host->getHostname() << ", reason: " << ex.what());
1728     }
1729 }
1730 
1731 void
createBindForMutation(const HostPtr & host,const Optional<SubnetID> & subnet_id,const IPv6Resrv * const reservation,const std::string & option_space,const OptionDescriptor & option_descriptor,StatementTag statement_tag,AnyArray & data)1732 CqlHostExchange::createBindForMutation(const HostPtr& host,
1733                                        const Optional<SubnetID>& subnet_id,
1734                                        const IPv6Resrv* const reservation,
1735                                        const std::string& option_space,
1736                                        const OptionDescriptor& option_descriptor,
1737                                        StatementTag statement_tag, AnyArray& data) {
1738     prepareExchange(host, subnet_id, reservation, option_space, option_descriptor);
1739 
1740     try {
1741         // Add all parameters to bind array.
1742         data.clear();
1743 
1744         if (statement_tag == CqlHostExchange::INSERT_HOST) {
1745             data.add(&key_);
1746             data.add(&id_);
1747             data.add(&host_identifier_);
1748             data.add(&host_identifier_type_);
1749             data.add(&host_ipv4_subnet_id_);
1750             data.add(&host_ipv6_subnet_id_);
1751             data.add(&host_ipv4_address_);
1752             data.add(&host_ipv4_next_server_);
1753             data.add(&host_ipv4_server_hostname_);
1754             data.add(&host_ipv4_boot_file_name_);
1755             data.add(&auth_key_);
1756             data.add(&hostname_);
1757             data.add(&lower_case_hostname_);
1758             data.add(&user_context_);
1759             data.add(&host_ipv4_client_classes_);
1760             data.add(&host_ipv6_client_classes_);
1761         }
1762 
1763         // Reservation
1764         data.add(&reserved_ipv6_prefix_address_);
1765         data.add(&reserved_ipv6_prefix_length_);
1766         data.add(&reserved_ipv6_prefix_address_type_);
1767         data.add(&iaid_);
1768 
1769         // Option
1770         data.add(&option_universe_);
1771         data.add(&option_code_);
1772         data.add(&option_value_);
1773         data.add(&option_formatted_value_);
1774         data.add(&option_space_);
1775         data.add(&option_is_persistent_);
1776         data.add(&option_client_class_);
1777         data.add(&option_subnet_id_);
1778         data.add(&option_user_context_);
1779         data.add(&option_scope_id_);
1780 
1781     } catch (const Exception& ex) {
1782         isc_throw(DbOperationError,
1783                   "CqlHostExchange::createBindForMutation(): "
1784                   "could not create bind array from host "
1785                   << host->getHostname() << ", reason: " << ex.what());
1786     }
1787 }
1788 
1789 void
createBindForDelete(const HostPtr & host,const Optional<SubnetID> & subnet_id,const IPv6Resrv * const reservation,const std::string & option_space,const OptionDescriptor & option_descriptor,StatementTag statement_tag,AnyArray & data)1790 CqlHostExchange::createBindForDelete(const HostPtr& host,
1791                                      const Optional<SubnetID>& subnet_id,
1792                                      const IPv6Resrv* const reservation,
1793                                      const std::string& option_space,
1794                                      const OptionDescriptor& option_descriptor,
1795                                      StatementTag statement_tag, AnyArray& data) {
1796     prepareExchange(host, subnet_id, reservation, option_space, option_descriptor);
1797 
1798     try {
1799         // Add all parameters to bind array.
1800         data.clear();
1801 
1802         if (statement_tag == CqlHostExchange::DELETE_HOST) {
1803             data.add(&key_);
1804             data.add(&id_);
1805         }
1806 
1807     } catch (const Exception& ex) {
1808         isc_throw(DbOperationError,
1809                   "CqlHostExchange::createBindForDelete(): "
1810                   "could not create bind array from host "
1811                   << host->getHostname() << ", reason: " << ex.what());
1812     }
1813 }
1814 
1815 uint64_t
hashIntoId() const1816 CqlHostExchange::hashIntoId() const {
1817     // Add a separator between aggregated field to avoid collisions
1818     // between distinct entries.
1819 
1820     // Get key.
1821     std::stringstream key_stream;
1822     key_stream << hostKey();
1823     key_stream << std::setw(V6ADDRESS_TEXT_MAX_LEN) << std::setfill('-')
1824                << reserved_ipv6_prefix_address_;
1825     key_stream << std::setw(4) << std::setfill('-')
1826                << reserved_ipv6_prefix_length_;
1827     key_stream << std::setw(4) << std::setfill('-') << option_code_;
1828     key_stream << std::setw(OPTION_SPACE_MAX_LEN) << std::setfill('-')
1829                << option_space_;
1830     const std::string key = key_stream.str();
1831 
1832     return (Hash64::hash(key));
1833 }
1834 
1835 uint64_t
hashIntoKey() const1836 CqlHostExchange::hashIntoKey() const {
1837     const std::string key = hostKey();
1838 
1839     return (Hash64::hash(key));
1840 }
1841 
1842 std::string
hostKey() const1843 CqlHostExchange::hostKey() const {
1844     // Add a separator between aggregated field to avoid collisions
1845     // between distinct entries.
1846     // Get key.
1847     std::stringstream key_stream;
1848     if (host_ipv4_address_) {
1849         key_stream << std::setw(3 * DUID::MAX_DUID_LEN - 1) << std::setfill('-')
1850                    << "-";
1851         key_stream << std::setw(10) << std::setfill('-') << "-";
1852     } else {
1853         key_stream << std::setw(3 * DUID::MAX_DUID_LEN - 1) << std::setfill('-')
1854                    << DUID(host_identifier_).toText();
1855         key_stream << std::setw(10) << std::setfill('-') << host_identifier_type_;
1856     }
1857     key_stream << std::setw(10) << std::setfill('-') << host_ipv4_subnet_id_;
1858     key_stream << std::setw(10) << std::setfill('-') << host_ipv6_subnet_id_;
1859     key_stream << std::setw(V4ADDRESS_TEXT_MAX_LEN) << std::setfill('-')
1860                << host_ipv4_address_;
1861     return key_stream.str();
1862 }
1863 
1864 boost::any
retrieve()1865 CqlHostExchange::retrieve() {
1866     const uint64_t id = static_cast<uint64_t>(id_);
1867 
1868     HostIdentifier host_identifier =
1869         HostIdentifier(host_identifier_.begin(), host_identifier_.end());
1870 
1871     // Set the host identifier type in a variable of the appropriate
1872     // data type.
1873     Host::IdentifierType host_identifier_type =
1874         static_cast<Host::IdentifierType>(host_identifier_type_);
1875 
1876     // Set IPv4 subnet ID to the value returned.
1877     SubnetID ipv4_subnet_id = static_cast<SubnetID>(host_ipv4_subnet_id_);
1878 
1879     // Set IPv6 subnet ID to the value returned.
1880     SubnetID ipv6_subnet_id = static_cast<SubnetID>(host_ipv6_subnet_id_);
1881 
1882     // Set IPv4 address reservation.
1883     asiolink::IOAddress ipv4_reservation =
1884         asiolink::IOAddress(static_cast<uint32_t>(host_ipv4_address_));
1885 
1886     Host* host = new Host(host_identifier.data(), host_identifier.size(),
1887                           host_identifier_type, ipv4_subnet_id, ipv6_subnet_id,
1888                           ipv4_reservation, hostname_,
1889                           host_ipv4_client_classes_, host_ipv6_client_classes_,
1890                           static_cast<uint32_t>(host_ipv4_next_server_),
1891                           host_ipv4_server_hostname_, host_ipv4_boot_file_name_,
1892                           AuthKey(auth_key_));
1893 
1894     // Set the user context if there is one.
1895     if (!user_context_.empty()) {
1896         try {
1897             ConstElementPtr ctx = Element::fromJSON(user_context_);
1898             if (!ctx || (ctx->getType() != Element::map)) {
1899                 isc_throw(BadValue, "user context '" << user_context_
1900                           << "' is not a JSON map");
1901             }
1902             host->setContext(ctx);
1903         } catch (const isc::data::JSONError& ex) {
1904             isc_throw(BadValue, "user context '" << user_context_
1905                       << "' is invalid JSON: " << ex.what());
1906         }
1907     }
1908 
1909     host->setHostId(id);
1910 
1911     const IPv6Resrv reservation = retrieveReservation();
1912     if (reservation != NULL_IPV6_RESERVATION &&
1913         !host->hasReservation(reservation)) {
1914         host->addReservation(reservation);
1915     }
1916 
1917     OptionWrapper option_wrapper = retrieveOption();
1918     if (option_wrapper.option_descriptor_) {
1919         if (option_wrapper.option_descriptor_->option_->getUniverse() == Option::V4) {
1920             host->getCfgOption4()->add(*option_wrapper.option_descriptor_,
1921                                        option_wrapper.option_space_);
1922         } else if (option_wrapper.option_descriptor_->option_->getUniverse() == Option::V6) {
1923             host->getCfgOption6()->add(*option_wrapper.option_descriptor_,
1924                                        option_wrapper.option_space_);
1925         }
1926     }
1927 
1928     return (host);
1929 }
1930 
1931 const IPv6Resrv
retrieveReservation() const1932 CqlHostExchange::retrieveReservation() const {
1933     // Set the IPv6 Reservation type (0 = IA_NA, 2 = IA_PD).
1934     IPv6Resrv::Type type;
1935     switch (reserved_ipv6_prefix_address_type_) {
1936     case 0:
1937         type = IPv6Resrv::TYPE_NA;
1938         break;
1939     case 2:
1940         type = IPv6Resrv::TYPE_PD;
1941         break;
1942     case NULL_RESERVED_IPV6_PREFIX_ADDRESS_TYPE:
1943         return (NULL_IPV6_RESERVATION);
1944     default:
1945         isc_throw(BadValue, "CqlHostExchange::retrieveReservation(): invalid IPv6 "
1946                   "reservation type returned: " << reserved_ipv6_prefix_address_type_
1947                   << ". Only 0 (IA_NA) or 2 (IA_PD) are allowed.");
1948     }
1949 
1950     return (IPv6Resrv(type, IOAddress(reserved_ipv6_prefix_address_),
1951                       reserved_ipv6_prefix_length_));
1952 }
1953 
1954 const OptionWrapper
retrieveOption() const1955 CqlHostExchange::retrieveOption() const {
1956     // Options are held in a binary or textual format in the database.
1957     // This is similar to having an option specified in a server
1958     // configuration file. Such option is converted to appropriate C++
1959     // class, using option definition. Thus, we need to find the
1960     // option definition for this option code and option space.
1961 
1962     // If the option space is a standard DHCPv4 or DHCPv6 option space,
1963     // this is most likely a standard option, for which we have a
1964     // definition created within libdhcp++.
1965     if (option_space_.empty() || option_universe_ == NULL_OPTION_UNIVERSE) {
1966         return (OptionWrapper(OptionDescriptorPtr(), ""));
1967     }
1968 
1969     OptionDefinitionPtr option_definition_ptr =
1970         LibDHCP::getOptionDef(option_space_, option_code_);
1971 
1972     // Otherwise, we may check if this an option encapsulated within the
1973     // vendor space.
1974     if (!option_definition_ptr && option_space_ != DHCP4_OPTION_SPACE &&
1975         option_space_ != DHCP6_OPTION_SPACE) {
1976         uint32_t vendor_id = LibDHCP::optionSpaceToVendorId(option_space_);
1977         if (vendor_id > 0) {
1978             option_definition_ptr = LibDHCP::getVendorOptionDef(
1979                 static_cast<Option::Universe>(option_universe_), vendor_id,
1980                 option_code_);
1981         }
1982     }
1983 
1984     // In all other cases, we use runtime option definitions, which
1985     // should be also registered within the libdhcp++.
1986     if (!option_definition_ptr) {
1987         option_definition_ptr =
1988             LibDHCP::getRuntimeOptionDef(option_space_, option_code_);
1989     }
1990 
1991     OptionPtr option;
1992     if (!option_definition_ptr) {
1993         // If no definition found, we use generic option type.
1994         OptionBuffer option_buffer(option_value_.begin(), option_value_.end());
1995         option = boost::make_shared<Option>(static_cast<Option::Universe>(option_universe_),
1996                                             static_cast<uint16_t>(option_code_),
1997                                             option_buffer.begin(), option_buffer.end());
1998     } else {
1999         // The option value may be specified in textual or binary format
2000         // in the
2001         // database. If formatted_value is empty, the binary format is
2002         // used.
2003         // Depending on the format we use a different variant of @ref
2004         // optionFactory().
2005         if (option_formatted_value_.empty()) {
2006             OptionBuffer option_buffer(option_value_.begin(),
2007                                        option_value_.end());
2008             option = option_definition_ptr->optionFactory(
2009                 static_cast<Option::Universe>(option_universe_),
2010                 static_cast<uint16_t>(option_code_), option_buffer.begin(),
2011                 option_buffer.end());
2012         } else {
2013             // Spit the value specified in comma separated values
2014             // format.
2015             std::vector<std::string> split_vector;
2016             boost::split(split_vector, option_formatted_value_,
2017                          boost::is_any_of(","));
2018             option = option_definition_ptr->optionFactory(
2019                 static_cast<Option::Universe>(option_universe_),
2020                 static_cast<uint16_t>(option_code_), split_vector);
2021         }
2022     }
2023 
2024     OptionWrapper result(boost::make_shared<OptionDescriptor>(option, option_is_persistent_,
2025                                                               option_formatted_value_),
2026                          option_space_);
2027     // Set the user context if there is one into the option descriptor.
2028     if (!option_user_context_.empty()) {
2029         try {
2030             ConstElementPtr ctx = Element::fromJSON(option_user_context_);
2031             if (!ctx || (ctx->getType() != Element::map)) {
2032                 isc_throw(BadValue, "option user context '" << option_user_context_
2033                           << "' is no a JSON map");
2034             }
2035             result.option_descriptor_->setContext(ctx);
2036         } catch (const isc::data::JSONError& ex) {
2037             isc_throw(BadValue, "option user context '" << option_user_context_
2038                       << "' is invalid JSON: " << ex.what());
2039         }
2040     }
2041 
2042     return result;
2043 }
2044 
2045 /// @brief Implementation of the @ref CqlHostDataSource.
2046 ///
2047 /// This class is encapsulate all the Cassandra communication details.
2048 class CqlHostDataSourceImpl {
2049 public:
2050     /// @brief Constructor.
2051     ///
2052     /// This constructor opens database connection and initializes
2053     /// prepared statements used in the queries.
2054     /// @param parameters parameters passed to the CQL connection.
2055     explicit CqlHostDataSourceImpl(const DatabaseConnection::ParameterMap& parameters);
2056 
2057     /// @brief Destructor.
2058     virtual ~CqlHostDataSourceImpl();
2059 
2060     /// @brief Implementation of @ref CqlHostDataSource::add() and del()
2061     ///
2062     /// See @ref CqlHostDataSource::add() for parameter details.
2063     ///
2064     /// @param host host to be added or deleted
2065     /// @param insert insert (true) or delete (false) the host
2066     virtual bool insertOrDelete(const HostPtr& host, bool insert);
2067 
2068     /// @brief Implementation of @ref CqlHostDataSource::get4()
2069     ///
2070     /// See @ref CqlHostDataSource::get4() for parameter details.
2071     ///
2072     /// @param subnet_id Id of the subnet to look into
2073     /// @param address IPv4 address to be retrieved
2074     virtual ConstHostPtr get4(const SubnetID& subnet_id,
2075                               const asiolink::IOAddress& address) const;
2076 
2077     /// @brief Implementation of @ref CqlHostDataSource::get4()
2078     ///
2079     /// See @ref CqlHostDataSource::get4() for parameter details.
2080     ///
2081     /// @param subnet_id Id of the subnet to look into
2082     /// @param identifier_type type of the identifier
2083     /// @param identifier_begin pointer to the first byte of the identifier
2084     /// @param identifier_len length of the identifier
2085     virtual ConstHostPtr get4(const SubnetID& subnet_id,
2086                               const Host::IdentifierType& identifier_type,
2087                               const uint8_t* identifier_begin,
2088                               const size_t identifier_len) const;
2089 
2090     /// @brief Retrieves a host by its reserved IPv6 address or prefix
2091     ///
2092     /// See @ref CqlHostDataSource::get6() for parameter details.
2093     ///
2094     /// @param prefix IPv6 address or prefix
2095     /// @param prefix_len length of the prefix (or 128 for address)
2096     virtual ConstHostPtr get6(const asiolink::IOAddress& prefix,
2097                               const uint8_t prefix_len) const;
2098 
2099     /// @brief Implementation of @ref CqlHostDataSource::get6()
2100     ///
2101     /// See @ref CqlHostDataSource::get6() for parameter details.
2102     ///
2103     /// @param subnet_id Id of the subnet to look into
2104     /// @param identifier_type type of the identifier (duid, hwaddr, flex, etc.)
2105     /// @param identifier_begin pointer to the first byte of the identifier
2106     /// @param identifier_len length of the identifier
2107     virtual ConstHostPtr get6(const SubnetID& subnet_id,
2108                               const Host::IdentifierType& identifier_type,
2109                               const uint8_t* identifier_begin,
2110                               const size_t identifier_len) const;
2111 
2112     /// @brief Implementation of @ref CqlHostDataSource::get6()
2113     ///
2114     /// See @ref CqlHostDataSource::get6() for parameter details.
2115     ///
2116     /// @param subnet_id Id of the subnet to look into
2117     /// @param address IPv6 address to be retrieved
2118     virtual ConstHostPtr get6(const SubnetID& subnet_id,
2119                               const asiolink::IOAddress& address) const;
2120 
2121     /// @brief Implementation of @ref CqlHostDataSource::getAll()
2122     ///
2123     /// See @ref CqlHostDataSource::getAll() for parameter details.
2124     ///
2125     /// @param identifier_type type of the identifier (duid, hwaddr, flex, etc.)
2126     /// @param identifier_begin pointer to the first byte of the identifier
2127     /// @param identifier_len length of the identifier
2128     virtual ConstHostCollection
2129     getAll(const Host::IdentifierType& identifier_type,
2130            const uint8_t* identifier_begin,
2131            const size_t identifier_len) const;
2132 
2133     /// @brief Implementation of @ref CqlHostDataSource::getAll4()
2134     ///
2135     /// See @ref CqlHostDataSource::getAll4() for parameter details.
2136     ///
2137     /// @param subnet_id identifier of the subnet to which hosts belong
2138     virtual ConstHostCollection getAll4(const SubnetID& subnet_id) const;
2139 
2140     /// @brief Implementation of @ref CqlHostDataSource::getAll6()
2141     ///
2142     /// See @ref CqlHostDataSource::getAll6() for parameter details.
2143     ///
2144     /// @param subnet_id identifier of the subnet to which hosts belong
2145     virtual ConstHostCollection getAll6(const SubnetID& subnet_id) const;
2146 
2147     /// @brief Implementation of @ref CqlHostDataSource::getAllbyHostname()
2148     ///
2149     /// See @ref CqlHostDataSource::getAllbyHostname() for parameter details.
2150     ///
2151     /// @param hostname The lower case hostname.
2152     virtual ConstHostCollection
2153     getAllbyHostname(const std::string& hostname) const;
2154 
2155     /// @brief Implementation of @ref CqlHostDataSource::getAllbyHostname4()
2156     ///
2157     /// See @ref CqlHostDataSource::getAllbyHostname4() for parameter details.
2158     ///
2159     /// @param hostname The lower case hostname.
2160     /// @param subnet_id Subnet identifier.
2161     virtual ConstHostCollection
2162     getAllbyHostname4(const std::string& hostname, const SubnetID& subnet_id) const;
2163 
2164     /// @brief Implementation of @ref CqlHostDataSource::getAllbyHostname6()
2165     ///
2166     /// See @ref CqlHostDataSource::getAllbyHostname6() for parameter details.
2167     ///
2168     /// @param hostname The lower case hostname.
2169     /// @param subnet_id Subnet identifier.
2170     virtual ConstHostCollection
2171     getAllbyHostname6(const std::string& hostname, const SubnetID& subnet_id) const;
2172 
2173     /// @brief Implementation of @ref CqlHostDataSource::getPage4()
2174     ///
2175     /// See @ref CqlHostDataSource::getPage4() for parameter details.
2176     ///
2177     /// @param subnet_id identifier of the subnet to which hosts belong
2178     /// @param lower_host_id Host identifier used as lower bound for the
2179     /// returned range.
2180     /// @param page_size maximum size of the page returned.
2181     virtual ConstHostCollection
2182     getPage4(const SubnetID& subnet_id,
2183              uint64_t lower_host_id,
2184              const HostPageSize& page_size) const;
2185 
2186     /// @brief Implementation of @ref CqlHostDataSource::getPage6()
2187     ///
2188     /// See @ref CqlHostDataSource::getPage6() for parameter details.
2189     ///
2190     /// @param subnet_id identifier of the subnet to which hosts belong
2191     /// @param lower_host_id Host identifier used as lower bound for the
2192     /// returned range.
2193     /// @param page_size maximum size of the page returned.
2194     virtual ConstHostCollection
2195     getPage6(const SubnetID& subnet_id,
2196              uint64_t lower_host_id,
2197              const HostPageSize& page_size) const;
2198 
2199     /// @brief Implementation of @ref CqlHostDataSource::getPage4()
2200     ///
2201     /// See @ref CqlHostDataSource::getPage4() for parameter details.
2202     ///
2203     /// @param lower_host_id Host identifier used as lower bound for the
2204     /// returned range.
2205     /// @param page_size maximum size of the page returned.
2206     virtual ConstHostCollection
2207     getPage4(uint64_t lower_host_id,
2208              const HostPageSize& page_size) const;
2209 
2210     /// @brief Implementation of @ref CqlHostDataSource::getPage6()
2211     ///
2212     /// See @ref CqlHostDataSource::getPage6() for parameter details.
2213     ///
2214     /// @param lower_host_id Host identifier used as lower bound for the
2215     /// returned range.
2216     /// @param page_size maximum size of the page returned.
2217     virtual ConstHostCollection
2218     getPage6(uint64_t lower_host_id,
2219              const HostPageSize& page_size) const;
2220 
2221     /// @brief Implementation of @ref CqlHostDataSource::getAll4()
2222     ///
2223     /// See @ref CqlHostDataSource::getAll4() for parameter details.
2224     ///
2225     /// @param address IPv4 address of the reservation to be retrieved
2226     virtual ConstHostCollection
2227     getAll4(const asiolink::IOAddress& address) const;
2228 
2229     /// @brief Implementation of @ref CqlHostDataSource::getAllHosts()
2230     ///
2231     /// See @ref CqlHostDataSource::getAllHosts() for parameter details.
2232     virtual ConstHostCollection
2233     getAllHosts() const;
2234 
2235     /// @brief Implementation of @ref CqlHostDataSource::getName()
2236     virtual std::string getName() const;
2237 
2238     /// @brief Implementation of @ref CqlHostDataSource::getVersion()
2239     virtual VersionPair getVersion() const;
2240 
2241 protected:
2242     /// @brief Adds/deletes any options found in the @ref Host object to/from a separate
2243     ///     table entry.
2244     ///
2245     /// @param insert insert or delete a host with options
2246     /// @param host @ref Host object from which options are retrieved and
2247     ///     inserted/deleted into/from the Cassandra database
2248     /// @param reservation reservation for the current denormalized table entry
2249     /// @param option_spaces list of option spaces to search for
2250     /// @param cfg_option option configuration used to match option spaces in
2251     ///     order to obtain actual options
2252     virtual bool insertOrDeleteHostWithOptions(bool insert,
2253         const HostPtr& host,
2254         const IPv6Resrv* const reservation = NULL,
2255         const std::list<std::string>& option_spaces = std::list<std::string>(),
2256         const ConstCfgOptionPtr cfg_option = ConstCfgOptionPtr());
2257 
2258     /// @brief Adds/deletes any reservations found in the @ref Host object to/from a separate
2259     ///     table entry.
2260     ///
2261     /// @param insert insert or deletes a host with reservations
2262     /// @param host @ref Host object from which reservations are retrieved and
2263     ///     inserted/deleted into/from the Cassandra database
2264     /// @param reservation reservation for the current denormalized table entry
2265     /// @param option_spaces4 list of option spaces for universe Option::V4 to search in
2266     /// @param cfg_option4 option configuration for universe Option::V4 used to
2267     ///     match option spaces in order to obtain actual options
2268     /// @param option_spaces6 list of option spaces for universe Option::V6 to
2269     ///     search in
2270     /// @param cfg_option6 option configuration for universe Option::V6 used to
2271     ///     match option spaces in order to obtain actual options
2272     virtual bool insertOrDeleteHostWithReservations(bool insert,
2273         const HostPtr& host,
2274         const IPv6Resrv* const reservation,
2275         const std::list<std::string>& option_spaces4,
2276         const ConstCfgOptionPtr cfg_option4,
2277         const std::list<std::string>& option_spaces6,
2278         const ConstCfgOptionPtr cfg_option6);
2279 
2280     /// @brief Retrieves a single host.
2281     ///
2282     /// Calls @ref getHostCollection() and checks if a single host was
2283     /// returned.
2284     ///
2285     /// @param where_values array of bound objects used to filter the results
2286     /// @param statement_tag prepared statement being executed
2287     ///
2288     /// @return one host or a null pointer to a host
2289     ///
2290     /// @throw MultipleRecords exception if two or more hosts are
2291     /// returned
2292     virtual ConstHostPtr getHost(StatementTag statement_tag,
2293                                  AnyArray& where_values) const;
2294 
2295     /// @brief Retrieves a collection of hosts.
2296     ///
2297     /// Calls @ref db::CqlExchange::executeSelect().
2298     ///
2299     /// @param where_values array of bound objects used to filter the results
2300     /// @param statement_tag prepared statement being executed
2301     ///
2302     /// @return a collection of hosts containing one or more hosts
2303     virtual ConstHostCollection getHostCollection(StatementTag statement_tag,
2304                                                   AnyArray& where_values) const;
2305 
2306     /// @brief Retrieves a page of hosts.
2307     ///
2308     /// @param subnet_id identifier of the subnet to which hosts belong
2309     /// @param lower_host_id Host identifier used as lower bound for the
2310     /// returned range.
2311     /// @param count the size of the page
2312     ///
2313     /// @return a collection of hosts containing one or more hosts
2314     virtual ConstHostCollection getHostCollectionPage4(const SubnetID& subnet_id,
2315                                                        uint64_t lower_host_id,
2316                                                        size_t count = 0) const;
2317 
2318     /// @brief Retrieves a page of hosts.
2319     ///
2320     /// @param subnet_id identifier of the subnet to which hosts belong
2321     /// @param lower_host_id Host identifier used as lower bound for the
2322     /// returned range.
2323     /// @param count the size of the page
2324     ///
2325     /// @return a collection of hosts containing one or more hosts
2326     virtual ConstHostCollection getHostCollectionPage6(const SubnetID& subnet_id,
2327                                                        uint64_t lower_host_id,
2328                                                        size_t count = 0) const;
2329 
2330     /// @brief Retrieves a page of hosts.
2331     ///
2332     /// @param lower_host_id Host identifier used as lower bound for the
2333     /// returned range.
2334     /// @param count the size of the page
2335     ///
2336     /// @return a collection of hosts containing one or more hosts
2337     virtual ConstHostCollection getHostCollectionPage4(uint64_t lower_host_id,
2338                                                        size_t count = 0) const;
2339 
2340     /// @brief Retrieves a page of hosts.
2341     ///
2342     /// @param lower_host_id Host identifier used as lower bound for the
2343     /// returned range.
2344     /// @param count the size of the page
2345     ///
2346     /// @return a collection of hosts containing one or more hosts
2347     virtual ConstHostCollection getHostCollectionPage6(uint64_t lower_host_id,
2348                                                        size_t count = 0) const;
2349 
2350     /// @brief Retrieves a host by key.
2351     ///
2352     /// @param key identifier of the host
2353     ///
2354     /// @return a host for the specific key
2355     virtual ConstHostPtr getHostByKey(uint64_t key) const;
2356 
2357     /// @brief Retrieves a valid host key.
2358     /// if lower_host_id is 0 the key parameter will be updated with the key of
2359     /// the first host
2360     /// if lower_host_id is not 0 the key parameter will be updated with the
2361     /// next valid host key
2362     ///
2363     /// @param subnet_id identifier of the subnet to which hosts belong
2364     /// @param lower_host_id Host identifier used as lower bound for the
2365     /// returned range.
2366     /// @param key identifier of the host which will be updated
2367     ///
2368     /// @return true if there is such a host
2369     virtual bool getHostKey4(const SubnetID& subnet_id,
2370                              uint64_t lower_host_id,
2371                              uint64_t& key) const;
2372 
2373     /// @brief Retrieves a valid host key.
2374     /// if lower_host_id is 0 the key parameter will be updated with the key of
2375     /// the first host
2376     /// if lower_host_id is not 0 the key parameter will be updated with the
2377     /// next valid host key
2378     ///
2379     /// @param subnet_id identifier of the subnet to which hosts belong
2380     /// @param lower_host_id Host identifier used as lower bound for the
2381     /// returned range.
2382     /// @param key identifier of the host which will be updated
2383     ///
2384     /// @return true if there is such a host
2385     virtual bool getHostKey6(const SubnetID& subnet_id,
2386                              uint64_t lower_host_id,
2387                              uint64_t& key) const;
2388 
2389     /// @brief Retrieves a valid host key.
2390     /// if lower_host_id is 0 the key parameter will be updated with the key of
2391     /// the first host
2392     /// if lower_host_id is not 0 the key parameter will be updated with the
2393     /// next valid host key
2394     ///
2395     /// @param lower_host_id Host identifier used as lower bound for the
2396     /// returned range.
2397     /// @param key identifier of the host which will be updated
2398     ///
2399     /// @return true if there is such a host
2400     virtual bool getHostKey(uint64_t lower_host_id,
2401                             uint64_t& key) const;
2402 
2403     /// @brief Retrieves next valid host key.
2404     ///
2405     /// @param subnet_id identifier of the subnet to which hosts belong
2406     /// @param key identifier of the host which will be updated with the next
2407     /// valid host key
2408     ///
2409     /// @return true if there is such a host
2410     virtual bool getNextHostKey4(const SubnetID& subnet_id,
2411                                  uint64_t& key) const;
2412 
2413     /// @brief Retrieves next valid host key.
2414     ///
2415     /// @param subnet_id identifier of the subnet to which hosts belong
2416     /// @param key identifier of the host which will be updated with the next
2417     /// valid host key
2418     ///
2419     /// @return true if there is such a host
2420     virtual bool getNextHostKey6(const SubnetID& subnet_id,
2421                                  uint64_t& key) const;
2422 
2423     /// @brief Retrieves next valid host key.
2424     ///
2425     /// @param key identifier of the host which will be updated with the next
2426     /// valid host key
2427     ///
2428     /// @return true if there is such a host
2429     virtual bool getNextHostKey(uint64_t& key) const;
2430 
2431     /// @brief Inserts or deletes a single host.
2432     ///
2433     /// All information is available here. Calls @ref
2434     /// db::CqlExchange::executeMutation().
2435     ///
2436     /// @param insert insert or delete a host
2437     /// @param host @ref Host object from which options are retrieved and
2438     ///     inserted/deleted into/from the Cassandra database
2439     /// @param subnet_id identifier of the subnet to which the host belongs
2440     /// @param reservation reservation for the current denormalized table entry
2441     /// @param option_space option space for the current denormalized table
2442     ///     entry's option
2443     /// @param option_descriptor option descriptor containing
2444     ///     information for the current denormalized table entry's option
2445     virtual bool insertOrDeleteHost(bool insert,
2446         const HostPtr& host,
2447         const Optional<SubnetID>& subnet_id = Optional<SubnetID>(),
2448         const IPv6Resrv* const reservation = NULL,
2449         const std::string& option_space = NULL_OPTION_SPACE,
2450         const OptionDescriptor& option_descriptor = OptionDescriptor(false));
2451 
2452     /// @brief Merge denormalized table entries that belong to the same host
2453     /// into a single host, one by one.
2454     ///
2455     /// @param target_host host which can contain multiple reservations and
2456     ///     options to which other distinct reservations and options are
2457     ///     added.
2458     /// @param source_host host that is being search for new reservations and
2459     ///     options that will be merged into the old host.
2460     virtual void mergeHosts(const ConstHostPtr& source_host,
2461                             HostPtr& target_host) const;
2462 
2463 private:
2464     /// @brief Parameters
2465     db::DatabaseConnection::ParameterMap parameters_;
2466 
2467     /// @brief CQL connection
2468     mutable CqlConnection dbconn_;
2469 };  // class CqlHostDataSourceImpl
2470 
2471 /// @brief hash function for HostMap
2472 ///
2473 /// Returns a 64-bits key value. The key is generated with FNV-1a 64 bit
2474 /// algorithm.
2475 ///
2476 /// @param key being hashed
2477 ///
2478 /// @return hash value
2479 std::size_t
hash_value(const HostKey & key)2480 hash_value(const HostKey& key) {
2481     // Get key.
2482     std::stringstream key_stream;
2483     HostIdentifier host_identifier = std::get<HOST_IDENTIFIER>(key);
2484     key_stream << DUID(host_identifier).toText() << "-";
2485     key_stream << std::get<HOST_IDENTIFIER_TYPE>(key) << "-";
2486     key_stream << std::get<IPv4_SUBNET_ID>(key) << "-";
2487     key_stream << std::get<IPv6_SUBNET_ID>(key) << "-";
2488     key_stream << std::get<IPv4_RESERVATION>(key);
2489     const std::string key_string = key_stream.str();
2490 
2491     const uint64_t hash = Hash64::hash(key_string);
2492 
2493     return (static_cast<std::size_t>(hash));
2494 }
2495 
2496 /// @brief equals operator for HostKey
2497 ///
2498 /// @param key1 left hand side operand
2499 /// @param key2 right hand side operand
2500 ///
2501 /// @return true if keys are equal. Deep comparison is made.
2502 bool
operator ==(const HostKey & key1,const HostKey & key2)2503 operator==(const HostKey& key1, const HostKey& key2) {
2504     return (std::get<HOST_IDENTIFIER>(key1) == std::get<HOST_IDENTIFIER>(key2) &&
2505             std::get<HOST_IDENTIFIER_TYPE>(key1) ==
2506             std::get<HOST_IDENTIFIER_TYPE>(key2) &&
2507             std::get<IPv4_SUBNET_ID>(key1) == std::get<IPv4_SUBNET_ID>(key2) &&
2508             std::get<IPv6_SUBNET_ID>(key1) == std::get<IPv6_SUBNET_ID>(key2) &&
2509             std::get<IPv4_RESERVATION>(key1) == std::get<IPv4_RESERVATION>(key2));
2510 }
2511 
CqlHostDataSourceImpl(const DatabaseConnection::ParameterMap & parameters)2512 CqlHostDataSourceImpl::CqlHostDataSourceImpl(const DatabaseConnection::ParameterMap& parameters)
2513     : parameters_(parameters), dbconn_(parameters) {
2514     // Validate the schema version first.
2515     std::pair<uint32_t, uint32_t> code_version(CQL_SCHEMA_VERSION_MAJOR,
2516                                                CQL_SCHEMA_VERSION_MINOR);
2517     std::pair<uint32_t, uint32_t> db_version = getVersion();
2518     if (code_version != db_version) {
2519         isc_throw(DbOpenError, "Cassandra schema version mismatch: need version: "
2520                   << code_version.first << "." << code_version.second
2521                   << " found version: " << db_version.first << "."
2522                   << db_version.second);
2523     }
2524 
2525     // Open the database.
2526     dbconn_.openDatabase();
2527 
2528     // Prepare all possible statements.
2529     dbconn_.prepareStatements(CqlHostExchange::tagged_statements_);
2530 }
2531 
~CqlHostDataSourceImpl()2532 CqlHostDataSourceImpl::~CqlHostDataSourceImpl() {
2533     // There is no need to close the database in this destructor: it is
2534     // closed in the destructor of the dbconn_ member variable.
2535 }
2536 
2537 bool
insertOrDelete(const HostPtr & host,bool insert)2538 CqlHostDataSourceImpl::insertOrDelete(const HostPtr& host, bool insert) {
2539     // If there is no host, there is nothing to do.
2540     if (!host) {
2541         return (false);
2542     }
2543 
2544     // Get option space names and vendor space names and combine them within a
2545     // single list.
2546 
2547     // For IPv4:
2548     ConstCfgOptionPtr cfg_option4 = host->getCfgOption4();
2549     std::list<std::string> option_spaces4 = cfg_option4->getOptionSpaceNames();
2550     std::list<std::string> vendor_spaces4 = cfg_option4->getVendorIdsSpaceNames();
2551     option_spaces4.insert(option_spaces4.end(), vendor_spaces4.begin(),
2552                           vendor_spaces4.end());
2553 
2554     // For IPv6:
2555     ConstCfgOptionPtr cfg_option6 = host->getCfgOption6();
2556     std::list<std::string> option_spaces6 = cfg_option6->getOptionSpaceNames();
2557     std::list<std::string> vendor_spaces6 = cfg_option6->getVendorIdsSpaceNames();
2558     option_spaces6.insert(option_spaces6.end(), vendor_spaces6.begin(),
2559                           vendor_spaces6.end());
2560 
2561     bool result = true;
2562 
2563     // For every IPv6 reservation, add each of their options to the
2564     // database.
2565     IPv6ResrvRange reservations = host->getIPv6Reservations();
2566     if (std::distance(reservations.first, reservations.second) > 0) {
2567         for (IPv6ResrvIterator it = reservations.first; result && it != reservations.second; ++it) {
2568             result = insertOrDeleteHostWithReservations(insert, host, &it->second, option_spaces4, cfg_option4,
2569                                                         option_spaces6, cfg_option6);
2570         }
2571     } else {
2572         // If host has no reservation, add entries with null
2573         // reservation. Options could still be present.
2574         result = insertOrDeleteHostWithReservations(insert, host, NULL, option_spaces4, cfg_option4,
2575                                                     option_spaces6, cfg_option6);
2576     }
2577 
2578     return (result);
2579 }
2580 
2581 ConstHostPtr
get4(const SubnetID & subnet_id,const asiolink::IOAddress & address) const2582 CqlHostDataSourceImpl::get4(const SubnetID& subnet_id, const asiolink::IOAddress& address) const {
2583     if (!address.isV4()) {
2584         isc_throw(BadValue, "CqlHostDataSource::get4(2): wrong address type, "
2585                             "address supplied is not an IPv4 address");
2586     }
2587 
2588     // Convert to CQL data types.
2589     cass_int32_t host_ipv4_subnet_id = static_cast<cass_int32_t>(subnet_id);
2590     cass_int32_t host_ipv4_address = static_cast<cass_int32_t>(address.toUint32());
2591 
2592     // Bind to array.
2593     AnyArray where_values;
2594     where_values.add(&host_ipv4_subnet_id);
2595     where_values.add(&host_ipv4_address);
2596 
2597     // Run statement.
2598     ConstHostPtr result = getHost(CqlHostExchange::GET_HOST_BY_IPV4_SUBNET_ID_AND_ADDRESS,
2599                                   where_values);
2600 
2601     return (result);
2602 }
2603 
2604 ConstHostPtr
get4(const SubnetID & subnet_id,const Host::IdentifierType & identifier_type,const uint8_t * identifier_begin,const size_t identifier_len) const2605 CqlHostDataSourceImpl::get4(const SubnetID& subnet_id,
2606                             const Host::IdentifierType& identifier_type,
2607                             const uint8_t* identifier_begin,
2608                             const size_t identifier_len) const {
2609     // Convert to CQL data types.
2610     CassBlob host_identifier(identifier_begin, identifier_begin + identifier_len);
2611     cass_int32_t host_identifier_type = static_cast<cass_int32_t>(identifier_type);
2612     cass_int32_t host_ipv4_subnet_id = static_cast<cass_int32_t>(subnet_id);
2613 
2614     // Bind to array.
2615     AnyArray where_values;
2616     where_values.add(&host_ipv4_subnet_id);
2617     where_values.add(&host_identifier);
2618     where_values.add(&host_identifier_type);
2619 
2620     // Run statement.
2621     ConstHostPtr result = getHost(CqlHostExchange::GET_HOST_BY_IPV4_SUBNET_ID_AND_HOST_ID,
2622                                   where_values);
2623 
2624     return (result);
2625 }
2626 
2627 ConstHostPtr
get6(const asiolink::IOAddress & prefix,const uint8_t prefix_len) const2628 CqlHostDataSourceImpl::get6(const asiolink::IOAddress& prefix,
2629                             const uint8_t prefix_len) const {
2630     // Convert to CQL data types.
2631     std::string reserved_ipv6_prefix_address = prefix.toText();
2632     cass_int32_t reserved_ipv6_prefix_length = prefix_len;
2633 
2634     ConstHostPtr host;
2635     // Bind to array.
2636     AnyArray where_values;
2637     where_values.add(&reserved_ipv6_prefix_address);
2638     where_values.add(&reserved_ipv6_prefix_length);
2639 
2640     // Get host id.
2641     host = getHost(CqlHostExchange::GET_HOST_BY_IPV6_PREFIX, where_values);
2642 
2643     if (!host) {
2644         return ConstHostPtr();
2645     }
2646 
2647     // Get host.
2648     HostIdentifier host_identifier = host->getIdentifier();
2649     // Delegate to getAll(3).
2650     ConstHostCollection collection = getAll(host->getIdentifierType(), host_identifier.data(),
2651                                             host_identifier.size());
2652 
2653     if (collection.empty()) {
2654         return (ConstHostPtr());
2655     }
2656 
2657     if (collection.size() >= 2u) {
2658         isc_throw(MultipleRecords,
2659                   "CqlHostDataSource::get6(2): multiple records were "
2660                   "found in the "
2661                   "database where only one was expected for statement "
2662                       << CqlHostExchange::GET_HOST_BY_IPV6_PREFIX);
2663     }
2664 
2665     ConstHostPtr result = *collection.begin();
2666 
2667     return (result);
2668 }
2669 
2670 ConstHostPtr
get6(const SubnetID & subnet_id,const Host::IdentifierType & identifier_type,const uint8_t * identifier_begin,const size_t identifier_len) const2671 CqlHostDataSourceImpl::get6(const SubnetID& subnet_id,
2672                             const Host::IdentifierType& identifier_type,
2673                             const uint8_t* identifier_begin,
2674                             const size_t identifier_len) const {
2675     // Convert to CQL data types.
2676     cass_int32_t host_ipv6_subnet_id = static_cast<cass_int32_t>(subnet_id);
2677     CassBlob host_identifier(identifier_begin, identifier_begin + identifier_len);
2678     cass_int32_t host_identifier_type = static_cast<cass_int32_t>(identifier_type);
2679 
2680     // Bind to array.
2681     AnyArray where_values;
2682     where_values.add(&host_ipv6_subnet_id);
2683     where_values.add(&host_identifier);
2684     where_values.add(&host_identifier_type);
2685 
2686     // Run statement.
2687     ConstHostPtr result = getHost(CqlHostExchange::GET_HOST_BY_IPV6_SUBNET_ID_AND_HOST_ID,
2688                                   where_values);
2689 
2690     return (result);
2691 }
2692 
2693 ConstHostPtr
get6(const SubnetID & subnet_id,const IOAddress & address) const2694 CqlHostDataSourceImpl::get6(const SubnetID& subnet_id, const IOAddress& address) const {
2695     // Convert to CQL data types.
2696     cass_int32_t host_ipv6_subnet_id = static_cast<cass_int32_t>(subnet_id);
2697     std::string reserved_ipv6_prefix_address = address.toText();
2698 
2699     // Bind to array.
2700     AnyArray where_values;
2701     where_values.add(&host_ipv6_subnet_id);
2702     where_values.add(&reserved_ipv6_prefix_address);
2703 
2704     // Run statement.
2705     ConstHostPtr result = getHost(CqlHostExchange::GET_HOST_BY_IPV6_SUBNET_ID_AND_ADDRESS,
2706                                   where_values);
2707 
2708     return (result);
2709 }
2710 
2711 ConstHostCollection
getAll(const Host::IdentifierType & identifier_type,const uint8_t * identifier_begin,const size_t identifier_len) const2712 CqlHostDataSourceImpl::getAll(const Host::IdentifierType& identifier_type,
2713                               const uint8_t* identifier_begin,
2714                               const size_t identifier_len) const {
2715     // Convert to CQL data types.
2716     CassBlob host_identifier(identifier_begin, identifier_begin + identifier_len);
2717     cass_int32_t host_identifier_type = static_cast<cass_int32_t>(identifier_type);
2718 
2719     // Bind to array.
2720     AnyArray where_values;
2721     where_values.add(&host_identifier);
2722     where_values.add(&host_identifier_type);
2723 
2724     // Run statement.
2725     ConstHostCollection result = getHostCollection(CqlHostExchange::GET_HOST_BY_HOST_ID,
2726                                                    where_values);
2727 
2728     return (result);
2729 }
2730 
2731 ConstHostCollection
getAll4(const SubnetID & subnet_id) const2732 CqlHostDataSourceImpl::getAll4(const SubnetID& subnet_id) const {
2733     // Convert to CQL data types.
2734     cass_int32_t host_ipv4_subnet_id = static_cast<cass_int32_t>(subnet_id);
2735 
2736     // Bind to array.
2737     AnyArray where_values;
2738     where_values.add(&host_ipv4_subnet_id);
2739 
2740     // Run statement.
2741     ConstHostCollection result =
2742         getHostCollection(CqlHostExchange::GET_HOST_BY_IPV4_SUBNET_ID,
2743                           where_values);
2744 
2745     return (result);
2746 }
2747 
2748 ConstHostCollection
getAll6(const SubnetID & subnet_id) const2749 CqlHostDataSourceImpl::getAll6(const SubnetID& subnet_id) const {
2750     // Convert to CQL data types.
2751     cass_int32_t host_ipv6_subnet_id = static_cast<cass_int32_t>(subnet_id);
2752 
2753     // Bind to array.
2754     AnyArray where_values;
2755     where_values.add(&host_ipv6_subnet_id);
2756 
2757     // Run statement.
2758     ConstHostCollection result =
2759         getHostCollection(CqlHostExchange::GET_HOST_BY_IPV6_SUBNET_ID,
2760                           where_values);
2761 
2762     return (result);
2763 }
2764 
2765 ConstHostCollection
getAllbyHostname(const std::string & hostname) const2766 CqlHostDataSourceImpl::getAllbyHostname(const std::string& hostname) const {
2767     // Convert to CQL data types.
2768     std::string hostname_ = hostname;
2769 
2770     // Bind to array.
2771     AnyArray where_values;
2772     where_values.add(&hostname_);
2773 
2774     // Run statement.
2775     ConstHostCollection result =
2776         getHostCollection(CqlHostExchange::GET_HOST_BY_HOST_NAME,
2777                           where_values);
2778 
2779     return (result);
2780 }
2781 
2782 ConstHostCollection
getAllbyHostname4(const std::string & hostname,const SubnetID & subnet_id) const2783 CqlHostDataSourceImpl::getAllbyHostname4(const std::string& hostname,
2784                                          const SubnetID& subnet_id) const {
2785     // Convert to CQL data types.
2786     std::string hostname_ = hostname;
2787     cass_int32_t host_ipv4_subnet_id = static_cast<cass_int32_t>(subnet_id);
2788 
2789     // Bind to array.
2790     AnyArray where_values;
2791     where_values.add(&hostname_);
2792     where_values.add(&host_ipv4_subnet_id);
2793 
2794     // Run statement.
2795     ConstHostCollection result =
2796         getHostCollection(CqlHostExchange::GET_HOST_BY_HOST_NAME_AND_IPV4_SUBNET_ID,
2797                           where_values);
2798 
2799     return (result);
2800 }
2801 
2802 ConstHostCollection
getAllbyHostname6(const std::string & hostname,const SubnetID & subnet_id) const2803 CqlHostDataSourceImpl::getAllbyHostname6(const std::string& hostname,
2804                                          const SubnetID& subnet_id) const {
2805     // Convert to CQL data types.
2806     std::string hostname_ = hostname;
2807     cass_int32_t host_ipv6_subnet_id = static_cast<cass_int32_t>(subnet_id);
2808 
2809     // Bind to array.
2810     AnyArray where_values;
2811     where_values.add(&hostname_);
2812     where_values.add(&host_ipv6_subnet_id);
2813 
2814     // Run statement.
2815     ConstHostCollection result =
2816         getHostCollection(CqlHostExchange::GET_HOST_BY_HOST_NAME_AND_IPV6_SUBNET_ID,
2817                           where_values);
2818 
2819     return (result);
2820 }
2821 
2822 // There are some problems implementing this for Cassandra.
2823 // Attempts show the per page ordering does not work and
2824 // it is not possible to order by TOKEN(host_id).
2825 // If the issue solved by paging is the Kea API overhead then
2826 // a solution is to get and cache all reservations and to handle
2827 // paging at the API level.
2828 
2829 ConstHostCollection
getPage4(const SubnetID & subnet_id,uint64_t lower_host_id,const HostPageSize & page_size) const2830 CqlHostDataSourceImpl::getPage4(const SubnetID& subnet_id,
2831                                 uint64_t lower_host_id,
2832                                 const HostPageSize& page_size) const {
2833     // Run statement.
2834     ConstHostCollection result =
2835         getHostCollectionPage4(subnet_id, lower_host_id, page_size.page_size_);
2836 
2837     return (result);
2838 }
2839 
2840 ConstHostCollection
getPage6(const SubnetID & subnet_id,uint64_t lower_host_id,const HostPageSize & page_size) const2841 CqlHostDataSourceImpl::getPage6(const SubnetID& subnet_id,
2842                                 uint64_t lower_host_id,
2843                                 const HostPageSize& page_size) const {
2844     // Run statement.
2845     ConstHostCollection result =
2846         getHostCollectionPage6(subnet_id, lower_host_id, page_size.page_size_);
2847 
2848     return (result);
2849 }
2850 
2851 ConstHostCollection
getPage4(uint64_t lower_host_id,const HostPageSize & page_size) const2852 CqlHostDataSourceImpl::getPage4(uint64_t lower_host_id,
2853                                 const HostPageSize& page_size) const {
2854     // Run statement.
2855     ConstHostCollection result =
2856         getHostCollectionPage4(lower_host_id, page_size.page_size_);
2857 
2858     return (result);
2859 }
2860 
2861 ConstHostCollection
getPage6(uint64_t lower_host_id,const HostPageSize & page_size) const2862 CqlHostDataSourceImpl::getPage6(uint64_t lower_host_id,
2863                                 const HostPageSize& page_size) const {
2864     // Run statement.
2865     ConstHostCollection result =
2866         getHostCollectionPage6(lower_host_id, page_size.page_size_);
2867 
2868     return (result);
2869 }
2870 
2871 ConstHostCollection
getAll4(const asiolink::IOAddress & address) const2872 CqlHostDataSourceImpl::getAll4(const asiolink::IOAddress& address) const {
2873     // Convert to CQL data types.
2874     cass_int32_t host_ipv4_address = static_cast<cass_int32_t>(address.toUint32());
2875 
2876     // Bind to array.
2877     AnyArray where_values;
2878     where_values.add(&host_ipv4_address);
2879 
2880     // Run statement.
2881     ConstHostCollection result = getHostCollection(CqlHostExchange::GET_HOST_BY_IPV4_ADDRESS,
2882                                                    where_values);
2883 
2884     return (result);
2885 }
2886 
2887 ConstHostCollection
getAllHosts() const2888 CqlHostDataSourceImpl::getAllHosts() const {
2889 
2890     // Bind to array.
2891     AnyArray where_values;
2892 
2893     // Run statement.
2894     ConstHostCollection result = getHostCollection(CqlHostExchange::GET_HOST, where_values);
2895 
2896     return (result);
2897 }
2898 
2899 std::string
getName() const2900 CqlHostDataSourceImpl::getName() const {
2901     std::string name;
2902     try {
2903         name = dbconn_.getParameter("name");
2904     } catch (...) {
2905         // Return an empty name.
2906     }
2907     return (name);
2908 }
2909 
2910 VersionPair
getVersion() const2911 CqlHostDataSourceImpl::getVersion() const {
2912     return CqlConnection::getVersion(parameters_);
2913 }
2914 
2915 bool
insertOrDeleteHostWithOptions(bool insert,const HostPtr & host,const IPv6Resrv * const reservation,const std::list<std::string> & option_spaces,const ConstCfgOptionPtr cfg_option)2916 CqlHostDataSourceImpl::insertOrDeleteHostWithOptions(bool insert,
2917                                                      const HostPtr& host,
2918                                                      const IPv6Resrv* const reservation,
2919                                                      const std::list<std::string>& option_spaces,
2920                                                      const ConstCfgOptionPtr cfg_option) {
2921     // If there is no host, there is nothing to do.
2922     if (!host) {
2923         return (false);
2924     }
2925 
2926     bool result = true;
2927 
2928     // For each option space retrieve all options and insert them into
2929     // the database.
2930     bool option_found = false;
2931     for (const std::string& space : option_spaces) {
2932         if (!result) {
2933             break;
2934         }
2935         OptionContainerPtr options = cfg_option->getAll(space);
2936         if (options && !options->empty()) {
2937             for (const OptionDescriptor& option : *options) {
2938                 if (!result) {
2939                     break;
2940                 }
2941                 option_found = true;
2942                 /// @todo: Assign actual value to subnet id.
2943                 result = insertOrDeleteHost(insert, host, Optional<SubnetID>(), reservation,
2944                                             space, option);
2945             }
2946         }
2947     }
2948     if (result && !option_found) {
2949         // @todo: Assign actual value to subnet id.
2950         result = insertOrDeleteHost(insert, host, Optional<SubnetID>(), reservation);
2951     }
2952 
2953     return (result);
2954 }
2955 
2956 bool
insertOrDeleteHostWithReservations(bool insert,const HostPtr & host,const IPv6Resrv * const reservation,const std::list<std::string> & option_spaces4,const ConstCfgOptionPtr cfg_option4,const std::list<std::string> & option_spaces6,const ConstCfgOptionPtr cfg_option6)2957 CqlHostDataSourceImpl::insertOrDeleteHostWithReservations(bool insert,
2958                                                           const HostPtr& host,
2959                                                           const IPv6Resrv* const reservation,
2960                                                           const std::list<std::string>& option_spaces4,
2961                                                           const ConstCfgOptionPtr cfg_option4,
2962                                                           const std::list<std::string>& option_spaces6,
2963                                                           const ConstCfgOptionPtr cfg_option6) {
2964     // If there is no host, there is nothing to do.
2965     if (!host) {
2966         return (false);
2967     }
2968 
2969     bool result = true;
2970 
2971     // If host has no reservation, add entries with null reservation.
2972     // Options could still be present.
2973     if (result && cfg_option4 && !cfg_option4->empty()) {
2974         result = insertOrDeleteHostWithOptions(insert, host, reservation, option_spaces4, cfg_option4);
2975     }
2976     if (result && cfg_option6 && !cfg_option6->empty()) {
2977         result = insertOrDeleteHostWithOptions(insert, host, reservation, option_spaces6, cfg_option6);
2978     }
2979     if (result &&
2980         (!cfg_option4 || cfg_option4->empty()) &&
2981         (!cfg_option6 || cfg_option6->empty())) {
2982         result = insertOrDeleteHostWithOptions(insert, host, reservation);
2983     }
2984 
2985     return (result);
2986 }
2987 
2988 ConstHostPtr
getHost(StatementTag statement_tag,AnyArray & where_values) const2989 CqlHostDataSourceImpl::getHost(StatementTag statement_tag,
2990                                AnyArray& where_values) const {
2991     ConstHostCollection collection = getHostCollection(statement_tag, where_values);
2992 
2993     if (collection.empty()) {
2994         return (ConstHostPtr());
2995     }
2996 
2997     if (collection.size() >= 2u) {
2998         isc_throw(MultipleRecords, "CqlHostDataSourceImpl::getHost(): multiple records were "
2999                   "found in the database where only one was expected for statement "
3000                   << statement_tag);
3001     }
3002 
3003     return (*collection.begin());
3004 }
3005 
3006 ConstHostCollection
getHostCollection(StatementTag statement_tag,AnyArray & where_values) const3007 CqlHostDataSourceImpl::getHostCollection(StatementTag statement_tag,
3008                                          AnyArray& where_values) const {
3009     // Run statement.
3010     std::unique_ptr<CqlHostExchange> host_exchange(new CqlHostExchange());
3011     AnyArray collection = host_exchange->executeSelect(dbconn_, where_values,
3012                                                        statement_tag, false);
3013 
3014     // Create HostPtr objects.
3015     HostCollection host_collection;
3016     for (boost::any& host : collection) {
3017         host_collection.push_back(HostPtr(boost::any_cast<Host*>(host)));
3018     }
3019 
3020     // Merge the denormalized table entries that belong to the same host into a single host.
3021     HostMap map;
3022     for (HostPtr& host : host_collection) {
3023         HostKey key = HostKey(host->getIdentifier(), host->getIdentifierType(),
3024                               host->getIPv4SubnetID(), host->getIPv6SubnetID(),
3025                               host->getIPv4Reservation());
3026         if (map.find(key) == map.end()) {
3027             map[key] = host;
3028         } else {
3029             mergeHosts(host, map[key]);
3030         }
3031     }
3032 
3033     ConstHostCollection result_collection;
3034 
3035     for (HostPtr& host : host_collection) {
3036         HostKey key = HostKey(host->getIdentifier(), host->getIdentifierType(),
3037                               host->getIPv4SubnetID(), host->getIPv6SubnetID(),
3038                               host->getIPv4Reservation());
3039         if (map.find(key) != map.end()) {
3040             result_collection.push_back(map[key]);
3041             map.erase(key);
3042         }
3043     }
3044     return (result_collection);
3045 }
3046 
3047 ConstHostPtr
getHostByKey(uint64_t key) const3048 CqlHostDataSourceImpl::getHostByKey(uint64_t key) const {
3049     // Bind to array.
3050     AnyArray where_values;
3051     cass_int64_t key_data = static_cast<cass_int64_t>(key);
3052     where_values.add(&key_data);
3053 
3054     // Run statement.
3055     ConstHostCollection collection =
3056             getHostCollection(CqlHostExchange::GET_HOST_KEY,
3057                               where_values);
3058 
3059     if (collection.empty()) {
3060         return (ConstHostPtr());
3061     }
3062 
3063     if (collection.size() >= 2u) {
3064         isc_throw(MultipleRecords, "CqlHostDataSourceImpl::getHost(): multiple records were "
3065                   "found in the database where only one was expected for statement "
3066                   << CqlHostExchange::GET_HOST_KEY);
3067     }
3068 
3069     return (*collection.begin());
3070 }
3071 
3072 ConstHostCollection
getHostCollectionPage4(const SubnetID & subnet_id,uint64_t lower_host_id,size_t count) const3073 CqlHostDataSourceImpl::getHostCollectionPage4(const SubnetID& subnet_id,
3074                                               uint64_t lower_host_id,
3075                                               size_t count) const {
3076     ConstHostCollection result_collection;
3077     for (; count; count--) {
3078         uint64_t key;
3079         bool valid_key = getHostKey4(subnet_id, lower_host_id, key);
3080 
3081         if (!valid_key) {
3082             break;
3083         }
3084 
3085         ConstHostPtr host = getHostByKey(key);
3086         result_collection.push_back(host);
3087         lower_host_id = host->getHostId();
3088     }
3089 
3090     return (result_collection);
3091 }
3092 
3093 ConstHostCollection
getHostCollectionPage6(const SubnetID & subnet_id,uint64_t lower_host_id,size_t count) const3094 CqlHostDataSourceImpl::getHostCollectionPage6(const SubnetID& subnet_id,
3095                                               uint64_t lower_host_id,
3096                                               size_t count) const {
3097     ConstHostCollection result_collection;
3098     for (; count; count--) {
3099         uint64_t key;
3100         bool valid_key = getHostKey6(subnet_id, lower_host_id, key);
3101 
3102         if (!valid_key) {
3103             break;
3104         }
3105 
3106         ConstHostPtr host = getHostByKey(key);
3107         result_collection.push_back(host);
3108         lower_host_id = host->getHostId();
3109     }
3110 
3111     return (result_collection);
3112 }
3113 
3114 ConstHostCollection
getHostCollectionPage4(uint64_t lower_host_id,size_t count) const3115 CqlHostDataSourceImpl::getHostCollectionPage4(uint64_t lower_host_id,
3116                                               size_t count) const {
3117     ConstHostCollection result_collection;
3118     for (; count; count--) {
3119         uint64_t key;
3120         bool valid_key = getHostKey(lower_host_id, key);
3121 
3122         if (!valid_key) {
3123             break;
3124         }
3125 
3126         ConstHostPtr host = getHostByKey(key);
3127         result_collection.push_back(host);
3128         lower_host_id = host->getHostId();
3129     }
3130 
3131     return (result_collection);
3132 }
3133 
3134 ConstHostCollection
getHostCollectionPage6(uint64_t lower_host_id,size_t count) const3135 CqlHostDataSourceImpl::getHostCollectionPage6(uint64_t lower_host_id,
3136                                               size_t count) const {
3137     ConstHostCollection result_collection;
3138     for (; count; count--) {
3139         uint64_t key;
3140         bool valid_key = getHostKey(lower_host_id, key);
3141 
3142         if (!valid_key) {
3143             break;
3144         }
3145 
3146         ConstHostPtr host = getHostByKey(key);
3147         result_collection.push_back(host);
3148         lower_host_id = host->getHostId();
3149     }
3150 
3151     return (result_collection);
3152 }
3153 
3154 bool
getHostKey4(const SubnetID & subnet_id,uint64_t lower_host_id,uint64_t & key) const3155 CqlHostDataSourceImpl::getHostKey4(const SubnetID& subnet_id,
3156                                    uint64_t lower_host_id,
3157                                    uint64_t& key) const {
3158     // Convert to CQL data types.
3159     cass_int32_t host_subnet_id = static_cast<cass_int32_t>(subnet_id);
3160 
3161     // Bind to array.
3162     AnyArray where_values;
3163     where_values.add(&host_subnet_id);
3164 
3165     cass_int64_t host_data = 0;
3166     if (lower_host_id) {
3167         host_data = static_cast<cass_int64_t>(lower_host_id);
3168         where_values.add(&host_data);
3169     }
3170 
3171     // Run statement.
3172     // This will retrieve first row of the first host (lower_host_id == 0)
3173     // or the first row of the host (lower_host_id != 0)
3174     std::unique_ptr<CqlHostExchange> host_exchange(new CqlHostExchange());
3175     AnyArray collection;
3176     if (lower_host_id) {
3177         collection = host_exchange->executeSelect(dbconn_, where_values,
3178                 CqlHostExchange::GET_HOST_BY_IPV4_SUBNET_ID_PAGE, false);
3179     } else {
3180         collection = host_exchange->executeSelect(dbconn_, where_values,
3181                 CqlHostExchange::GET_HOST_BY_IPV4_SUBNET_ID_LIMIT, false);
3182     }
3183 
3184     // Create HostPtr objects.
3185     HostCollection host_collection;
3186     for (boost::any& host : collection) {
3187         host_collection.push_back(HostPtr(boost::any_cast<Host*>(host)));
3188     }
3189 
3190     // If there is no host, just exit
3191     if (host_collection.empty()) {
3192         return false;
3193     }
3194 
3195     key = host_exchange->hashIntoKey();
3196 
3197     if (lower_host_id) {
3198         return getNextHostKey4(subnet_id, key);
3199     }
3200 
3201     return true;
3202 }
3203 
3204 bool
getHostKey6(const SubnetID & subnet_id,uint64_t lower_host_id,uint64_t & key) const3205 CqlHostDataSourceImpl::getHostKey6(const SubnetID& subnet_id,
3206                                    uint64_t lower_host_id,
3207                                    uint64_t& key) const {
3208     // Convert to CQL data types.
3209     cass_int32_t host_subnet_id = static_cast<cass_int32_t>(subnet_id);
3210 
3211     // Bind to array.
3212     AnyArray where_values;
3213     where_values.add(&host_subnet_id);
3214 
3215     cass_int64_t host_data = 0;
3216     if (lower_host_id) {
3217         host_data = static_cast<cass_int64_t>(lower_host_id);
3218         where_values.add(&host_data);
3219     }
3220 
3221     // Run statement.
3222     // This will retrieve first row of the first host (lower_host_id == 0)
3223     // or the first row of the host (lower_host_id != 0)
3224     std::unique_ptr<CqlHostExchange> host_exchange(new CqlHostExchange());
3225     AnyArray collection;
3226     if (lower_host_id) {
3227         collection = host_exchange->executeSelect(dbconn_, where_values,
3228                 CqlHostExchange::GET_HOST_BY_IPV6_SUBNET_ID_PAGE, false);
3229     } else {
3230         collection = host_exchange->executeSelect(dbconn_, where_values,
3231                 CqlHostExchange::GET_HOST_BY_IPV6_SUBNET_ID_LIMIT, false);
3232     }
3233 
3234     // Create HostPtr objects.
3235     HostCollection host_collection;
3236     for (boost::any& host : collection) {
3237         host_collection.push_back(HostPtr(boost::any_cast<Host*>(host)));
3238     }
3239 
3240     // If there is no host, just exit
3241     if (host_collection.empty()) {
3242         return false;
3243     }
3244 
3245     key = host_exchange->hashIntoKey();
3246 
3247     if (lower_host_id) {
3248         return getNextHostKey6(subnet_id, key);
3249     }
3250 
3251     return true;
3252 }
3253 
3254 bool
getHostKey(uint64_t lower_host_id,uint64_t & key) const3255 CqlHostDataSourceImpl::getHostKey(uint64_t lower_host_id,
3256                                   uint64_t& key) const {
3257     // Bind to array.
3258     AnyArray where_values;
3259     cass_int64_t host_data = 0;
3260     if (lower_host_id) {
3261         host_data = static_cast<cass_int64_t>(lower_host_id);
3262         where_values.add(&host_data);
3263     }
3264 
3265     // Run statement.
3266     // This will retrieve first row of the first host (lower_host_id == 0)
3267     // or the first row of the host (lower_host_id != 0)
3268     std::unique_ptr<CqlHostExchange> host_exchange(new CqlHostExchange());
3269     AnyArray collection;
3270     if (lower_host_id) {
3271         collection = host_exchange->executeSelect(dbconn_, where_values,
3272                 CqlHostExchange::GET_HOST_PAGE, false);
3273     } else {
3274         collection = host_exchange->executeSelect(dbconn_, where_values,
3275                 CqlHostExchange::GET_HOST_LIMIT, false);
3276     }
3277 
3278     // Create HostPtr objects.
3279     HostCollection host_collection;
3280     for (boost::any& host : collection) {
3281         host_collection.push_back(HostPtr(boost::any_cast<Host*>(host)));
3282     }
3283 
3284     // If there is no host, just exit
3285     if (host_collection.empty()) {
3286         return false;
3287     }
3288 
3289     key = host_exchange->hashIntoKey();
3290 
3291     if (lower_host_id) {
3292         return getNextHostKey(key);
3293     }
3294 
3295     return true;
3296 }
3297 
3298 bool
getNextHostKey4(const SubnetID & subnet_id,uint64_t & key) const3299 CqlHostDataSourceImpl::getNextHostKey4(const SubnetID& subnet_id,
3300                                        uint64_t& key) const {
3301     // Convert to CQL data types.
3302     cass_int32_t host_subnet_id = static_cast<cass_int32_t>(subnet_id);
3303     cass_int64_t key_data = static_cast<cass_int64_t>(key);
3304 
3305     // Bind to array.
3306     AnyArray where_values;
3307     where_values.add(&host_subnet_id);
3308     where_values.add(&key_data);
3309 
3310     // This will retrieve first row of the next host (lower_host_id != 0)
3311     std::unique_ptr<CqlHostExchange> host_exchange(new CqlHostExchange());
3312     AnyArray collection = host_exchange->executeSelect(dbconn_, where_values,
3313             CqlHostExchange::GET_HOST_BY_IPV4_SUBNET_ID_NEXT_KEY, false);
3314 
3315     // Create HostPtr objects.
3316     HostCollection host_collection;
3317     for (boost::any& host : collection) {
3318         host_collection.push_back(HostPtr(boost::any_cast<Host*>(host)));
3319     }
3320 
3321     if (host_collection.empty()) {
3322         return false;
3323     }
3324 
3325     key = host_exchange->hashIntoKey();
3326     return true;
3327 }
3328 
3329 bool
getNextHostKey6(const SubnetID & subnet_id,uint64_t & key) const3330 CqlHostDataSourceImpl::getNextHostKey6(const SubnetID& subnet_id,
3331                                        uint64_t& key) const {
3332     // Convert to CQL data types.
3333     cass_int32_t host_subnet_id = static_cast<cass_int32_t>(subnet_id);
3334     cass_int64_t key_data = static_cast<cass_int64_t>(key);
3335 
3336     // Bind to array.
3337     AnyArray where_values;
3338     where_values.add(&host_subnet_id);
3339     where_values.add(&key_data);
3340 
3341     // This will retrieve first row of the next host (lower_host_id != 0)
3342     std::unique_ptr<CqlHostExchange> host_exchange(new CqlHostExchange());
3343     AnyArray collection = host_exchange->executeSelect(dbconn_, where_values,
3344             CqlHostExchange::GET_HOST_BY_IPV6_SUBNET_ID_NEXT_KEY, false);
3345 
3346     // Create HostPtr objects.
3347     HostCollection host_collection;
3348     for (boost::any& host : collection) {
3349         host_collection.push_back(HostPtr(boost::any_cast<Host*>(host)));
3350     }
3351 
3352     if (host_collection.empty()) {
3353         return false;
3354     }
3355 
3356     key = host_exchange->hashIntoKey();
3357     return true;
3358 }
3359 
3360 bool
getNextHostKey(uint64_t & key) const3361 CqlHostDataSourceImpl::getNextHostKey(uint64_t& key) const {
3362     // Convert to CQL data types.
3363     cass_int64_t key_data = static_cast<cass_int64_t>(key);
3364 
3365     // Bind to array.
3366     AnyArray where_values;
3367     where_values.add(&key_data);
3368 
3369     // This will retrieve first row of the next host (lower_host_id != 0)
3370     std::unique_ptr<CqlHostExchange> host_exchange(new CqlHostExchange());
3371     AnyArray collection = host_exchange->executeSelect(dbconn_, where_values,
3372             CqlHostExchange::GET_HOST_NEXT_KEY, false);
3373 
3374     // Create HostPtr objects.
3375     HostCollection host_collection;
3376     for (boost::any& host : collection) {
3377         host_collection.push_back(HostPtr(boost::any_cast<Host*>(host)));
3378     }
3379 
3380     if (host_collection.empty()) {
3381         return false;
3382     }
3383 
3384     key = host_exchange->hashIntoKey();
3385     return true;
3386 }
3387 
3388 bool
insertOrDeleteHost(bool insert,const HostPtr & host,const Optional<SubnetID> & subnet_id,const IPv6Resrv * const reservation,const std::string & option_space,const OptionDescriptor & option_descriptor)3389 CqlHostDataSourceImpl::insertOrDeleteHost(bool insert,
3390                                           const HostPtr& host,
3391                                           const Optional<SubnetID>& subnet_id,
3392                                           const IPv6Resrv* const reservation,
3393                                           const std::string& option_space,
3394                                           const OptionDescriptor& option_descriptor) {
3395     // If there is no host, there is nothing to do.
3396     if (!host) {
3397         return (false);
3398     }
3399 
3400     AnyArray assigned_values;
3401 
3402     std::unique_ptr<CqlHostExchange> host_exchange(new CqlHostExchange());
3403 
3404     try {
3405         if (insert) {
3406             host_exchange->createBindForMutation(host, subnet_id, reservation, option_space,
3407                 option_descriptor, CqlHostExchange::INSERT_HOST, assigned_values);
3408 
3409             host_exchange->executeMutation(dbconn_, assigned_values, CqlHostExchange::INSERT_HOST);
3410         } else {
3411             host_exchange->createBindForDelete(host, subnet_id, reservation, option_space,
3412                 option_descriptor, CqlHostExchange::DELETE_HOST, assigned_values);
3413 
3414             host_exchange->executeMutation(dbconn_, assigned_values, CqlHostExchange::DELETE_HOST);
3415         }
3416     } catch (const StatementNotApplied& exception) {
3417         if (insert) {
3418             isc_throw(DuplicateEntry, exception.what());
3419         } else {
3420             return (false);
3421         }
3422     }
3423 
3424     return (true);
3425 }
3426 
3427 void
mergeHosts(const ConstHostPtr & source_host,HostPtr & target_host) const3428 CqlHostDataSourceImpl::mergeHosts(const ConstHostPtr& source_host,
3429                                   HostPtr& target_host) const {
3430     // Merge reservations.
3431     const IPv6ResrvRange reservations_range =
3432         source_host->getIPv6Reservations();
3433     if (std::distance(reservations_range.first, reservations_range.second) > 0) {
3434         for (IPv6ResrvIterator reservations_iterator = reservations_range.first;
3435              reservations_iterator != reservations_range.second;
3436              ++reservations_iterator) {
3437             if (!target_host->hasReservation(reservations_iterator->second)) {
3438                 target_host->addReservation(reservations_iterator->second);
3439             }
3440         }
3441     }
3442 
3443     // Merge DHCPv4 options.
3444     source_host->getCfgOption4()->mergeTo(*target_host->getCfgOption4());
3445 
3446     // Merge DHCPv6 options.
3447     source_host->getCfgOption6()->mergeTo(*target_host->getCfgOption6());
3448 }
3449 
CqlHostDataSource(const DatabaseConnection::ParameterMap & parameters)3450 CqlHostDataSource::CqlHostDataSource(const DatabaseConnection::ParameterMap& parameters)
3451     : impl_(new CqlHostDataSourceImpl(parameters)) {
3452     // Cassandra support is now deprecated.
3453     LOG_WARN(dhcpsrv_logger, DHCPSRV_DEPRECATED).arg("Cassandra host backend");
3454 }
3455 
~CqlHostDataSource()3456 CqlHostDataSource::~CqlHostDataSource() {
3457     delete impl_;
3458 }
3459 
3460 void
add(const HostPtr & host)3461 CqlHostDataSource::add(const HostPtr& host) {
3462     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_ADD);
3463 
3464     impl_->insertOrDelete(host, true);
3465 }
3466 
3467 bool
del(const SubnetID & subnet_id,const asiolink::IOAddress & address)3468 CqlHostDataSource::del(const SubnetID& subnet_id, const asiolink::IOAddress& address) {
3469     HostPtr host = boost::const_pointer_cast<Host>(impl_->get4(subnet_id, address));
3470 
3471     return (host ? impl_->insertOrDelete(host, false) : false);
3472 }
3473 
3474 bool
del4(const SubnetID & subnet_id,const Host::IdentifierType & identifier_type,const uint8_t * identifier_begin,const size_t identifier_len)3475 CqlHostDataSource::del4(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type,
3476                         const uint8_t* identifier_begin, const size_t identifier_len) {
3477     HostPtr host = boost::const_pointer_cast<Host>(impl_->get4(subnet_id, identifier_type,
3478                                                                identifier_begin, identifier_len));
3479 
3480     return (host ? impl_->insertOrDelete(host, false) : false);
3481 }
3482 
3483 bool
del6(const SubnetID & subnet_id,const Host::IdentifierType & identifier_type,const uint8_t * identifier_begin,const size_t identifier_len)3484 CqlHostDataSource::del6(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type,
3485                         const uint8_t* identifier_begin, const size_t identifier_len) {
3486     HostPtr host = boost::const_pointer_cast<Host>(impl_->get6(subnet_id, identifier_type,
3487                                                                identifier_begin, identifier_len));
3488 
3489     return (host ? impl_->insertOrDelete(host, false) : false);
3490 }
3491 
3492 ConstHostCollection
getAll(const Host::IdentifierType & identifier_type,const uint8_t * identifier_begin,const size_t identifier_len) const3493 CqlHostDataSource::getAll(const Host::IdentifierType& identifier_type,
3494                           const uint8_t* identifier_begin,
3495                           const size_t identifier_len) const {
3496     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_GET_ALL);
3497 
3498     return (impl_->getAll(identifier_type, identifier_begin, identifier_len));
3499 }
3500 
3501 ConstHostCollection
getAll4(const SubnetID & subnet_id) const3502 CqlHostDataSource::getAll4(const SubnetID& subnet_id) const {
3503     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_GET_ALL);
3504 
3505     return (impl_->getAll4(subnet_id));
3506 }
3507 
3508 ConstHostCollection
getAll6(const SubnetID & subnet_id) const3509 CqlHostDataSource::getAll6(const SubnetID& subnet_id) const {
3510     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_GET_ALL);
3511 
3512     return (impl_->getAll6(subnet_id));
3513 }
3514 
3515 ConstHostCollection
getAllbyHostname(const std::string & hostname) const3516 CqlHostDataSource::getAllbyHostname(const std::string& hostname) const {
3517     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_GET_ALL);
3518 
3519     return (impl_->getAllbyHostname(hostname));
3520 }
3521 
3522 ConstHostCollection
getAllbyHostname4(const std::string & hostname,const SubnetID & subnet_id) const3523 CqlHostDataSource::getAllbyHostname4(const std::string& hostname,
3524                                      const SubnetID& subnet_id) const {
3525     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_GET_ALL);
3526 
3527     return (impl_->getAllbyHostname4(hostname, subnet_id));
3528 }
3529 
3530 ConstHostCollection
getAllbyHostname6(const std::string & hostname,const SubnetID & subnet_id) const3531 CqlHostDataSource::getAllbyHostname6(const std::string& hostname,
3532                                      const SubnetID& subnet_id) const {
3533     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_GET_ALL);
3534 
3535     return (impl_->getAllbyHostname6(hostname, subnet_id));
3536 }
3537 
3538 ConstHostCollection
getPage4(const SubnetID & subnet_id,size_t &,uint64_t lower_host_id,const HostPageSize & page_size) const3539 CqlHostDataSource::getPage4(const SubnetID& subnet_id,
3540                             size_t& /*source_index*/,
3541                             uint64_t lower_host_id,
3542                             const HostPageSize& page_size) const {
3543     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_GET_ALL);
3544 
3545     return (impl_->getPage4(subnet_id, lower_host_id, page_size));
3546 }
3547 
3548 ConstHostCollection
getPage6(const SubnetID & subnet_id,size_t &,uint64_t lower_host_id,const HostPageSize & page_size) const3549 CqlHostDataSource::getPage6(const SubnetID& subnet_id,
3550                             size_t& /*source_index*/,
3551                             uint64_t lower_host_id,
3552                             const HostPageSize& page_size) const {
3553     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_GET_ALL);
3554 
3555     return (impl_->getPage6(subnet_id, lower_host_id, page_size));
3556 }
3557 
3558 ConstHostCollection
getPage4(size_t &,uint64_t lower_host_id,const HostPageSize & page_size) const3559 CqlHostDataSource::getPage4(size_t& /*source_index*/,
3560                             uint64_t lower_host_id,
3561                             const HostPageSize& page_size) const {
3562     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_GET_ALL);
3563 
3564     return (impl_->getPage4(lower_host_id, page_size));
3565 }
3566 
3567 ConstHostCollection
getPage6(size_t &,uint64_t lower_host_id,const HostPageSize & page_size) const3568 CqlHostDataSource::getPage6(size_t& /*source_index*/,
3569                             uint64_t lower_host_id,
3570                             const HostPageSize& page_size) const {
3571     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_GET_ALL);
3572 
3573     return (impl_->getPage6(lower_host_id, page_size));
3574 }
3575 
3576 ConstHostCollection
getAll4(const asiolink::IOAddress & address) const3577 CqlHostDataSource::getAll4(const asiolink::IOAddress& address) const {
3578     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_GET_ALL);
3579 
3580     return (impl_->getAll4(address));
3581 }
3582 
3583 ConstHostPtr
get4(const SubnetID & subnet_id,const Host::IdentifierType & identifier_type,const uint8_t * identifier_begin,const size_t identifier_len) const3584 CqlHostDataSource::get4(const SubnetID& subnet_id,
3585                         const Host::IdentifierType& identifier_type,
3586                         const uint8_t* identifier_begin,
3587                         const size_t identifier_len) const {
3588     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_GET4);
3589 
3590     return (impl_->get4(subnet_id, identifier_type, identifier_begin,
3591                         identifier_len));
3592 }
3593 
3594 ConstHostPtr
get4(const SubnetID & subnet_id,const asiolink::IOAddress & address) const3595 CqlHostDataSource::get4(const SubnetID& subnet_id,
3596                         const asiolink::IOAddress& address) const {
3597     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_GET4);
3598 
3599     return (impl_->get4(subnet_id, address));
3600 }
3601 
3602 ConstHostCollection
getAll4(const SubnetID & subnet_id,const asiolink::IOAddress & address) const3603 CqlHostDataSource::getAll4(const SubnetID& subnet_id,
3604                            const asiolink::IOAddress& address) const {
3605     ConstHostCollection hosts;
3606     auto host = get4(subnet_id, address);
3607     if (host) {
3608         hosts.push_back(host);
3609     }
3610     return (hosts);
3611 }
3612 
3613 ConstHostPtr
get6(const SubnetID & subnet_id,const Host::IdentifierType & identifier_type,const uint8_t * identifier_begin,const size_t identifier_len) const3614 CqlHostDataSource::get6(const SubnetID& subnet_id,
3615                         const Host::IdentifierType& identifier_type,
3616                         const uint8_t* identifier_begin,
3617                         const size_t identifier_len) const {
3618     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_GET6);
3619 
3620     return (impl_->get6(subnet_id, identifier_type, identifier_begin, identifier_len));
3621 }
3622 
3623 ConstHostPtr
get6(const asiolink::IOAddress & prefix,const uint8_t prefix_len) const3624 CqlHostDataSource::get6(const asiolink::IOAddress& prefix,
3625                         const uint8_t prefix_len) const {
3626     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_GET6);
3627 
3628     return (impl_->get6(prefix, prefix_len));
3629 }
3630 
3631 ConstHostPtr
get6(const SubnetID & subnet_id,const asiolink::IOAddress & address) const3632 CqlHostDataSource::get6(const SubnetID& subnet_id,
3633                         const asiolink::IOAddress& address) const {
3634     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_GET6);
3635 
3636     return (impl_->get6(subnet_id, address));
3637 }
3638 
3639 ConstHostCollection
getAll6(const SubnetID & subnet_id,const asiolink::IOAddress & address) const3640 CqlHostDataSource::getAll6(const SubnetID& subnet_id,
3641                            const asiolink::IOAddress& address) const {
3642     ConstHostCollection hosts;
3643     auto host = get6(subnet_id, address);
3644     if (host) {
3645         hosts.push_back(host);
3646     }
3647     return (hosts);
3648 }
3649 
3650 ConstHostCollection
getAllHosts() const3651 CqlHostDataSource::getAllHosts() const {
3652     return (impl_->getAllHosts());
3653 }
3654 
3655 std::string
getType() const3656 CqlHostDataSource::getType() const {
3657     return std::string("cql");
3658 }
3659 
3660 std::string
getName() const3661 CqlHostDataSource::getName() const {
3662     return (impl_->getName());
3663 }
3664 
3665 std::string
getDescription() const3666 CqlHostDataSource::getDescription() const {
3667     return std::string("Host data source that stores host information in the CQL database");
3668 }
3669 
3670 VersionPair
getVersion() const3671 CqlHostDataSource::getVersion() const {
3672     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_DB_GET_VERSION);
3673 
3674     return impl_->getVersion();
3675 }
3676 
3677 void
commit()3678 CqlHostDataSource::commit() {
3679     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_COMMIT);
3680 }
3681 
3682 void
rollback()3683 CqlHostDataSource::rollback() {
3684     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_ROLLBACK);
3685 }
3686 
3687 bool
setIPReservationsUnique(const bool unique)3688 CqlHostDataSource::setIPReservationsUnique(const bool unique) {
3689     // This backend does not support the mode in which multiple reservations
3690     // for the same IP address are created. If selecting this mode is
3691     // attempted this function returns false to indicate that this is
3692     // not allowed.
3693     return (unique);
3694 }
3695 
3696 }  // namespace dhcp
3697 }  // namespace isc
3698