1 // Copyright (C) 2019-2021 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 #include <mysql_cb_dhcp6.h>
9 #include <asiolink/addr_utilities.h>
10 #include <database/database_connection.h>
11 #include <database/db_exceptions.h>
12 #include <database/testutils/schema.h>
13 #include <dhcp/dhcp6.h>
14 #include <dhcp/libdhcp++.h>
15 #include <dhcp/option6_addrlst.h>
16 #include <dhcp/option_int.h>
17 #include <dhcp/option_space.h>
18 #include <dhcp/option_string.h>
19 #include <dhcpsrv/cfgmgr.h>
20 #include <dhcpsrv/config_backend_dhcp6_mgr.h>
21 #include <dhcpsrv/pool.h>
22 #include <dhcpsrv/subnet.h>
23 #include <dhcpsrv/testutils/mysql_generic_backend_unittest.h>
24 #include <dhcpsrv/testutils/test_utils.h>
25 #include <mysql/testutils/mysql_schema.h>
26 #include <testutils/multi_threading_utils.h>
27 #include <testutils/gtest_utils.h>
28 
29 #include <boost/shared_ptr.hpp>
30 #include <gtest/gtest.h>
31 #include <mysql.h>
32 #include <map>
33 #include <sstream>
34 
35 using namespace isc;
36 using namespace isc::asiolink;
37 using namespace isc::db;
38 using namespace isc::db::test;
39 using namespace isc::data;
40 using namespace isc::dhcp;
41 using namespace isc::dhcp::test;
42 using namespace isc::process;
43 using namespace isc::test;
44 namespace ph = std::placeholders;
45 
46 namespace {
47 
48 /// @brief Test implementation of the MySQL configuration backend.
49 ///
50 /// It exposes protected members of the @c MySqlConfigBackendDHCPv6.
51 class TestMySqlConfigBackendDHCPv6 : public MySqlConfigBackendDHCPv6 {
52 public:
53 
54     /// @brief Constructor.
55     ///
56     /// @param parameters A data structure relating keywords and values
57     /// concerned with the database.
TestMySqlConfigBackendDHCPv6(const DatabaseConnection::ParameterMap & parameters)58     explicit TestMySqlConfigBackendDHCPv6(const DatabaseConnection::ParameterMap& parameters)
59         : MySqlConfigBackendDHCPv6(parameters) {
60     }
61 
62     using MySqlConfigBackendDHCPv6::base_impl_;
63 
64 };
65 
66 /// @brief Test fixture class for @c MySqlConfigBackendDHCPv6.
67 ///
68 /// @todo The tests we're providing here only test cases when the
69 /// server selector is set to 'ALL' (configuration elements belong to
70 /// all servers). Currently we have no API to insert servers into
71 /// the database, and therefore we can't test the case when
72 /// configuration elements are assigned to particular servers by
73 /// server tags. We will have to expand existing tests when
74 /// the API is extended allowing for inserting servers to the
75 /// database.
76 class MySqlConfigBackendDHCPv6Test : public MySqlGenericBackendTest {
77 public:
78 
79     /// @brief Constructor.
MySqlConfigBackendDHCPv6Test()80     MySqlConfigBackendDHCPv6Test()
81         : test_subnets_(), test_networks_(), test_option_defs_(),
82           test_options_(), test_client_classes_(), test_servers_(), timestamps_(),
83           cbptr_(), audit_entries_() {
84         // Ensure we have the proper schema with no transient data.
85         createMySQLSchema();
86 
87         try {
88             // Create MySQL connection and use it to start the backend.
89             DatabaseConnection::ParameterMap params =
90                 DatabaseConnection::parse(validMySQLConnectionString());
91             cbptr_.reset(new TestMySqlConfigBackendDHCPv6(params));
92 
93         } catch (...) {
94             std::cerr << "*** ERROR: unable to open database. The test\n"
95                          "*** environment is broken and must be fixed before\n"
96                          "*** the MySQL tests will run correctly.\n"
97                          "*** The reason for the problem is described in the\n"
98                          "*** accompanying exception output.\n";
99             throw;
100         }
101 
102         // Create test data.
103         initTestServers();
104         initTestOptions();
105         initTestSubnets();
106         initTestSharedNetworks();
107         initTestOptionDefs();
108         initTestClientClasses();
109         initTimestamps();
110     }
111 
112     /// @brief Destructor.
~MySqlConfigBackendDHCPv6Test()113     virtual ~MySqlConfigBackendDHCPv6Test() {
114         cbptr_.reset();
115         // If data wipe enabled, delete transient data otherwise destroy the schema.
116         destroyMySQLSchema();
117     }
118 
119     /// @brief Counts rows in a selected table in MySQL database.
120     ///
121     /// This method can be used to verify that some configuration elements were
122     /// deleted from a selected table as a result of cascade delete or a trigger.
123     /// For example, deleting a subnet should trigger deletion of its address
124     /// pools and options. By counting the rows on each table we can determine
125     /// whether the deletion took place on all tables for which it was expected.
126     ///
127     /// @param table Table name.
128     /// @return Number of rows in the specified table.
countRows(const std::string & table) const129     size_t countRows(const std::string& table) const {
130         auto p = boost::dynamic_pointer_cast<TestMySqlConfigBackendDHCPv6>(cbptr_);
131         if (!p) {
132             ADD_FAILURE() << "cbptr_ does not cast to TestMySqlConfigBackendDHCPv6";
133             return (0);
134         }
135 
136         // Reuse the existing connection of the backend.
137         auto impl = boost::dynamic_pointer_cast<MySqlConfigBackendImpl>(p->base_impl_);
138         auto& conn = impl->conn_;
139 
140         return (MySqlGenericBackendTest::countRows(conn, table));
141     }
142 
143     /// @brief Creates several servers used in tests.
initTestServers()144     void initTestServers() {
145         test_servers_.push_back(Server::create(ServerTag("server1"), "this is server 1"));
146         test_servers_.push_back(Server::create(ServerTag("server1"), "this is server 1 bis"));
147         test_servers_.push_back(Server::create(ServerTag("server2"), "this is server 2"));
148         test_servers_.push_back(Server::create(ServerTag("server3"), "this is server 3"));
149     }
150 
151     /// @brief Creates several subnets used in tests.
initTestSubnets()152     void initTestSubnets() {
153         // First subnet includes all parameters.
154         std::string interface_id_text = "whale";
155         OptionBuffer interface_id(interface_id_text.begin(), interface_id_text.end());
156         OptionPtr opt_interface_id(new Option(Option::V6, D6O_INTERFACE_ID,
157                                               interface_id));
158         ElementPtr user_context = Element::createMap();
159         user_context->set("foo", Element::create("bar"));
160 
161         Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8::"),
162                                       64, 30, 40, 50, 60, 1024));
163         subnet->allowClientClass("home");
164         subnet->setIface("eth1");
165         subnet->setInterfaceId(opt_interface_id);
166         subnet->setT2(323212);
167         subnet->addRelayAddress(IOAddress("2001:db8:1::2"));
168         subnet->addRelayAddress(IOAddress("2001:db8:3::4"));
169         subnet->setT1(1234);
170         subnet->requireClientClass("required-class1");
171         subnet->requireClientClass("required-class2");
172         subnet->setReservationsGlobal(false);
173         subnet->setReservationsInSubnet(false);
174         subnet->setContext(user_context);
175         subnet->setValid(555555);
176         subnet->setPreferred(4444444);
177         subnet->setCalculateTeeTimes(true);
178         subnet->setT1Percent(0.345);
179         subnet->setT2Percent(0.444);
180         subnet->setDdnsSendUpdates(false);
181 
182         Pool6Ptr pool1(new Pool6(Lease::TYPE_NA,
183                                  IOAddress("2001:db8::10"),
184                                  IOAddress("2001:db8::20")));
185         subnet->addPool(pool1);
186 
187         Pool6Ptr pool2(new Pool6(Lease::TYPE_NA,
188                                  IOAddress("2001:db8::50"),
189                                  IOAddress("2001:db8::60")));
190         subnet->addPool(pool2);
191 
192         Pool6Ptr pdpool1(new Pool6(Lease::TYPE_PD,
193                                    IOAddress("2001:db8:a::"), 48, 64));
194         subnet->addPool(pdpool1);
195 
196         Pool6Ptr pdpool2(new Pool6(Lease::TYPE_PD,
197                                    IOAddress("2001:db8:b::"), 48, 64));
198         subnet->addPool(pdpool2);
199 
200         // Add several options to the subnet.
201         subnet->getCfgOption()->add(test_options_[0]->option_,
202                                     test_options_[0]->persistent_,
203                                     test_options_[0]->space_name_);
204 
205         subnet->getCfgOption()->add(test_options_[1]->option_,
206                                     test_options_[1]->persistent_,
207                                     test_options_[1]->space_name_);
208 
209         subnet->getCfgOption()->add(test_options_[2]->option_,
210                                     test_options_[2]->persistent_,
211                                     test_options_[2]->space_name_);
212 
213         test_subnets_.push_back(subnet);
214 
215         // Adding another subnet with the same subnet id to test
216         // cases that this second instance can override existing
217         // subnet instance.
218         subnet.reset(new Subnet6(IOAddress("2001:db8:1::"),
219                                  48, 20, 30, 40, 50, 1024));
220 
221         pool1.reset(new Pool6(Lease::TYPE_NA,
222                               IOAddress("2001:db8:1::10"),
223                               IOAddress("2001:db8:1::20")));
224         subnet->addPool(pool1);
225 
226         pool1->getCfgOption()->add(test_options_[3]->option_,
227                                    test_options_[3]->persistent_,
228                                    test_options_[3]->space_name_);
229 
230         pool1->getCfgOption()->add(test_options_[4]->option_,
231                                    test_options_[4]->persistent_,
232                                    test_options_[4]->space_name_);
233 
234         pool2.reset(new Pool6(Lease::TYPE_NA,
235                               IOAddress("2001:db8:1::50"),
236                               IOAddress("2001:db8:1::60")));
237         subnet->addPool(pool2);
238 
239         pool2->allowClientClass("work");
240         pool2->requireClientClass("required-class3");
241         pool2->requireClientClass("required-class4");
242         user_context = Element::createMap();
243         user_context->set("bar", Element::create("foo"));
244         pool2->setContext(user_context);
245 
246         pdpool1.reset(new Pool6(IOAddress("2001:db8:c::"), 48, 64,
247                                 IOAddress("2001:db8:c::1"), 96));
248         subnet->addPool(pdpool1);
249 
250         pdpool1->getCfgOption()->add(test_options_[3]->option_,
251                                      test_options_[3]->persistent_,
252                                      test_options_[3]->space_name_);
253 
254         pdpool1->getCfgOption()->add(test_options_[4]->option_,
255                                      test_options_[4]->persistent_,
256                                      test_options_[4]->space_name_);
257 
258         // Create the prefix delegation pool with an excluded prefix.
259         pdpool2.reset(new Pool6(IOAddress("2001:db8:d::"), 48, 64,
260                                 IOAddress("2001:db8:d::2000"), 120));
261 
262         subnet->addPool(pdpool2);
263 
264         pdpool2->allowClientClass("work");
265         pdpool2->requireClientClass("required-class3");
266         pdpool2->requireClientClass("required-class4");
267         user_context = Element::createMap();
268         user_context->set("bar", Element::create("foo"));
269         pdpool2->setContext(user_context);
270 
271         test_subnets_.push_back(subnet);
272 
273         subnet.reset(new Subnet6(IOAddress("2001:db8:3::"),
274                                  64, 20, 30, 40, 50, 2048));
275         Triplet<uint32_t> null_timer;
276         subnet->setPreferred(null_timer);
277         subnet->setT1(null_timer);
278         subnet->setT2(null_timer);
279         subnet->setValid(null_timer);
280         subnet->setPreferred(null_timer);
281         subnet->setDdnsSendUpdates(true);
282         subnet->setDdnsOverrideNoUpdate(true);
283         subnet->setDdnsOverrideClientUpdate(false);
284         subnet->setDdnsReplaceClientNameMode(D2ClientConfig::ReplaceClientNameMode::RCM_WHEN_PRESENT);
285         subnet->setDdnsGeneratedPrefix("myhost");
286         subnet->setDdnsQualifyingSuffix("example.org");
287 
288         subnet->getCfgOption()->add(test_options_[0]->option_,
289                                     test_options_[0]->persistent_,
290                                     test_options_[0]->space_name_);
291 
292         test_subnets_.push_back(subnet);
293 
294         // Add a subnet with all defaults.
295         subnet.reset(new Subnet6(IOAddress("2001:db8:4::"), 64,
296                                  Triplet<uint32_t>(), Triplet<uint32_t>(),
297                                  Triplet<uint32_t>(), Triplet<uint32_t>(),
298                                  4096));
299         test_subnets_.push_back(subnet);
300     }
301 
302     /// @brief Creates several subnets used in tests.
initTestSharedNetworks()303     void initTestSharedNetworks() {
304         ElementPtr user_context = Element::createMap();
305         user_context->set("foo", Element::create("bar"));
306 
307         std::string interface_id_text = "fish";
308         OptionBuffer interface_id(interface_id_text.begin(),
309                                   interface_id_text.end());
310         OptionPtr opt_interface_id(new Option(Option::V6, D6O_INTERFACE_ID,
311                                               interface_id));
312 
313         SharedNetwork6Ptr shared_network(new SharedNetwork6("level1"));
314         shared_network->allowClientClass("foo");
315         shared_network->setIface("eth1");
316         shared_network->setInterfaceId(opt_interface_id);
317         shared_network->setT2(323212);
318         shared_network->addRelayAddress(IOAddress("2001:db8:1::2"));
319         shared_network->addRelayAddress(IOAddress("2001:db8:3::4"));
320         shared_network->setT1(1234);
321         shared_network->requireClientClass("required-class1");
322         shared_network->requireClientClass("required-class2");
323         shared_network->setReservationsGlobal(false);
324         shared_network->setReservationsInSubnet(false);
325         shared_network->setContext(user_context);
326         shared_network->setValid(5555);
327         shared_network->setPreferred(4444);
328         shared_network->setCalculateTeeTimes(true);
329         shared_network->setT1Percent(0.345);
330         shared_network->setT2Percent(0.444);
331         shared_network->setDdnsSendUpdates(false);
332 
333         // Add several options to the shared network.
334         shared_network->getCfgOption()->add(test_options_[2]->option_,
335                                             test_options_[2]->persistent_,
336                                             test_options_[2]->space_name_);
337 
338         shared_network->getCfgOption()->add(test_options_[3]->option_,
339                                             test_options_[3]->persistent_,
340                                             test_options_[3]->space_name_);
341 
342         shared_network->getCfgOption()->add(test_options_[4]->option_,
343                                             test_options_[4]->persistent_,
344                                             test_options_[4]->space_name_);
345 
346         test_networks_.push_back(shared_network);
347 
348         // Adding another shared network called "level1" to test
349         // cases that this second instance can override existing
350         // "level1" instance.
351         shared_network.reset(new SharedNetwork6("level1"));
352         test_networks_.push_back(shared_network);
353 
354         // Add more shared networks.
355         shared_network.reset(new SharedNetwork6("level2"));
356         Triplet<uint32_t> null_timer;
357         shared_network->setPreferred(null_timer);
358         shared_network->setT1(null_timer);
359         shared_network->setT2(null_timer);
360         shared_network->setValid(null_timer);
361         shared_network->setPreferred(null_timer);
362         shared_network->setDdnsSendUpdates(true);
363         shared_network->setDdnsOverrideNoUpdate(true);
364         shared_network->setDdnsOverrideClientUpdate(false);
365         shared_network->setDdnsReplaceClientNameMode(D2ClientConfig::ReplaceClientNameMode::RCM_WHEN_PRESENT);
366         shared_network->setDdnsGeneratedPrefix("myhost");
367         shared_network->setDdnsQualifyingSuffix("example.org");
368 
369         shared_network->getCfgOption()->add(test_options_[0]->option_,
370                                             test_options_[0]->persistent_,
371                                             test_options_[0]->space_name_);
372         test_networks_.push_back(shared_network);
373 
374         shared_network.reset(new SharedNetwork6("level3"));
375         test_networks_.push_back(shared_network);
376     }
377 
378     /// @brief Creates several option definitions used in tests.
initTestOptionDefs()379     void initTestOptionDefs() {
380         ElementPtr user_context = Element::createMap();
381         user_context->set("foo", Element::create("bar"));
382 
383         OptionDefinitionPtr option_def(new OptionDefinition("foo", 1234,
384                                                             DHCP6_OPTION_SPACE,
385                                                             "string",
386                                                             "espace"));
387         test_option_defs_.push_back(option_def);
388 
389         option_def.reset(new OptionDefinition("bar", 1234, DHCP6_OPTION_SPACE,
390                                               "uint32", true));
391         test_option_defs_.push_back(option_def);
392 
393         option_def.reset(new OptionDefinition("fish", 5235, DHCP6_OPTION_SPACE,
394                                               "record", true));
395         option_def->addRecordField("uint32");
396         option_def->addRecordField("string");
397         test_option_defs_.push_back(option_def);
398 
399         option_def.reset(new OptionDefinition("whale", 20236, "xyz", "string"));
400         test_option_defs_.push_back(option_def);
401 
402         option_def.reset(new OptionDefinition("bar", 1234, DHCP6_OPTION_SPACE,
403                                               "uint64", true));
404         test_option_defs_.push_back(option_def);
405     }
406 
407     /// @brief Creates several DHCP options used in tests.
initTestOptions()408     void initTestOptions() {
409         ElementPtr user_context = Element::createMap();
410         user_context->set("foo", Element::create("bar"));
411 
412         OptionDefSpaceContainer defs;
413 
414         OptionDescriptor desc =
415             createOption<OptionString>(Option::V6, D6O_NEW_POSIX_TIMEZONE,
416                                        true, false, "my-timezone");
417         desc.space_name_ = DHCP6_OPTION_SPACE;
418         desc.setContext(user_context);
419         test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc)));
420 
421         desc = createOption<OptionUint8>(Option::V6, D6O_PREFERENCE,
422                                          false, true, 64);
423         desc.space_name_ = DHCP6_OPTION_SPACE;
424         test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc)));
425 
426         desc = createOption<OptionUint32>(Option::V6, 1, false, false, 312131),
427         desc.space_name_ = "vendor-encapsulated-options";
428         test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc)));
429 
430         desc = createAddressOption<Option6AddrLst>(1254, true, true,
431                                                    "2001:db8::3");
432         desc.space_name_ = DHCP6_OPTION_SPACE;
433         test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc)));
434 
435         desc = createEmptyOption(Option::V6, 1, true);
436         desc.space_name_ = "isc";
437         test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc)));
438 
439         desc = createAddressOption<Option6AddrLst>(2, false, true,
440                                                    "2001:db8:1::5",
441                                                    "2001:db8:1::3",
442                                                    "2001:db8:3::4");
443         desc.space_name_ = "isc";
444         test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc)));
445 
446         desc = createOption<OptionString>(Option::V6, D6O_NEW_POSIX_TIMEZONE,
447                                           true, false, "my-timezone-2");
448         desc.space_name_ = DHCP6_OPTION_SPACE;
449         desc.setContext(user_context);
450         test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc)));
451 
452         desc = createOption<OptionString>(Option::V6, D6O_NEW_POSIX_TIMEZONE,
453                                           true, false, "my-timezone-3");
454         desc.space_name_ = DHCP6_OPTION_SPACE;
455         desc.setContext(user_context);
456         test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc)));
457 
458         // Add definitions for DHCPv6 non-standard options in case we need to
459         // compare subnets, networks and pools in JSON format. In that case,
460         // the @c toElement functions require option definitions to generate the
461         // proper output.
462         defs.addItem(OptionDefinitionPtr(new OptionDefinition(
463                          "vendor-encapsulated-1", 1,
464                          "vendor-encapsulated-options", "uint32")));
465         defs.addItem(OptionDefinitionPtr(new OptionDefinition(
466                          "option-1254", 1254, DHCP6_OPTION_SPACE,
467                          "ipv6-address", true)));
468         defs.addItem(OptionDefinitionPtr(new OptionDefinition("isc-1", 1, "isc", "empty")));
469         defs.addItem(OptionDefinitionPtr(new OptionDefinition("isc-2", 2, "isc", "ipv6-address", true)));
470 
471         // Register option definitions.
472         LibDHCP::setRuntimeOptionDefs(defs);
473     }
474 
475     /// @brief Creates several client classes used in tests.
initTestClientClasses()476     void initTestClientClasses() {
477         ExpressionPtr match_expr = boost::make_shared<Expression>();
478         CfgOptionPtr cfg_option = boost::make_shared<CfgOption>();
479         auto class1 = boost::make_shared<ClientClassDef>("foo", match_expr, cfg_option);
480         class1->setRequired(true);
481         class1->setValid(Triplet<uint32_t>(30, 60, 90));
482         test_client_classes_.push_back(class1);
483 
484         auto class2 = boost::make_shared<ClientClassDef>("bar", match_expr, cfg_option);
485         class2->setTest("member('foo')");
486         test_client_classes_.push_back(class2);
487 
488         auto class3 = boost::make_shared<ClientClassDef>("foobar", match_expr, cfg_option);
489         class3->setTest("member('foo') and member('bar')");
490         test_client_classes_.push_back(class3);
491     }
492 
493     /// @brief Initialize posix time values used in tests.
initTimestamps()494     void initTimestamps() {
495         // Current time minus 1 hour to make sure it is in the past.
496         timestamps_["today"] = boost::posix_time::second_clock::local_time()
497             - boost::posix_time::hours(1);
498         // One second after today.
499         timestamps_["after today"] = timestamps_["today"] + boost::posix_time::seconds(1);
500         // Yesterday.
501         timestamps_["yesterday"] = timestamps_["today"] - boost::posix_time::hours(24);
502         // One second after yesterday.
503         timestamps_["after yesterday"] = timestamps_["yesterday"] + boost::posix_time::seconds(1);
504         // Two days ago.
505         timestamps_["two days ago"] = timestamps_["today"] - boost::posix_time::hours(48);
506         // Tomorrow.
507         timestamps_["tomorrow"] = timestamps_["today"] + boost::posix_time::hours(24);
508         // One second after tomorrow.
509         timestamps_["after tomorrow"] = timestamps_["tomorrow"] + boost::posix_time::seconds(1);
510     }
511 
512     /// @brief Logs audit entries in the @c audit_entries_ member.
513     ///
514     /// This function is called in case of an error.
515     ///
516     /// @param server_tag Server tag for which the audit entries should be logged.
logExistingAuditEntries(const std::string & server_tag)517     std::string logExistingAuditEntries(const std::string& server_tag) {
518         std::ostringstream s;
519 
520         auto& mod_time_idx = audit_entries_[server_tag].get<AuditEntryModificationTimeIdTag>();
521 
522         for (auto audit_entry_it = mod_time_idx.begin();
523              audit_entry_it != mod_time_idx.end();
524              ++audit_entry_it) {
525             auto audit_entry = *audit_entry_it;
526             s << audit_entry->getObjectType() << ", "
527               << audit_entry->getObjectId() << ", "
528               << static_cast<int>(audit_entry->getModificationType()) << ", "
529               << audit_entry->getModificationTime() << ", "
530               << audit_entry->getRevisionId() << ", "
531               << audit_entry->getLogMessage()
532               << std::endl;
533         }
534 
535         return (s.str());
536     }
537 
538     /// @brief Tests that the new audit entry is added.
539     ///
540     /// This method retrieves a collection of the existing audit entries and
541     /// checks that the new one has been added at the end of this collection.
542     /// It then verifies the values of the audit entry against the values
543     /// specified by the caller.
544     ///
545     /// @param exp_object_type Expected object type.
546     /// @param exp_modification_type Expected modification type.
547     /// @param exp_log_message Expected log message.
548     /// @param server_selector Server selector to be used for next query.
549     /// @param new_entries_num Number of the new entries expected to be inserted.
550     /// @param max_tested_entries Maximum number of entries tested.
testNewAuditEntry(const std::string & exp_object_type,const AuditEntry::ModificationType & exp_modification_type,const std::string & exp_log_message,const ServerSelector & server_selector=ServerSelector::ALL (),const size_t new_entries_num=1,const size_t max_tested_entries=65535)551     void testNewAuditEntry(const std::string& exp_object_type,
552                            const AuditEntry::ModificationType& exp_modification_type,
553                            const std::string& exp_log_message,
554                            const ServerSelector& server_selector = ServerSelector::ALL(),
555                            const size_t new_entries_num = 1,
556                            const size_t max_tested_entries = 65535) {
557         // Get the server tag for which the entries are fetched.
558         std::string tag;
559         if (server_selector.getType() == ServerSelector::Type::ALL) {
560             // Server tag is 'all'.
561             tag = "all";
562 
563         } else {
564             auto tags = server_selector.getTags();
565             // This test is not meant to handle multiple server tags all at once.
566             if (tags.size() > 1) {
567                 ADD_FAILURE() << "Test error: do not use multiple server tags";
568 
569             } else if (tags.size() == 1) {
570                 // Get the server tag for which we run the current test.
571                 tag = tags.begin()->get();
572             }
573         }
574 
575         auto audit_entries_size_save = audit_entries_[tag].size();
576 
577         // Audit entries for different server tags are stored in separate
578         // containers.
579         audit_entries_[tag] = cbptr_->getRecentAuditEntries(server_selector,
580                                                             timestamps_["two days ago"], 0);
581         ASSERT_EQ(audit_entries_size_save + new_entries_num, audit_entries_[tag].size())
582             << logExistingAuditEntries(tag);
583 
584         auto& mod_time_idx = audit_entries_[tag].get<AuditEntryModificationTimeIdTag>();
585 
586         // Iterate over specified number of entries starting from the most recent
587         // one and check they have correct values.
588         for (auto audit_entry_it = mod_time_idx.rbegin();
589              ((std::distance(mod_time_idx.rbegin(), audit_entry_it) < new_entries_num) &&
590               (std::distance(mod_time_idx.rbegin(), audit_entry_it) < max_tested_entries));
591              ++audit_entry_it) {
592             auto audit_entry = *audit_entry_it;
593             EXPECT_EQ(exp_object_type, audit_entry->getObjectType())
594                 << logExistingAuditEntries(tag);
595             EXPECT_EQ(exp_modification_type, audit_entry->getModificationType())
596                 << logExistingAuditEntries(tag);
597             EXPECT_EQ(exp_log_message, audit_entry->getLogMessage())
598                 << logExistingAuditEntries(tag);
599         }
600     }
601 
602     /// @brief Holds pointers to subnets used in tests.
603     std::vector<Subnet6Ptr> test_subnets_;
604 
605     /// @brief Holds pointers to shared networks used in tests.
606     std::vector<SharedNetwork6Ptr> test_networks_;
607 
608     /// @brief Holds pointers to option definitions used in tests.
609     std::vector<OptionDefinitionPtr> test_option_defs_;
610 
611     /// @brief Holds pointers to options used in tests.
612     std::vector<OptionDescriptorPtr> test_options_;
613 
614     /// @brief Holds pointers to classes used in tests.
615     std::vector<ClientClassDefPtr> test_client_classes_;
616 
617     /// @brief Holds pointers to the servers used in tests.
618     std::vector<ServerPtr> test_servers_;
619 
620     /// @brief Holds timestamp values used in tests.
621     std::map<std::string, boost::posix_time::ptime> timestamps_;
622 
623     /// @brief Holds pointer to the backend.
624     boost::shared_ptr<ConfigBackendDHCPv6> cbptr_;
625 
626     /// @brief Holds the most recent audit entries.
627     std::map<std::string, AuditEntryCollection> audit_entries_;
628 };
629 
630 // This test verifies that the expected backend type is returned.
TEST_F(MySqlConfigBackendDHCPv6Test,getType)631 TEST_F(MySqlConfigBackendDHCPv6Test, getType) {
632     DatabaseConnection::ParameterMap params;
633     params["name"] = "keatest";
634     params["password"] = "keatest";
635     params["user"] = "keatest";
636     ASSERT_NO_THROW(cbptr_.reset(new MySqlConfigBackendDHCPv6(params)));
637     ASSERT_NE(cbptr_->getParameters(), DatabaseConnection::ParameterMap());
638     EXPECT_EQ("mysql", cbptr_->getType());
639 }
640 
641 // This test verifies that by default localhost is returned as MySQL connection
642 // host.
TEST_F(MySqlConfigBackendDHCPv6Test,getHost)643 TEST_F(MySqlConfigBackendDHCPv6Test, getHost) {
644     DatabaseConnection::ParameterMap params;
645     params["name"] = "keatest";
646     params["password"] = "keatest";
647     params["user"] = "keatest";
648     ASSERT_NO_THROW(cbptr_.reset(new MySqlConfigBackendDHCPv6(params)));
649     ASSERT_NE(cbptr_->getParameters(), DatabaseConnection::ParameterMap());
650     EXPECT_EQ("localhost", cbptr_->getHost());
651 }
652 
653 // This test verifies that by default port of 0 is returned as MySQL connection
654 // port.
TEST_F(MySqlConfigBackendDHCPv6Test,getPort)655 TEST_F(MySqlConfigBackendDHCPv6Test, getPort) {
656     DatabaseConnection::ParameterMap params;
657     params["name"] = "keatest";
658     params["password"] = "keatest";
659     params["user"] = "keatest";
660     ASSERT_NO_THROW(cbptr_.reset(new MySqlConfigBackendDHCPv6(params)));
661     ASSERT_NE(cbptr_->getParameters(), DatabaseConnection::ParameterMap());
662     EXPECT_EQ(0, cbptr_->getPort());
663 }
664 
665 // This test verifies that the server can be added, updated and deleted.
TEST_F(MySqlConfigBackendDHCPv6Test,createUpdateDeleteServer)666 TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateDeleteServer) {
667     // Explicitly set modification time to make sure that the time
668     // returned from the database is correct.
669     test_servers_[0]->setModificationTime(timestamps_["yesterday"]);
670     test_servers_[1]->setModificationTime(timestamps_["today"]);
671 
672     // Insert the server1 into the database.
673     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[0]));
674 
675     {
676         SCOPED_TRACE("CREATE audit entry for server");
677         testNewAuditEntry("dhcp6_server",
678                           AuditEntry::ModificationType::CREATE,
679                           "server set");
680     }
681 
682     // It should not be possible to create a duplicate of the logical
683     // server 'all'.
684     auto all_server = Server::create(ServerTag("all"), "this is logical server all");
685     EXPECT_THROW(cbptr_->createUpdateServer6(all_server), isc::InvalidOperation);
686 
687     ServerPtr returned_server;
688 
689     // An attempt to fetch the server that hasn't been inserted should return
690     // a null pointer.
691     EXPECT_NO_THROW(returned_server = cbptr_->getServer6(ServerTag("server2")));
692     EXPECT_FALSE(returned_server);
693 
694     // Try to fetch the server which we expect to exist.
695     EXPECT_NO_THROW(returned_server = cbptr_->getServer6(ServerTag("server1")));
696     ASSERT_TRUE(returned_server);
697     EXPECT_EQ("server1", returned_server->getServerTagAsText());
698     EXPECT_EQ("this is server 1", returned_server->getDescription());
699     EXPECT_EQ(timestamps_["yesterday"], returned_server->getModificationTime());
700 
701     // This call is expected to update the existing server.
702     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[1]));
703 
704     {
705         SCOPED_TRACE("UPDATE audit entry for server");
706         testNewAuditEntry("dhcp6_server",
707                           AuditEntry::ModificationType::UPDATE,
708                           "server set");
709     }
710 
711     // Verify that the server has been updated.
712     EXPECT_NO_THROW(returned_server = cbptr_->getServer6(ServerTag("server1")));
713     ASSERT_TRUE(returned_server);
714     EXPECT_EQ("server1", returned_server->getServerTag().get());
715     EXPECT_EQ("this is server 1 bis", returned_server->getDescription());
716     EXPECT_EQ(timestamps_["today"], returned_server->getModificationTime());
717 
718 
719     uint64_t servers_deleted = 0;
720 
721     // Try to delete non-existing server.
722     EXPECT_NO_THROW(servers_deleted = cbptr_->deleteServer6(ServerTag("server2")));
723     EXPECT_EQ(0, servers_deleted);
724 
725     // Make sure that the server1 wasn't deleted.
726     EXPECT_NO_THROW(returned_server = cbptr_->getServer6(ServerTag("server1")));
727     EXPECT_TRUE(returned_server);
728 
729     // Deleting logical server 'all' is not allowed.
730     EXPECT_THROW(cbptr_->deleteServer6(ServerTag()), isc::InvalidOperation);
731 
732     // Delete the existing server.
733     EXPECT_NO_THROW(servers_deleted = cbptr_->deleteServer6(ServerTag("server1")));
734     EXPECT_EQ(1, servers_deleted);
735 
736     {
737         SCOPED_TRACE("DELETE audit entry for server");
738         testNewAuditEntry("dhcp6_server",
739                           AuditEntry::ModificationType::DELETE,
740                           "deleting a server");
741     }
742 
743     // Make sure that the server is gone.
744     EXPECT_NO_THROW(returned_server = cbptr_->getServer6(ServerTag("server1")));
745     EXPECT_FALSE(returned_server);
746 }
747 
748 // This test verifies that it is possible to retrieve all servers from the
749 // database and then delete all of them.
TEST_F(MySqlConfigBackendDHCPv6Test,getAndDeleteAllServers)750 TEST_F(MySqlConfigBackendDHCPv6Test, getAndDeleteAllServers) {
751     for (auto i = 1; i < test_servers_.size(); ++i) {
752         EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[i]));
753     }
754 
755     ServerCollection servers;
756     EXPECT_NO_THROW(servers = cbptr_->getAllServers6());
757     ASSERT_EQ(test_servers_.size() - 1, servers.size());
758 
759     // All servers should have been returned.
760     EXPECT_TRUE(ServerFetcher::get(servers, ServerTag("server1")));
761     EXPECT_TRUE(ServerFetcher::get(servers, ServerTag("server2")));
762     EXPECT_TRUE(ServerFetcher::get(servers, ServerTag("server3")));
763 
764     // The logical server all should not be returned. We merely return the
765     // user configured servers.
766     EXPECT_FALSE(ServerFetcher::get(servers, ServerTag()));
767 
768     // Delete all servers and make sure they are gone.
769     uint64_t deleted_servers = 0;
770     EXPECT_NO_THROW(deleted_servers = cbptr_->deleteAllServers6());
771 
772     EXPECT_NO_THROW(servers = cbptr_->getAllServers6());
773     EXPECT_TRUE(servers.empty());
774 
775     // All servers should be gone.
776     EXPECT_FALSE(ServerFetcher::get(servers, ServerTag("server1")));
777     EXPECT_FALSE(ServerFetcher::get(servers, ServerTag("server2")));
778     EXPECT_FALSE(ServerFetcher::get(servers, ServerTag("server3")));
779 
780     // The number of deleted server should be equal to the number of
781     // inserted servers. The logical 'all' server should be excluded.
782     EXPECT_EQ(test_servers_.size() - 1, deleted_servers);
783 }
784 
785 // This test verifies that the global parameter can be added, updated and
786 // deleted.
TEST_F(MySqlConfigBackendDHCPv6Test,createUpdateDeleteGlobalParameter6)787 TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateDeleteGlobalParameter6) {
788     StampedValuePtr global_parameter = StampedValue::create("global", "whale");
789 
790     // Explicitly set modification time to make sure that the time
791     // returned from the database is correct.
792     global_parameter->setModificationTime(timestamps_["yesterday"]);
793     cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(),
794                                          global_parameter);
795 
796     {
797         SCOPED_TRACE("CREATE audit entry for global parameter");
798         testNewAuditEntry("dhcp6_global_parameter",
799                           AuditEntry::ModificationType::CREATE,
800                           "global parameter set");
801     }
802 
803     // Verify returned parameter and the modification time.
804     StampedValuePtr returned_global_parameter =
805         cbptr_->getGlobalParameter6(ServerSelector::ALL(), "global");
806     ASSERT_TRUE(returned_global_parameter);
807     EXPECT_EQ("global", returned_global_parameter->getName());
808     EXPECT_EQ("whale", returned_global_parameter->getValue());
809     EXPECT_TRUE(returned_global_parameter->getModificationTime() ==
810                 global_parameter->getModificationTime());
811     ASSERT_EQ(1, returned_global_parameter->getServerTags().size());
812     EXPECT_EQ("all", returned_global_parameter->getServerTags().begin()->get());
813 
814     // Because we have added the global parameter for all servers, it
815     // should be also returned for the explicitly specified server.
816     returned_global_parameter = cbptr_->getGlobalParameter6(ServerSelector::ONE("server1"),
817                                                             "global");
818     ASSERT_TRUE(returned_global_parameter);
819     EXPECT_EQ("global", returned_global_parameter->getName());
820     EXPECT_EQ("whale", returned_global_parameter->getValue());
821     EXPECT_TRUE(returned_global_parameter->getModificationTime() ==
822                 global_parameter->getModificationTime());
823     ASSERT_EQ(1, returned_global_parameter->getServerTags().size());
824     EXPECT_EQ("all", returned_global_parameter->getServerTags().begin()->get());
825 
826     // Check that the parameter is updated when selector is specified correctly.
827     global_parameter = StampedValue::create("global", "fish");
828     cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(),
829                                          global_parameter);
830     returned_global_parameter = cbptr_->getGlobalParameter6(ServerSelector::ALL(),
831                                                             "global");
832     ASSERT_TRUE(returned_global_parameter);
833     EXPECT_EQ("global", returned_global_parameter->getName());
834     EXPECT_EQ("fish", returned_global_parameter->getValue());
835     EXPECT_TRUE(returned_global_parameter->getModificationTime() ==
836                 global_parameter->getModificationTime());
837 
838     {
839         SCOPED_TRACE("UPDATE audit entry for the global parameter");
840         testNewAuditEntry("dhcp6_global_parameter",
841                           AuditEntry::ModificationType::UPDATE,
842                           "global parameter set");
843     }
844 
845     // Should not delete parameter specified for all servers if explicit
846     // server name is provided.
847     EXPECT_EQ(0, cbptr_->deleteGlobalParameter6(ServerSelector::ONE("server1"),
848                                                 "global"));
849 
850     // Delete parameter and make sure it is gone.
851     cbptr_->deleteGlobalParameter6(ServerSelector::ALL(), "global");
852     returned_global_parameter = cbptr_->getGlobalParameter6(ServerSelector::ALL(),
853                                                             "global");
854     EXPECT_FALSE(returned_global_parameter);
855 
856     {
857         SCOPED_TRACE("DELETE audit entry for the global parameter");
858         testNewAuditEntry("dhcp6_global_parameter",
859                           AuditEntry::ModificationType::DELETE,
860                           "global parameter deleted");
861     }
862 }
863 
864 // This test verifies that it is possible to differentiate between the
865 // global parameters by server tag and that the value specified for the
866 // particular server overrides the value specified for all servers.
TEST_F(MySqlConfigBackendDHCPv6Test,globalParameters6WithServerTags)867 TEST_F(MySqlConfigBackendDHCPv6Test, globalParameters6WithServerTags) {
868     // Create three global parameters having the same name.
869     StampedValuePtr global_parameter1 = StampedValue::create("global", "value1");
870     StampedValuePtr global_parameter2 = StampedValue::create("global", "value2");
871     StampedValuePtr global_parameter3 = StampedValue::create("global", "value3");
872 
873     // Try to insert one of them and associate with non-existing server.
874     // This should fail because the server must be inserted first.
875     EXPECT_THROW(cbptr_->createUpdateGlobalParameter6(ServerSelector::ONE("server1"),
876                                                       global_parameter1),
877                  NullKeyError);
878 
879     // Create two servers.
880     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[1]));
881     {
882         SCOPED_TRACE("server1 is created");
883         testNewAuditEntry("dhcp6_server",
884                           AuditEntry::ModificationType::CREATE,
885                           "server set");
886     }
887 
888     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[2]));
889     {
890         SCOPED_TRACE("server2 is created");
891         testNewAuditEntry("dhcp6_server",
892                           AuditEntry::ModificationType::CREATE,
893                           "server set");
894     }
895 
896     // This time inserting the global parameters for the server1 and server2 should
897     // be successful.
898     EXPECT_NO_THROW(cbptr_->createUpdateGlobalParameter6(ServerSelector::ONE("server1"),
899                                                          global_parameter1));
900     {
901         SCOPED_TRACE("Global parameter for server1 is set");
902         // The value of 3 means there should be 3 audit entries available for the
903         // server1, two that indicate creation of the servers and one that we
904         // validate, which sets the global value.
905         testNewAuditEntry("dhcp6_global_parameter",
906                           AuditEntry::ModificationType::CREATE,
907                           "global parameter set",
908                           ServerSelector::ONE("server1"),
909                           3, 1);
910     }
911 
912 
913     EXPECT_NO_THROW(cbptr_->createUpdateGlobalParameter6(ServerSelector::ONE("server2"),
914                                                          global_parameter2));
915     {
916         SCOPED_TRACE("Global parameter for server2 is set");
917         // Same as in case of the server2, there should be 3 audit entries of
918         // which one we validate.
919         testNewAuditEntry("dhcp6_global_parameter",
920                           AuditEntry::ModificationType::CREATE,
921                           "global parameter set",
922                           ServerSelector::ONE("server2"),
923                           3, 1);
924     }
925 
926     // The last parameter is associated with all servers.
927     EXPECT_NO_THROW(cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(),
928                                                          global_parameter3));
929     {
930         SCOPED_TRACE("Global parameter for all servers is set");
931         // There should be one new audit entry for all servers. It indicates
932         // the insertion of the global value.
933         testNewAuditEntry("dhcp6_global_parameter",
934                           AuditEntry::ModificationType::CREATE,
935                           "global parameter set",
936                           ServerSelector::ALL(),
937                           1, 1);
938     }
939 
940     StampedValuePtr returned_global;
941 
942     // Try to fetch the value specified for all servers.
943     EXPECT_NO_THROW(
944         returned_global = cbptr_->getGlobalParameter6(ServerSelector::ALL(),
945                                                       "global")
946     );
947     ASSERT_TRUE(returned_global);
948     EXPECT_EQ(global_parameter3->getValue(), returned_global->getValue());
949     ASSERT_EQ(1, returned_global->getServerTags().size());
950     EXPECT_EQ("all", returned_global->getServerTags().begin()->get());
951 
952     // Try to fetch the value specified for the server1. This should override the
953     // value specified for all servers.
954     EXPECT_NO_THROW(
955         returned_global = cbptr_->getGlobalParameter6(ServerSelector::ONE("server1"),
956                                                       "global")
957     );
958     ASSERT_TRUE(returned_global);
959     EXPECT_EQ(global_parameter1->getValue(), returned_global->getValue());
960 
961     ASSERT_EQ(1, returned_global->getServerTags().size());
962     EXPECT_EQ("server1", returned_global->getServerTags().begin()->get());
963 
964     // The same in case of the server2.
965     EXPECT_NO_THROW(
966         returned_global = cbptr_->getGlobalParameter6(ServerSelector::ONE("server2"),
967                                                       "global")
968     );
969     ASSERT_TRUE(returned_global);
970     EXPECT_EQ(global_parameter2->getValue(), returned_global->getValue());
971     ASSERT_EQ(1, returned_global->getServerTags().size());
972     EXPECT_EQ("server2", returned_global->getServerTags().begin()->get());
973 
974     StampedValueCollection returned_globals;
975 
976     // Try to fetch the collection of globals for the server1, server2 and server3.
977     // The server3 does not have an explicit value so for this server we should get
978     /// the value for 'all'.
979     EXPECT_NO_THROW(
980         returned_globals = cbptr_->getAllGlobalParameters6(ServerSelector::
981                                                            MULTIPLE({ "server1", "server2",
982                                                                       "server3" }));
983     );
984     ASSERT_EQ(3, returned_globals.size());
985 
986     // Capture the returned values into the map so as we can check the
987     // values against the servers.
988     std::map<std::string, std::string> values;
989     for (auto g = returned_globals.begin(); g != returned_globals.end(); ++g) {
990         ASSERT_EQ(1, (*g)->getServerTags().size());
991         values[(*g)->getServerTags().begin()->get()] = ((*g)->getValue());
992     }
993 
994     ASSERT_EQ(3, values.size());
995     EXPECT_EQ(global_parameter1->getValue(), values["server1"]);
996     EXPECT_EQ(global_parameter2->getValue(), values["server2"]);
997     EXPECT_EQ(global_parameter3->getValue(), values["all"]);
998 
999     // Try to fetch the collection of global parameters specified for all servers.
1000     // This excludes the values specific to server1 and server2. It returns only the
1001     // common ones.
1002     EXPECT_NO_THROW(
1003         returned_globals = cbptr_->getAllGlobalParameters6(ServerSelector::ALL())
1004     );
1005     ASSERT_EQ(1, returned_globals.size());
1006     returned_global = *returned_globals.begin();
1007     EXPECT_EQ(global_parameter3->getValue(), returned_global->getValue());
1008     ASSERT_EQ(1, returned_global->getServerTags().size());
1009     EXPECT_EQ("all", returned_global->getServerTags().begin()->get());
1010 
1011     // Delete the server1. It should remove associations of this server with the
1012     // global parameter and the global parameter itself.
1013     EXPECT_NO_THROW(cbptr_->deleteServer6(ServerTag("server1")));
1014     EXPECT_NO_THROW(
1015         returned_globals = cbptr_->getAllGlobalParameters6(ServerSelector::ONE("server1"))
1016     );
1017     ASSERT_EQ(1, returned_globals.size());
1018     returned_global = *returned_globals.begin();
1019     // As a result, the value fetched for the server1 should be the one available for
1020     // all servers, rather than the one dedicated for server1. The association of
1021     // the server1 specific value with the server1 should be gone.
1022     EXPECT_EQ(global_parameter3->getValue(), returned_global->getValue());
1023     ASSERT_EQ(1, returned_global->getServerTags().size());
1024     EXPECT_EQ("all", returned_global->getServerTags().begin()->get());
1025 
1026     {
1027         SCOPED_TRACE("DELETE audit entry for the global parameter after server deletion");
1028         // We expect two new audit entries for the server1, one indicating that the
1029         // server has been deleted and another one indicating that the corresponding
1030         // global value has been deleted. We check the latter entry.
1031         testNewAuditEntry("dhcp6_global_parameter",
1032                           AuditEntry::ModificationType::DELETE,
1033                           "deleting a server", ServerSelector::ONE("server1"),
1034                           2, 1);
1035     }
1036 
1037     // Attempt to delete global parameter for server1.
1038     uint64_t deleted_num = 0;
1039     EXPECT_NO_THROW(deleted_num = cbptr_->deleteGlobalParameter6(ServerSelector::ONE("server1"),
1040                                                                  "global"));
1041     // No parameters should be deleted. In particular, the parameter for the logical
1042     // server 'all' should not be deleted.
1043     EXPECT_EQ(0, deleted_num);
1044 
1045     // Deleting the existing value for server2 should succeed.
1046     EXPECT_NO_THROW(deleted_num = cbptr_->deleteGlobalParameter6(ServerSelector::ONE("server2"),
1047                                                                  "global"));
1048     EXPECT_EQ(1, deleted_num);
1049 
1050     // Create it again to test that deletion of all server removes this too.
1051     EXPECT_NO_THROW(cbptr_->createUpdateGlobalParameter6(ServerSelector::ONE("server2"),
1052                                                          global_parameter2));
1053 
1054     // Delete all servers, except 'all'.
1055     EXPECT_NO_THROW(deleted_num = cbptr_->deleteAllServers6());
1056     EXPECT_NO_THROW(
1057         returned_globals = cbptr_->getAllGlobalParameters6(ServerSelector::ALL())
1058     );
1059     EXPECT_EQ(1, deleted_num);
1060     ASSERT_EQ(1, returned_globals.size());
1061     returned_global = *returned_globals.begin();
1062     // The common value for all servers should still be available because 'all'
1063     // logical server should not be deleted.
1064     EXPECT_EQ(global_parameter3->getValue(), returned_global->getValue());
1065     ASSERT_EQ(1, returned_global->getServerTags().size());
1066     EXPECT_EQ("all", returned_global->getServerTags().begin()->get());
1067 
1068     {
1069         SCOPED_TRACE("DELETE audit entry for the global parameter after deletion of"
1070                      " all servers");
1071         // There should be 4 new audit entries. One for deleting the global, one for
1072         // re-creating it, one for deleting the server2 and one for deleting the
1073         // global again as a result of deleting the server2.
1074         testNewAuditEntry("dhcp6_global_parameter",
1075                           AuditEntry::ModificationType::DELETE,
1076                           "deleting all servers", ServerSelector::ONE("server2"),
1077                           4, 1);
1078     }
1079 }
1080 
1081 // This test verifies that all global parameters can be retrieved and deleted.
TEST_F(MySqlConfigBackendDHCPv6Test,getAllGlobalParameters6)1082 TEST_F(MySqlConfigBackendDHCPv6Test, getAllGlobalParameters6) {
1083     // Create 3 parameters and put them into the database.
1084     cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(),
1085         StampedValue::create("name1", "value1"));
1086     cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(),
1087         StampedValue::create("name2", Element::create(static_cast<int64_t>(65))));
1088     cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(),
1089         StampedValue::create("name3", "value3"));
1090     cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(),
1091         StampedValue::create("name4", Element::create(static_cast<bool>(true))));
1092     cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(),
1093         StampedValue::create("name5", Element::create(static_cast<double>(1.65))));
1094 
1095     // Fetch all parameters.
1096     auto parameters = cbptr_->getAllGlobalParameters6(ServerSelector::ALL());
1097     ASSERT_EQ(5, parameters.size());
1098 
1099     const auto& parameters_index = parameters.get<StampedValueNameIndexTag>();
1100 
1101     for (auto param = parameters_index.begin(); param != parameters_index.end();
1102          ++param) {
1103         ASSERT_EQ(1, (*param)->getServerTags().size());
1104         EXPECT_EQ("all", (*param)->getServerTags().begin()->get());
1105     }
1106 
1107     // Verify their values.
1108     EXPECT_EQ("value1", (*parameters_index.find("name1"))->getValue());
1109     EXPECT_EQ(65, (*parameters_index.find("name2"))->getIntegerValue());
1110     EXPECT_EQ("value3", (*parameters_index.find("name3"))->getValue());
1111     EXPECT_TRUE((*parameters_index.find("name4"))->getBoolValue());
1112     EXPECT_EQ(1.65, (*parameters_index.find("name5"))->getDoubleValue());
1113 
1114     // Should be able to fetch these parameters when explicitly providing
1115     // the server tag.
1116     parameters = cbptr_->getAllGlobalParameters6(ServerSelector::ONE("server1"));
1117     EXPECT_EQ(5, parameters.size());
1118 
1119     // Deleting global parameters with non-matching server selector
1120     // should fail.
1121     EXPECT_EQ(0, cbptr_->deleteAllGlobalParameters6(ServerSelector::ONE("server1")));
1122 
1123     // Delete all parameters and make sure they are gone.
1124     EXPECT_EQ(5, cbptr_->deleteAllGlobalParameters6(ServerSelector::ALL()));
1125     parameters = cbptr_->getAllGlobalParameters6(ServerSelector::ALL());
1126     EXPECT_TRUE(parameters.empty());
1127 }
1128 
1129 // This test verifies that modified global parameters can be retrieved.
TEST_F(MySqlConfigBackendDHCPv6Test,getModifiedGlobalParameters6)1130 TEST_F(MySqlConfigBackendDHCPv6Test, getModifiedGlobalParameters6) {
1131     // Create 3 global parameters and assign modification times:
1132     // "yesterday", "today" and "tomorrow" respectively.
1133     StampedValuePtr value = StampedValue::create("name1", "value1");
1134     value->setModificationTime(timestamps_["yesterday"]);
1135     cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(),
1136                                          value);
1137 
1138     value = StampedValue::create("name2", Element::create(static_cast<int64_t>(65)));
1139     value->setModificationTime(timestamps_["today"]);
1140     cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(),
1141                                          value);
1142 
1143     value = StampedValue::create("name3", "value3");
1144     value->setModificationTime(timestamps_["tomorrow"]);
1145     cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(),
1146                                          value);
1147 
1148     // Get parameters modified after "today".
1149     auto parameters = cbptr_->getModifiedGlobalParameters6(ServerSelector::ALL(),
1150                                                            timestamps_["after today"]);
1151 
1152     const auto& parameters_index = parameters.get<StampedValueNameIndexTag>();
1153 
1154     // It should be the one modified "tomorrow".
1155     ASSERT_EQ(1, parameters_index.size());
1156 
1157     auto parameter = parameters_index.find("name3");
1158     ASSERT_FALSE(parameter == parameters_index.end());
1159 
1160     ASSERT_TRUE(*parameter);
1161     EXPECT_EQ("value3", (*parameter)->getValue());
1162 
1163     // Should be able to fetct these parameters when explicitly providing
1164     // the server tag.
1165     parameters = cbptr_->getModifiedGlobalParameters6(ServerSelector::ONE("server1"),
1166                                                       timestamps_["after today"]);
1167     EXPECT_EQ(1, parameters.size());
1168 }
1169 
1170 // Test that the NullKeyError message is correctly updated.
TEST_F(MySqlConfigBackendDHCPv6Test,nullKeyError)1171 TEST_F(MySqlConfigBackendDHCPv6Test, nullKeyError) {
1172     // Create a global parameter (it should work with any object type).
1173     StampedValuePtr global_parameter = StampedValue::create("global", "value");
1174 
1175     // Try to insert it and associate with non-existing server.
1176     std::string msg;
1177     try {
1178         cbptr_->createUpdateGlobalParameter6(ServerSelector::ONE("server1"),
1179                                              global_parameter);
1180         msg = "got no exception";
1181     } catch (const NullKeyError& ex) {
1182         msg = ex.what();
1183     } catch (const std::exception&) {
1184         msg = "got another exception";
1185     }
1186     EXPECT_EQ("server 'server1' does not exist", msg);
1187 }
1188 
1189 // Test that ceateUpdateSubnet6 throws appropriate exceptions for various
1190 // server selectors.
TEST_F(MySqlConfigBackendDHCPv6Test,createUpdateSubnet6Selectors)1191 TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateSubnet6Selectors) {
1192     ASSERT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[0]));
1193     ASSERT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[2]));
1194 
1195     // Supported selectors.
1196     Subnet6Ptr subnet = test_subnets_[0];
1197     EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ALL(),
1198                                                 subnet));
1199     subnet = test_subnets_[2];
1200     EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ONE("server1"),
1201                                                 subnet));
1202     subnet = test_subnets_[3];
1203     EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
1204                                                 subnet));
1205 
1206     // Not supported server selectors.
1207     EXPECT_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ANY(), subnet),
1208                  isc::InvalidOperation);
1209 
1210     // Not implemented server selectors.
1211     EXPECT_THROW(cbptr_->createUpdateSubnet6(ServerSelector::UNASSIGNED(),
1212                                              subnet),
1213                  isc::NotImplemented);
1214 }
1215 
1216 // Test that subnet can be inserted, fetched, updated and then fetched again.
TEST_F(MySqlConfigBackendDHCPv6Test,getSubnet6)1217 TEST_F(MySqlConfigBackendDHCPv6Test, getSubnet6) {
1218     // Insert the server2 into the database.
1219     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[2]));
1220     {
1221         SCOPED_TRACE("CREATE audit entry for server");
1222         testNewAuditEntry("dhcp6_server",
1223                           AuditEntry::ModificationType::CREATE,
1224                           "server set");
1225     }
1226 
1227     auto subnet = test_subnets_[0];
1228     auto subnet2 = test_subnets_[2];
1229 
1230     // An attempt to add a subnet to a non-existing server (server1) should fail.
1231     EXPECT_THROW(cbptr_->createUpdateSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
1232                                              subnet2),
1233                  NullKeyError);
1234 
1235     // The subnet shouldn't have been added, even though one of the servers exists.
1236     Subnet6Ptr returned_subnet;
1237     ASSERT_NO_THROW(returned_subnet = cbptr_->getSubnet6(ServerSelector::ONE("server2"),
1238                                                                              subnet2->getID()));
1239     EXPECT_FALSE(returned_subnet);
1240 
1241     // Insert two subnets, one for all servers and one for server2.
1242     EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet));
1243     {
1244         SCOPED_TRACE("CREATE audit entry for the subnet");
1245         testNewAuditEntry("dhcp6_subnet",
1246                           AuditEntry::ModificationType::CREATE,
1247                           "subnet set");
1248     }
1249 
1250 
1251     EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ONE("server2"), subnet2));
1252     {
1253         SCOPED_TRACE("CREATE audit entry for the subnet");
1254         testNewAuditEntry("dhcp6_subnet",
1255                           AuditEntry::ModificationType::CREATE,
1256                           "subnet set", ServerSelector::ONE("subnet2"),
1257                           2, 1);
1258     }
1259 
1260     // We are not going to support selection of a single entry for multiple servers.
1261     EXPECT_THROW(cbptr_->getSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
1262                                     subnet->getID()),
1263                  isc::InvalidOperation);
1264 
1265     EXPECT_THROW(cbptr_->getSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
1266                                     subnet->toText()),
1267                  isc::InvalidOperation);
1268 
1269     // Test that this subnet will be fetched for various server selectors.
1270     auto test_get_subnet = [this, &subnet] (const std::string& test_case_name,
1271                                             const ServerSelector& server_selector,
1272                                             const std::string& expected_tag = ServerTag::ALL) {
1273         SCOPED_TRACE(test_case_name);
1274 
1275         // Test fetching subnet by id.
1276         Subnet6Ptr returned_subnet;
1277         ASSERT_NO_THROW(returned_subnet = cbptr_->getSubnet6(server_selector, subnet->getID()));
1278         ASSERT_TRUE(returned_subnet);
1279 
1280         ASSERT_EQ(1, returned_subnet->getServerTags().size());
1281         EXPECT_TRUE(returned_subnet->hasServerTag(ServerTag(expected_tag)));
1282 
1283         EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
1284 
1285         // Test fetching subnet by prefix.
1286         ASSERT_NO_THROW(returned_subnet = cbptr_->getSubnet6(server_selector,
1287                                                              subnet->toText()));
1288         ASSERT_TRUE(returned_subnet);
1289 
1290         ASSERT_EQ(1, returned_subnet->getServerTags().size());
1291         EXPECT_TRUE(returned_subnet->hasServerTag(ServerTag(expected_tag)));
1292 
1293         EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
1294     };
1295 
1296     {
1297         SCOPED_TRACE("testing various server selectors before update");
1298         test_get_subnet("all servers", ServerSelector::ALL());
1299         test_get_subnet("one server", ServerSelector::ONE("server1"));
1300         test_get_subnet("any server", ServerSelector::ANY());
1301     }
1302 
1303     subnet = subnet2;
1304     {
1305         SCOPED_TRACE("testing server selectors for another server");
1306         test_get_subnet("one server", ServerSelector::ONE("server2"), "server2");
1307         test_get_subnet("any server", ServerSelector::ANY(), "server2");
1308     }
1309 
1310     // Update the subnet in the database (both use the same ID).
1311     subnet = test_subnets_[1];
1312     EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet));
1313     {
1314         SCOPED_TRACE("CREATE audit entry for the subnet");
1315         testNewAuditEntry("dhcp6_subnet",
1316                           AuditEntry::ModificationType::UPDATE,
1317                           "subnet set");
1318     }
1319 
1320     {
1321         SCOPED_TRACE("testing various server selectors after update");
1322         test_get_subnet("all servers", ServerSelector::ALL());
1323         test_get_subnet("one server", ServerSelector::ONE("server1"));
1324         test_get_subnet("any server", ServerSelector::ANY());
1325     }
1326 
1327     // The server2 specific subnet should not be returned if the server selector
1328     // is not matching.
1329     EXPECT_FALSE(cbptr_->getSubnet6(ServerSelector::ALL(), subnet2->getID()));
1330     EXPECT_FALSE(cbptr_->getSubnet6(ServerSelector::ALL(), subnet2->toText()));
1331     EXPECT_FALSE(cbptr_->getSubnet6(ServerSelector::ONE("server1"), subnet2->getID()));
1332     EXPECT_FALSE(cbptr_->getSubnet6(ServerSelector::ONE("server1"), subnet2->toText()));
1333 
1334     // Update the subnet in the database (both use the same prefix).
1335     subnet2.reset(new Subnet6(IOAddress("2001:db8:3::"),
1336                               64, 30, 40, 50, 80, 8192));
1337     EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ONE("server2"),  subnet2));
1338 
1339     // Fetch again and verify.
1340     returned_subnet = cbptr_->getSubnet6(ServerSelector::ONE("server2"), subnet2->toText());
1341     ASSERT_TRUE(returned_subnet);
1342     EXPECT_EQ(subnet2->toElement()->str(), returned_subnet->toElement()->str());
1343 
1344     // Update the subnet when it conflicts same id and same prefix both
1345     // with different subnets. This should throw.
1346     // Subnets are 2001:db8:1::/48 id 1024 and 2001:db8:3::/64 id 8192
1347     subnet2.reset(new Subnet6(IOAddress("2001:db8:1::"),
1348                               48, 30, 40, 50, 80, 8192));
1349     EXPECT_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ONE("server2"),  subnet2),
1350                  DuplicateEntry);
1351 }
1352 
1353 // Test that getSubnet6 by ID throws appropriate exceptions for various server
1354 // selectors.
TEST_F(MySqlConfigBackendDHCPv6Test,getSubnet6byIdSelectors)1355 TEST_F(MySqlConfigBackendDHCPv6Test, getSubnet6byIdSelectors) {
1356     // Supported selectors.
1357     EXPECT_NO_THROW(cbptr_->getSubnet6(ServerSelector::ANY(), SubnetID(1)));
1358     EXPECT_NO_THROW(cbptr_->getSubnet6(ServerSelector::UNASSIGNED(), SubnetID(1)));
1359     EXPECT_NO_THROW(cbptr_->getSubnet6(ServerSelector::ALL(), SubnetID(1)));
1360     EXPECT_NO_THROW(cbptr_->getSubnet6(ServerSelector::ONE("server1"), SubnetID(1)));
1361 
1362     // Not supported selectors.
1363     EXPECT_THROW(cbptr_->getSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
1364                                     SubnetID(1)),
1365                  isc::InvalidOperation);
1366 }
1367 
1368 // Test that the information about unspecified optional parameters gets
1369 // propagated to the database.
TEST_F(MySqlConfigBackendDHCPv6Test,getSubnet6WithOptionalUnspecified)1370 TEST_F(MySqlConfigBackendDHCPv6Test, getSubnet6WithOptionalUnspecified) {
1371     // Create a subnet and wrap it within a shared network. It is important
1372     // to have the shared network to verify that the subnet doesn't inherit
1373     // the values of the shared network but stores the NULL values in the
1374     // for those parameters that are unspecified on the subnet level.
1375     Subnet6Ptr subnet = test_subnets_[2];
1376     SharedNetwork6Ptr shared_network = test_networks_[0];
1377     shared_network->add(subnet);
1378 
1379     // Need to add the shared network to the database because otherwise
1380     // the subnet foreign key would fail.
1381     cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(), shared_network);
1382     cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet);
1383 
1384     // Fetch this subnet by subnet identifier.
1385     Subnet6Ptr returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
1386                                                     subnet->getID());
1387     ASSERT_TRUE(returned_subnet);
1388 
1389     EXPECT_TRUE(returned_subnet->getIface().unspecified());
1390     EXPECT_TRUE(returned_subnet->getIface().empty());
1391 
1392     EXPECT_TRUE(returned_subnet->getClientClass().unspecified());
1393     EXPECT_TRUE(returned_subnet->getClientClass().empty());
1394 
1395     EXPECT_TRUE(returned_subnet->getValid().unspecified());
1396     EXPECT_EQ(0, returned_subnet->getValid().get());
1397 
1398     EXPECT_TRUE(returned_subnet->getPreferred().unspecified());
1399     EXPECT_EQ(0, returned_subnet->getPreferred().get());
1400 
1401     EXPECT_TRUE(returned_subnet->getT1().unspecified());
1402     EXPECT_EQ(0, returned_subnet->getT1().get());
1403 
1404     EXPECT_TRUE(returned_subnet->getT2().unspecified());
1405     EXPECT_EQ(0, returned_subnet->getT2().get());
1406 
1407     EXPECT_TRUE(returned_subnet->getReservationsGlobal().unspecified());
1408     EXPECT_FALSE(returned_subnet->getReservationsGlobal().get());
1409 
1410     EXPECT_TRUE(returned_subnet->getReservationsInSubnet().unspecified());
1411     EXPECT_TRUE(returned_subnet->getReservationsInSubnet().get());
1412 
1413     EXPECT_TRUE(returned_subnet->getReservationsOutOfPool().unspecified());
1414     EXPECT_FALSE(returned_subnet->getReservationsOutOfPool().get());
1415 
1416     EXPECT_TRUE(returned_subnet->getCalculateTeeTimes().unspecified());
1417     EXPECT_FALSE(returned_subnet->getCalculateTeeTimes().get());
1418 
1419     EXPECT_TRUE(returned_subnet->getT1Percent().unspecified());
1420     EXPECT_EQ(0.0, returned_subnet->getT1Percent().get());
1421 
1422     EXPECT_TRUE(returned_subnet->getT2Percent().unspecified());
1423     EXPECT_EQ(0.0, returned_subnet->getT2Percent().get());
1424 
1425     EXPECT_TRUE(returned_subnet->getRapidCommit().unspecified());
1426     EXPECT_FALSE(returned_subnet->getRapidCommit().get());
1427 
1428     EXPECT_FALSE(returned_subnet->getDdnsSendUpdates().unspecified());
1429     EXPECT_TRUE(returned_subnet->getDdnsSendUpdates().get());
1430 
1431     EXPECT_FALSE(returned_subnet->getDdnsOverrideNoUpdate().unspecified());
1432     EXPECT_TRUE(returned_subnet->getDdnsOverrideNoUpdate().get());
1433 
1434     EXPECT_FALSE(returned_subnet->getDdnsOverrideClientUpdate().unspecified());
1435     EXPECT_FALSE(returned_subnet->getDdnsOverrideClientUpdate().get());
1436 
1437     EXPECT_FALSE(returned_subnet->getDdnsReplaceClientNameMode().unspecified());
1438     EXPECT_EQ(D2ClientConfig::ReplaceClientNameMode::RCM_WHEN_PRESENT,
1439               returned_subnet->getDdnsReplaceClientNameMode().get());
1440 
1441     EXPECT_FALSE(returned_subnet->getDdnsGeneratedPrefix().unspecified());
1442     EXPECT_EQ("myhost", returned_subnet->getDdnsGeneratedPrefix().get());
1443 
1444     EXPECT_FALSE(returned_subnet->getDdnsQualifyingSuffix().unspecified());
1445     EXPECT_EQ("example.org", returned_subnet->getDdnsQualifyingSuffix().get());
1446 
1447     // The easiest way to verify whether the returned subnet matches the inserted
1448     // subnet is to convert both to text.
1449     EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
1450 
1451 }
1452 
1453 // Test that subnet can be associated with a shared network.
TEST_F(MySqlConfigBackendDHCPv6Test,getSubnet6SharedNetwork)1454 TEST_F(MySqlConfigBackendDHCPv6Test, getSubnet6SharedNetwork) {
1455     Subnet6Ptr subnet = test_subnets_[0];
1456     SharedNetwork6Ptr shared_network = test_networks_[0];
1457 
1458     // Add subnet to a shared network.
1459     shared_network->add(subnet);
1460 
1461     // Store shared network in the database.
1462     cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(),
1463                                        shared_network);
1464 
1465     // Store subnet associated with the shared network in the database.
1466     cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet);
1467 
1468     // Fetch this subnet by subnet identifier.
1469     Subnet6Ptr returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
1470                                                     test_subnets_[0]->getID());
1471     ASSERT_TRUE(returned_subnet);
1472     ASSERT_EQ(1, returned_subnet->getServerTags().size());
1473     EXPECT_EQ("all", returned_subnet->getServerTags().begin()->get());
1474 
1475     // The easiest way to verify whether the returned subnet matches the inserted
1476     // subnet is to convert both to text.
1477     EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
1478 
1479     // However, the check above doesn't verify whether shared network name was
1480     // correctly returned from the database.
1481     EXPECT_EQ(shared_network->getName(), returned_subnet->getSharedNetworkName());
1482 }
1483 
1484 // Test that subnet can be fetched by prefix.
TEST_F(MySqlConfigBackendDHCPv6Test,getSubnet6ByPrefix)1485 TEST_F(MySqlConfigBackendDHCPv6Test, getSubnet6ByPrefix) {
1486     // Insert subnet to the database.
1487     Subnet6Ptr subnet = test_subnets_[0];
1488     cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet);
1489 
1490     // Fetch the subnet by prefix.
1491     Subnet6Ptr returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
1492                                                     "2001:db8::/64");
1493     ASSERT_TRUE(returned_subnet);
1494     ASSERT_EQ(1, returned_subnet->getServerTags().size());
1495     EXPECT_EQ("all", returned_subnet->getServerTags().begin()->get());
1496 
1497     // Verify subnet contents.
1498     EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
1499 
1500     // Fetching the subnet for an explicitly specified server tag should
1501     // succeed too.
1502     returned_subnet = cbptr_->getSubnet6(ServerSelector::ONE("server1"),
1503                                          "2001:db8::/64");
1504     EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
1505 }
1506 
1507 // Test that getSubnet6 by prefix throws appropriate exceptions for various server
1508 // selectors.
TEST_F(MySqlConfigBackendDHCPv6Test,getSubnet6byPrefixSelectors)1509 TEST_F(MySqlConfigBackendDHCPv6Test, getSubnet6byPrefixSelectors) {
1510     // Supported selectors.
1511     EXPECT_NO_THROW(cbptr_->getSubnet6(ServerSelector::ANY(), "192.0.2.0/26"));
1512     EXPECT_NO_THROW(cbptr_->getSubnet6(ServerSelector::UNASSIGNED(), "192.0.2.0/26"));
1513     EXPECT_NO_THROW(cbptr_->getSubnet6(ServerSelector::ALL(), "192.0.2.0/26"));
1514     EXPECT_NO_THROW(cbptr_->getSubnet6(ServerSelector::ONE("server1"), "192.0.2.0/26"));
1515 
1516     // Not supported selectors.
1517     EXPECT_THROW(cbptr_->getSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
1518                                     "192.0.2.0/26"),
1519                  isc::InvalidOperation);
1520 }
1521 
1522 // Test that all subnets can be fetched and then deleted.
TEST_F(MySqlConfigBackendDHCPv6Test,getAllSubnets6)1523 TEST_F(MySqlConfigBackendDHCPv6Test, getAllSubnets6) {
1524     // Insert test subnets into the database. Note that the second subnet will
1525     // overwrite the first subnet as they use the same ID.
1526     for (auto subnet : test_subnets_) {
1527         cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet);
1528 
1529         // That subnet overrides the first subnet so the audit entry should
1530         // indicate an update.
1531         if (subnet->toText() == "2001:db8:1::/48") {
1532             SCOPED_TRACE("UPDATE audit entry for the subnet " + subnet->toText());
1533             testNewAuditEntry("dhcp6_subnet",
1534                               AuditEntry::ModificationType::UPDATE,
1535                               "subnet set");
1536 
1537         } else {
1538             SCOPED_TRACE("CREATE audit entry for the subnet " + subnet->toText());
1539             testNewAuditEntry("dhcp6_subnet",
1540                               AuditEntry::ModificationType::CREATE,
1541                               "subnet set");
1542         }
1543     }
1544 
1545     // Fetch all subnets.
1546     Subnet6Collection subnets = cbptr_->getAllSubnets6(ServerSelector::ALL());
1547     ASSERT_EQ(test_subnets_.size() - 1, subnets.size());
1548 
1549     // All subnets should also be returned for explicitly specified server tag.
1550     subnets = cbptr_->getAllSubnets6(ServerSelector::ONE("server1"));
1551     ASSERT_EQ(test_subnets_.size() - 1, subnets.size());
1552 
1553     // See if the subnets are returned ok.
1554     auto subnet_it = subnets.begin();
1555     for (auto i = 0; i < subnets.size(); ++i, ++subnet_it) {
1556         EXPECT_EQ(test_subnets_[i + 1]->toElement()->str(),
1557                   (*subnet_it)->toElement()->str());
1558         ASSERT_EQ(1, (*subnet_it)->getServerTags().size());
1559         EXPECT_EQ("all", (*subnet_it)->getServerTags().begin()->get());
1560     }
1561 
1562     // Attempt to remove the non existing subnet should  return 0.
1563     EXPECT_EQ(0, cbptr_->deleteSubnet6(ServerSelector::ALL(), 22));
1564     EXPECT_EQ(0, cbptr_->deleteSubnet6(ServerSelector::ALL(),
1565                                        "2001:db8:555::/64"));
1566     // All subnets should be still there.
1567     ASSERT_EQ(test_subnets_.size() - 1, subnets.size());
1568 
1569     // Should not delete the subnet for explicit server tag because
1570     // our subnet is for all servers.
1571     EXPECT_EQ(0, cbptr_->deleteSubnet6(ServerSelector::ONE("server1"),
1572                                        test_subnets_[1]->getID()));
1573 
1574     // Also, verify that behavior when deleting by prefix.
1575     EXPECT_EQ(0, cbptr_->deleteSubnet6(ServerSelector::ONE("server1"),
1576                                        test_subnets_[2]->toText()));
1577 
1578     // Same for all subnets.
1579     EXPECT_EQ(0, cbptr_->deleteAllSubnets6(ServerSelector::ONE("server1")));
1580 
1581     // Delete first subnet by id and verify that it is gone.
1582     EXPECT_EQ(1, cbptr_->deleteSubnet6(ServerSelector::ALL(),
1583                                        test_subnets_[1]->getID()));
1584 
1585     {
1586         SCOPED_TRACE("DELETE first subnet audit entry");
1587         testNewAuditEntry("dhcp6_subnet",
1588                           AuditEntry::ModificationType::DELETE,
1589                           "subnet deleted");
1590     }
1591 
1592     subnets = cbptr_->getAllSubnets6(ServerSelector::ALL());
1593     ASSERT_EQ(test_subnets_.size() - 2, subnets.size());
1594 
1595     // Delete second subnet by prefix and verify it is gone.
1596     EXPECT_EQ(1, cbptr_->deleteSubnet6(ServerSelector::ALL(),
1597                                        test_subnets_[2]->toText()));
1598     subnets = cbptr_->getAllSubnets6(ServerSelector::ALL());
1599     ASSERT_EQ(test_subnets_.size() - 3, subnets.size());
1600 
1601     {
1602         SCOPED_TRACE("DELETE second subnet audit entry");
1603         testNewAuditEntry("dhcp6_subnet",
1604                           AuditEntry::ModificationType::DELETE,
1605                           "subnet deleted");
1606     }
1607 
1608     // Delete all.
1609     EXPECT_EQ(1, cbptr_->deleteAllSubnets6(ServerSelector::ALL()));
1610     subnets = cbptr_->getAllSubnets6(ServerSelector::ALL());
1611     ASSERT_TRUE(subnets.empty());
1612 
1613     {
1614         SCOPED_TRACE("DELETE all subnets audit entry");
1615         testNewAuditEntry("dhcp6_subnet",
1616                           AuditEntry::ModificationType::DELETE,
1617                           "deleted all subnets");
1618     }
1619 }
1620 
1621 // Test that getAllSubnets6 throws appropriate exceptions for various
1622 // server selectors.
TEST_F(MySqlConfigBackendDHCPv6Test,getAllSubnets6Selectors)1623 TEST_F(MySqlConfigBackendDHCPv6Test, getAllSubnets6Selectors) {
1624     // Supported selectors.
1625     EXPECT_NO_THROW(cbptr_->getAllSubnets6(ServerSelector::UNASSIGNED()));
1626     EXPECT_NO_THROW(cbptr_->getAllSubnets6(ServerSelector::ALL()));
1627     EXPECT_NO_THROW(cbptr_->getAllSubnets6(ServerSelector::ONE("server1")));
1628     EXPECT_NO_THROW(cbptr_->getAllSubnets6(ServerSelector::MULTIPLE({ "server1", "server2" })));
1629 
1630     // Not supported selectors.
1631     EXPECT_THROW(cbptr_->getAllSubnets6(ServerSelector::ANY()), isc::InvalidOperation);
1632 }
1633 
1634 // Test that subnets with different server associations are returned.
TEST_F(MySqlConfigBackendDHCPv6Test,getAllSubnets6WithServerTags)1635 TEST_F(MySqlConfigBackendDHCPv6Test, getAllSubnets6WithServerTags) {
1636     auto subnet1 = test_subnets_[0];
1637     auto subnet2 = test_subnets_[2];
1638     auto subnet3 = test_subnets_[3];
1639 
1640     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[0]));
1641     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[2]));
1642 
1643     EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ALL(),
1644                                                 subnet1));
1645     EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ONE("server1"),
1646                                                 subnet2));
1647     EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
1648                                                 subnet3));
1649 
1650     Subnet6Collection subnets;
1651 
1652     // All three subnets are associated with the server1.
1653     EXPECT_NO_THROW(subnets = cbptr_->getAllSubnets6(ServerSelector::ONE("server1")));
1654     EXPECT_EQ(3, subnets.size());
1655 
1656     // First subnet is associated with all servers.
1657     auto returned_subnet = SubnetFetcher6::get(subnets, SubnetID(1024));
1658     ASSERT_TRUE(returned_subnet);
1659     EXPECT_TRUE(returned_subnet->hasAllServerTag());
1660     EXPECT_FALSE(returned_subnet->hasServerTag(ServerTag("server1")));
1661     EXPECT_FALSE(returned_subnet->hasServerTag(ServerTag("server2")));
1662 
1663     // Second subnet is only associated with the server1.
1664     returned_subnet = SubnetFetcher6::get(subnets, SubnetID(2048));
1665     ASSERT_TRUE(returned_subnet);
1666     EXPECT_FALSE(returned_subnet->hasAllServerTag());
1667     EXPECT_TRUE(returned_subnet->hasServerTag(ServerTag("server1")));
1668     EXPECT_FALSE(returned_subnet->hasServerTag(ServerTag("server2")));
1669 
1670     // Third subnet is associated with both server1 and server2.
1671     returned_subnet = SubnetFetcher6::get(subnets, SubnetID(4096));
1672     ASSERT_TRUE(returned_subnet);
1673     EXPECT_FALSE(returned_subnet->hasAllServerTag());
1674     EXPECT_TRUE(returned_subnet->hasServerTag(ServerTag("server1")));
1675     EXPECT_TRUE(returned_subnet->hasServerTag(ServerTag("server2")));
1676 
1677     // For server2 we should only get two subnets, i.e. first and last.
1678     EXPECT_NO_THROW(subnets = cbptr_->getAllSubnets6(ServerSelector::ONE("server2")));
1679     EXPECT_EQ(2, subnets.size());
1680 
1681     // First subnet is associated with all servers.
1682     returned_subnet = SubnetFetcher6::get(subnets, SubnetID(1024));
1683     ASSERT_TRUE(returned_subnet);
1684     EXPECT_TRUE(returned_subnet->hasAllServerTag());
1685     EXPECT_FALSE(returned_subnet->hasServerTag(ServerTag("server1")));
1686     EXPECT_FALSE(returned_subnet->hasServerTag(ServerTag("server2")));
1687 
1688     // Last subnet is associated with server1 and server2.
1689     returned_subnet = SubnetFetcher6::get(subnets, SubnetID(4096));
1690     ASSERT_TRUE(returned_subnet);
1691     EXPECT_FALSE(returned_subnet->hasAllServerTag());
1692     EXPECT_TRUE(returned_subnet->hasServerTag(ServerTag("server1")));
1693     EXPECT_TRUE(returned_subnet->hasServerTag(ServerTag("server2")));
1694 
1695     // Only the first subnet is associated with all servers.
1696     EXPECT_NO_THROW(subnets = cbptr_->getAllSubnets6(ServerSelector::ALL()));
1697     EXPECT_EQ(1, subnets.size());
1698 
1699     returned_subnet = SubnetFetcher6::get(subnets, SubnetID(1024));
1700     ASSERT_TRUE(returned_subnet);
1701     EXPECT_TRUE(returned_subnet->hasAllServerTag());
1702     EXPECT_FALSE(returned_subnet->hasServerTag(ServerTag("server1")));
1703     EXPECT_FALSE(returned_subnet->hasServerTag(ServerTag("server2")));
1704 }
1705 
1706 // Test that selected subnet can be deleted.
TEST_F(MySqlConfigBackendDHCPv6Test,deleteSubnet6)1707 TEST_F(MySqlConfigBackendDHCPv6Test, deleteSubnet6) {
1708     // Create two servers in the database.
1709     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[0]));
1710     {
1711         SCOPED_TRACE("CREATE audit entry for server");
1712         testNewAuditEntry("dhcp6_server",
1713                           AuditEntry::ModificationType::CREATE,
1714                           "server set");
1715     }
1716 
1717     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[2]));
1718     {
1719         SCOPED_TRACE("CREATE audit entry for server");
1720         testNewAuditEntry("dhcp6_server",
1721                           AuditEntry::ModificationType::CREATE,
1722                           "server set");
1723     }
1724 
1725     auto subnet1 = test_subnets_[0];
1726     auto subnet2 = test_subnets_[2];
1727     auto subnet3 = test_subnets_[3];
1728 
1729     auto create_test_subnets = [&] () {
1730         // Insert three subnets, one for all servers, one for server2 and one for two
1731         // servers: server1 and server2.
1732         EXPECT_NO_THROW(
1733             cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet1)
1734         );
1735         EXPECT_NO_THROW(
1736             cbptr_->createUpdateSubnet6(ServerSelector::ONE("server2"), subnet2)
1737         );
1738         EXPECT_NO_THROW(
1739             cbptr_->createUpdateSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
1740                                         subnet3)
1741         );
1742     };
1743 
1744     create_test_subnets();
1745 
1746     // Test that subnet is not deleted for a specified server selector.
1747     auto test_no_delete = [this] (const std::string& test_case_name,
1748                                   const ServerSelector& server_selector,
1749                                   const Subnet6Ptr& subnet) {
1750         SCOPED_TRACE(test_case_name);
1751         uint64_t deleted_count = 0;
1752         EXPECT_NO_THROW(
1753             deleted_count = cbptr_->deleteSubnet6(server_selector, subnet->getID())
1754         );
1755         EXPECT_EQ(0, deleted_count);
1756 
1757         deleted_count = 0;
1758         EXPECT_NO_THROW(
1759             deleted_count = cbptr_->deleteSubnet6(server_selector, subnet->toText())
1760         );
1761         EXPECT_EQ(0, deleted_count);
1762     };
1763 
1764     {
1765         SCOPED_TRACE("Test valid but non matching server selectors");
1766         test_no_delete("selector: one, actual: all", ServerSelector::ONE("server2"),
1767                        subnet1);
1768         test_no_delete("selector: all, actual: one", ServerSelector::ALL(),
1769                        subnet2);
1770         test_no_delete("selector: all, actual: multiple", ServerSelector::ALL(),
1771                        subnet3);
1772     }
1773 
1774     // Test successful deletion of a subnet by ID.
1775     auto test_delete_by_id = [this] (const std::string& test_case_name,
1776                                      const ServerSelector& server_selector,
1777                                      const Subnet6Ptr& subnet) {
1778         SCOPED_TRACE(test_case_name);
1779         uint64_t deleted_count = 0;
1780         EXPECT_NO_THROW(
1781             deleted_count = cbptr_->deleteSubnet6(server_selector, subnet->getID())
1782         );
1783         EXPECT_EQ(1, deleted_count);
1784 
1785         EXPECT_FALSE(cbptr_->getSubnet6(server_selector, subnet->getID()));
1786     };
1787 
1788     test_delete_by_id("all servers", ServerSelector::ALL(), subnet1);
1789     test_delete_by_id("any server", ServerSelector::ANY(), subnet2);
1790     test_delete_by_id("one server", ServerSelector::ONE("server1"), subnet3);
1791 
1792     // Re-create deleted subnets.
1793     create_test_subnets();
1794 
1795     // Test successful deletion of a subnet by prefix.
1796     auto test_delete_by_prefix = [this] (const std::string& test_case_name,
1797                                          const ServerSelector& server_selector,
1798                                          const Subnet6Ptr& subnet) {
1799         SCOPED_TRACE(test_case_name);
1800         uint64_t deleted_count = 0;
1801         EXPECT_NO_THROW(
1802             deleted_count = cbptr_->deleteSubnet6(server_selector, subnet->toText())
1803         );
1804         EXPECT_EQ(1, deleted_count);
1805 
1806         EXPECT_FALSE(cbptr_->getSubnet6(server_selector, subnet->toText()));
1807     };
1808 
1809     test_delete_by_prefix("all servers", ServerSelector::ALL(), subnet1);
1810     test_delete_by_prefix("any server", ServerSelector::ANY(), subnet2);
1811     test_delete_by_prefix("one server", ServerSelector::ONE("server1"), subnet3);
1812 }
1813 
1814 // Test that deleteSubnet6 by ID throws appropriate exceptions for various
1815 // server selectors.
TEST_F(MySqlConfigBackendDHCPv6Test,deleteSubnet6ByIdSelectors)1816 TEST_F(MySqlConfigBackendDHCPv6Test, deleteSubnet6ByIdSelectors) {
1817     // Supported selectors.
1818     EXPECT_NO_THROW(cbptr_->deleteSubnet6(ServerSelector::ANY(), SubnetID(1)));
1819     EXPECT_NO_THROW(cbptr_->deleteSubnet6(ServerSelector::ALL(), SubnetID(1)));
1820     EXPECT_NO_THROW(cbptr_->deleteSubnet6(ServerSelector::ONE("server1"), SubnetID(1)));
1821 
1822     // Not supported selectors.
1823     EXPECT_THROW(cbptr_->deleteSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
1824                                            SubnetID(1)),
1825                  isc::InvalidOperation);
1826 
1827     // Not implemented selectors.
1828     EXPECT_THROW(cbptr_->deleteSubnet6(ServerSelector::UNASSIGNED(), SubnetID(1)),
1829                  isc::NotImplemented);
1830 }
1831 
1832 // Test that deleteSubnet6 by prefix throws appropriate exceptions for various
1833 // server selectors.
TEST_F(MySqlConfigBackendDHCPv6Test,deleteSubnet6ByPrefixSelectors)1834 TEST_F(MySqlConfigBackendDHCPv6Test, deleteSubnet6ByPrefixSelectors) {
1835     // Supported selectors.
1836     EXPECT_NO_THROW(cbptr_->deleteSubnet6(ServerSelector::ANY(), "192.0.2.0/26"));
1837     EXPECT_NO_THROW(cbptr_->deleteSubnet6(ServerSelector::ALL(), "192.0.2.0/26"));
1838     EXPECT_NO_THROW(cbptr_->deleteSubnet6(ServerSelector::ONE("server1"), "192.0.2.0/26"));
1839 
1840     // Not supported selectors.
1841     EXPECT_THROW(cbptr_->deleteSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
1842                                            "192.0.2.0/26"),
1843                  isc::InvalidOperation);
1844 
1845     // Not implemented selectors.
1846     EXPECT_THROW(cbptr_->deleteSubnet6(ServerSelector::UNASSIGNED(), "192.0.2.0/26"),
1847                  isc::NotImplemented);
1848 }
1849 
1850 // Test that deleteAllSubnets6 throws appropriate exceptions for various
1851 // server selectors.
TEST_F(MySqlConfigBackendDHCPv6Test,deleteAllSubnets6Selectors)1852 TEST_F(MySqlConfigBackendDHCPv6Test, deleteAllSubnets6Selectors) {
1853     // Supported selectors.
1854     EXPECT_NO_THROW(cbptr_->deleteAllSubnets6(ServerSelector::UNASSIGNED()));
1855     EXPECT_NO_THROW(cbptr_->deleteAllSubnets6(ServerSelector::ALL()));
1856     EXPECT_NO_THROW(cbptr_->deleteAllSubnets6(ServerSelector::ONE("server1")));
1857 
1858     // Not supported selectors.
1859     EXPECT_THROW(cbptr_->deleteAllSubnets6(ServerSelector::ANY()),
1860                  isc::InvalidOperation);
1861     EXPECT_THROW(cbptr_->deleteAllSubnets6(ServerSelector::MULTIPLE({ "server1", "server2" })),
1862                  isc::InvalidOperation);
1863 }
1864 
1865 // Test that it is possible to retrieve and delete orphaned subnet.
TEST_F(MySqlConfigBackendDHCPv6Test,unassignedSubnet6)1866 TEST_F(MySqlConfigBackendDHCPv6Test, unassignedSubnet6) {
1867     // Create the server.
1868     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[0]));
1869 
1870     // Create the subnets and associate them with the server1.
1871     auto subnet = test_subnets_[0];
1872     auto subnet2 = test_subnets_[2];
1873     EXPECT_NO_THROW(
1874         cbptr_->createUpdateSubnet6(ServerSelector::ONE("server1"), subnet)
1875     );
1876     EXPECT_NO_THROW(
1877         cbptr_->createUpdateSubnet6(ServerSelector::ONE("server1"), subnet2)
1878     );
1879 
1880     // Delete the server. The subnets should be preserved but are considered orphaned,
1881     // i.e. do not belong to any server.
1882     uint64_t deleted_count = 0;
1883     EXPECT_NO_THROW(deleted_count = cbptr_->deleteServer6(ServerTag("server1")));
1884     EXPECT_EQ(1, deleted_count);
1885 
1886     // Trying to fetch the subnet by server tag should return no result.
1887     Subnet6Ptr returned_subnet;
1888     EXPECT_NO_THROW(returned_subnet = cbptr_->getSubnet6(ServerSelector::ONE("server1"),
1889                                                          subnet->getID()));
1890     EXPECT_FALSE(returned_subnet);
1891 
1892     // The same if we use other calls.
1893     EXPECT_NO_THROW(returned_subnet = cbptr_->getSubnet6(ServerSelector::ONE("server1"),
1894                                                          subnet->toText()));
1895     EXPECT_FALSE(returned_subnet);
1896 
1897     Subnet6Collection returned_subnets;
1898     EXPECT_NO_THROW(returned_subnets = cbptr_->getAllSubnets6(ServerSelector::ONE("server1")));
1899     EXPECT_TRUE(returned_subnets.empty());
1900 
1901     EXPECT_NO_THROW(
1902         returned_subnets = cbptr_->getModifiedSubnets6(ServerSelector::ONE("server1"),
1903                                                        timestamps_["two days ago"])
1904     );
1905     EXPECT_TRUE(returned_subnets.empty());
1906 
1907     // We should get the subnet if we ask for unassigned.
1908     EXPECT_NO_THROW(returned_subnet = cbptr_->getSubnet6(ServerSelector::UNASSIGNED(),
1909                                                          subnet->getID()));
1910     ASSERT_TRUE(returned_subnet);
1911 
1912     EXPECT_NO_THROW(returned_subnet = cbptr_->getSubnet6(ServerSelector::UNASSIGNED(),
1913                                                          subnet->toText()));
1914     ASSERT_TRUE(returned_subnet);
1915 
1916     // Also if we ask for all unassigned subnets it should be returned.
1917     EXPECT_NO_THROW(returned_subnets = cbptr_->getAllSubnets6(ServerSelector::UNASSIGNED()));
1918     ASSERT_EQ(2, returned_subnets.size());
1919 
1920     // Same for modified subnets.
1921     EXPECT_NO_THROW(
1922         returned_subnets = cbptr_->getModifiedSubnets6(ServerSelector::UNASSIGNED(),
1923                                                        timestamps_["two days ago"])
1924     );
1925     ASSERT_EQ(2, returned_subnets.size());
1926 
1927     // If we ask for any subnet by subnet id, it should be returned too.
1928     EXPECT_NO_THROW(returned_subnet = cbptr_->getSubnet6(ServerSelector::ANY(),
1929                                                          subnet->getID()));
1930     ASSERT_TRUE(returned_subnet);
1931 
1932     EXPECT_NO_THROW(returned_subnet = cbptr_->getSubnet6(ServerSelector::ANY(),
1933                                                          subnet->toText()));
1934     ASSERT_TRUE(returned_subnet);
1935 
1936     // Deleting the subnet with the mismatched server tag should not affect our
1937     // subnet.
1938     EXPECT_NO_THROW(
1939         deleted_count = cbptr_->deleteSubnet6(ServerSelector::ONE("server1"),
1940                                               subnet->getID())
1941     );
1942     EXPECT_EQ(0, deleted_count);
1943 
1944     // Also, if we delete all subnets for server1.
1945     EXPECT_NO_THROW(
1946         deleted_count = cbptr_->deleteAllSubnets6(ServerSelector::ONE("server1"))
1947     );
1948     EXPECT_EQ(0, deleted_count);
1949 
1950     // We can delete this subnet when we specify ANY and the matching id.
1951     EXPECT_NO_THROW(
1952         deleted_count = cbptr_->deleteSubnet6(ServerSelector::ANY(), subnet->getID())
1953     );
1954     EXPECT_EQ(1, deleted_count);
1955 
1956     // We can delete all subnets using UNASSIGNED selector.
1957     EXPECT_NO_THROW(
1958         deleted_count = cbptr_->deleteAllSubnets6(ServerSelector::UNASSIGNED());
1959     );
1960     EXPECT_EQ(1, deleted_count);
1961 }
1962 
1963 // Test that subnets modified after given time can be fetched.
TEST_F(MySqlConfigBackendDHCPv6Test,getModifiedSubnets6)1964 TEST_F(MySqlConfigBackendDHCPv6Test, getModifiedSubnets6) {
1965     // Explicitly set timestamps of subnets. First subnet has a timestamp
1966     // pointing to the future. Second subnet has timestamp pointing to the
1967     // past (yesterday). Third subnet has a timestamp pointing to the
1968     // past (an hour ago).
1969     test_subnets_[1]->setModificationTime(timestamps_["tomorrow"]);
1970     test_subnets_[2]->setModificationTime(timestamps_["yesterday"]);
1971     test_subnets_[3]->setModificationTime(timestamps_["today"]);
1972 
1973     // Insert subnets into the database.
1974     for (int i = 1; i < test_subnets_.size(); ++i) {
1975         cbptr_->createUpdateSubnet6(ServerSelector::ALL(),
1976                                     test_subnets_[i]);
1977     }
1978 
1979     // Fetch subnets with timestamp later than today. Only one subnet
1980     // should be returned.
1981     Subnet6Collection
1982         subnets = cbptr_->getModifiedSubnets6(ServerSelector::ALL(),
1983                                               timestamps_["after today"]);
1984     ASSERT_EQ(1, subnets.size());
1985 
1986     // All subnets should also be returned for explicitly specified server tag.
1987     subnets = cbptr_->getModifiedSubnets6(ServerSelector::ONE("server1"),
1988                                           timestamps_["after today"]);
1989     ASSERT_EQ(1, subnets.size());
1990 
1991     // Fetch subnets with timestamp later than yesterday. We should get
1992     // two subnets.
1993     subnets = cbptr_->getModifiedSubnets6(ServerSelector::ALL(),
1994                                           timestamps_["after yesterday"]);
1995     ASSERT_EQ(2, subnets.size());
1996 
1997     // Fetch subnets with timestamp later than tomorrow. Nothing should
1998     // be returned.
1999     subnets = cbptr_->getModifiedSubnets6(ServerSelector::ALL(),
2000                                           timestamps_["after tomorrow"]);
2001     ASSERT_TRUE(subnets.empty());
2002 }
2003 
2004 // Test that getModifiedSubnets6 throws appropriate exceptions for various
2005 // server selectors.
TEST_F(MySqlConfigBackendDHCPv6Test,getModifiedSubnets6Selectors)2006 TEST_F(MySqlConfigBackendDHCPv6Test, getModifiedSubnets6Selectors) {
2007     // Supported selectors.
2008     EXPECT_NO_THROW(cbptr_->getModifiedSubnets6(ServerSelector::UNASSIGNED(),
2009                                                 timestamps_["yesterday"]));
2010     EXPECT_NO_THROW(cbptr_->getModifiedSubnets6(ServerSelector::ALL(),
2011                                                 timestamps_["yesterday"]));
2012     EXPECT_NO_THROW(cbptr_->getModifiedSubnets6(ServerSelector::ONE("server1"),
2013                                                 timestamps_["yesterday"]));
2014     EXPECT_NO_THROW(cbptr_->getModifiedSubnets6(ServerSelector::MULTIPLE({ "server1", "server2" }),
2015                                                 timestamps_["yesterday"]));
2016 
2017     // Not supported selectors.
2018     EXPECT_THROW(cbptr_->getModifiedSubnets6(ServerSelector::ANY(),
2019                                              timestamps_["yesterday"]),
2020                  isc::InvalidOperation);
2021 }
2022 
2023 // Test that lifetimes in subnets are handled as expected.
TEST_F(MySqlConfigBackendDHCPv6Test,subnetLifetime)2024 TEST_F(MySqlConfigBackendDHCPv6Test, subnetLifetime) {
2025     // Insert new subnet with unspecified valid lifetime
2026     Triplet<uint32_t> unspecified;
2027     Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8::"), 64, 30, 40,
2028                                   unspecified, unspecified, 1111));
2029     subnet->setIface("eth1");
2030     cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet);
2031 
2032     // Fetch this subnet by subnet identifier
2033     Subnet6Ptr returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
2034                                                     subnet->getID());
2035     ASSERT_TRUE(returned_subnet);
2036 
2037     // Verified returned and original subnets match.
2038     EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
2039 
2040     // Update the preferred and valid lifetime.
2041     subnet->setPreferred( Triplet<uint32_t>(100, 200, 300));
2042     subnet->setValid( Triplet<uint32_t>(200, 300, 400));
2043     cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet);
2044 
2045     // Fetch and verify again.
2046     returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(), subnet->getID());
2047     ASSERT_TRUE(returned_subnet);
2048     EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
2049 }
2050 
2051 // Test that subnets belonging to a shared network can be retrieved.
TEST_F(MySqlConfigBackendDHCPv6Test,getSharedNetworkSubnets6)2052 TEST_F(MySqlConfigBackendDHCPv6Test, getSharedNetworkSubnets6) {
2053     // Assign test subnets to shared networks level1 and level2.
2054     test_subnets_[1]->setSharedNetworkName("level1");
2055     test_subnets_[2]->setSharedNetworkName("level2");
2056     test_subnets_[3]->setSharedNetworkName("level2");
2057 
2058     // Store shared networks in the database.
2059     for (auto network : test_networks_) {
2060         cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(), network);
2061     }
2062 
2063     // Store subnets in the database.
2064     for (auto subnet : test_subnets_) {
2065         cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet);
2066     }
2067 
2068     // Fetch all subnets belonging to shared network level1.
2069     Subnet6Collection subnets = cbptr_->getSharedNetworkSubnets6(ServerSelector::ALL(),
2070                                                                  "level1");
2071     ASSERT_EQ(1, subnets.size());
2072 
2073     // Returned subnet should match test subnet #1.
2074     EXPECT_TRUE(isEquivalent(test_subnets_[1]->toElement(),
2075                              (*subnets.begin())->toElement()));
2076 
2077     // All subnets should also be returned for ANY server.
2078     subnets = cbptr_->getSharedNetworkSubnets6(ServerSelector::ANY(), "level1");
2079     ASSERT_EQ(1, subnets.size());
2080 
2081     // Returned subnet should match test subnet #1.
2082     EXPECT_TRUE(isEquivalent(test_subnets_[1]->toElement(),
2083                              (*subnets.begin())->toElement()));
2084 
2085     // Fetch all subnets belonging to shared network level2.
2086     subnets = cbptr_->getSharedNetworkSubnets6(ServerSelector::ALL(), "level2");
2087     ASSERT_EQ(2, subnets.size());
2088 
2089     ElementPtr test_list = Element::createList();
2090     test_list->add(test_subnets_[2]->toElement());
2091     test_list->add(test_subnets_[3]->toElement());
2092 
2093     ElementPtr returned_list = Element::createList();
2094     auto subnet = subnets.begin();
2095     returned_list->add((*subnet)->toElement());
2096     returned_list->add((*++subnet)->toElement());
2097 
2098     EXPECT_TRUE(isEquivalent(returned_list, test_list));
2099 
2100     // All subnets should also be returned for explicitly specified server tag.
2101     subnets = cbptr_->getSharedNetworkSubnets6(ServerSelector::ONE("server1"), "level2");
2102     ASSERT_EQ(2, subnets.size());
2103 
2104     returned_list = Element::createList();
2105     subnet = subnets.begin();
2106     returned_list->add((*subnet)->toElement());
2107     returned_list->add((*++subnet)->toElement());
2108 
2109     EXPECT_TRUE(isEquivalent(returned_list, test_list));
2110 }
2111 
2112 // Test that pools are properly updated as a result a subnet update.
TEST_F(MySqlConfigBackendDHCPv6Test,subnetUpdatePools)2113 TEST_F(MySqlConfigBackendDHCPv6Test, subnetUpdatePools) {
2114 
2115     auto test_subnet_update = [this](const std::string& subnet_prefix,
2116                                      const SubnetID& subnet_id) {
2117         // Add the subnet with two address pools and two prefix delegation
2118         // pools.
2119         EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ALL(),
2120                                                     test_subnets_[0]));
2121         // Make sure that the pools have been added to the database.
2122         EXPECT_EQ(2, countRows("dhcp6_pool"));
2123         EXPECT_EQ(2, countRows("dhcp6_pd_pool"));
2124 
2125         // Create the subnet without options which updates the existing
2126         // subnet.
2127         Subnet6Ptr subnet(new Subnet6(IOAddress(subnet_prefix), 64, 30, 60, 50, 60,
2128                                       subnet_id));
2129         EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet));
2130         // Check that options are gone.
2131         EXPECT_EQ(0, countRows("dhcp6_pool"));
2132         EXPECT_EQ(0, countRows("dhcp6_pd_pool"));
2133     };
2134 
2135     {
2136         SCOPED_TRACE("update subnet, modify subnet id");
2137         // Create another subnet with the same prefix as the original subnet but
2138         // different id. This is legal to update the subnet id if the prefix is
2139         // stable. However, the new subnet has no address pools, so we need to
2140         // check of the pools associated with the existing subnet instance are
2141         // gone after the update.
2142         test_subnet_update("2001:db8::", 2048);
2143     }
2144 
2145     {
2146         SCOPED_TRACE("update subnet, modify prefix");
2147         // Create a subnet with the same subnet id but different prefix.
2148         // The prefix should be updated.
2149         test_subnet_update("2001:db9::", 1024);
2150     }
2151 }
2152 
2153 // Test that deleting a subnet triggers deletion of the options associated
2154 // with the subnet and pools.
TEST_F(MySqlConfigBackendDHCPv6Test,subnetOptions)2155 TEST_F(MySqlConfigBackendDHCPv6Test, subnetOptions) {
2156     // Add the subnet with two pools and three options.
2157     EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ALL(), test_subnets_[0]));
2158     EXPECT_EQ(2, countRows("dhcp6_pool"));
2159     EXPECT_EQ(2, countRows("dhcp6_pd_pool"));
2160     EXPECT_EQ(3, countRows("dhcp6_options"));
2161 
2162     // The second subnet uses the same subnet id, so this operation should replace
2163     // the existing subnet and its options. The new instance has four pools, each
2164     // including one option, so we should end up with four options.
2165     EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ALL(), test_subnets_[1]));
2166     EXPECT_EQ(2, countRows("dhcp6_pool"));
2167     EXPECT_EQ(2, countRows("dhcp6_pd_pool"));
2168     EXPECT_EQ(4, countRows("dhcp6_options"));
2169 
2170     // Add third subnet with a single option. The number of options in the database
2171     // should now be 5.
2172     EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ALL(), test_subnets_[2]));
2173     EXPECT_EQ(2, countRows("dhcp6_pool"));
2174     EXPECT_EQ(2, countRows("dhcp6_pd_pool"));
2175     EXPECT_EQ(5, countRows("dhcp6_options"));
2176 
2177     // Delete the subnet. All options and pools it contains should also be removed, leaving
2178     // the last added subnet and its sole option.
2179     EXPECT_NO_THROW(cbptr_->deleteSubnet6(ServerSelector::ALL(), test_subnets_[1]->getID()));
2180     EXPECT_EQ(1, countRows("dhcp6_subnet"));
2181     EXPECT_EQ(0, countRows("dhcp6_pool"));
2182     EXPECT_EQ(0, countRows("dhcp6_pd_pool"));
2183     EXPECT_EQ(1, countRows("dhcp6_options"));
2184 
2185     // Add the first subnet again. We should now have 4 options: 3 options from the
2186     // newly added subnet and one option from the existing subnet.
2187     EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ALL(), test_subnets_[0]));
2188     EXPECT_EQ(2, countRows("dhcp6_pool"));
2189     EXPECT_EQ(2, countRows("dhcp6_pd_pool"));
2190     EXPECT_EQ(4, countRows("dhcp6_options"));
2191 
2192     // Delete the subnet including 3 options. The option from the other subnet should not
2193     // be affected.
2194     EXPECT_NO_THROW(cbptr_->deleteSubnet6(ServerSelector::ALL(), test_subnets_[0]->getID()));
2195     EXPECT_EQ(1, countRows("dhcp6_subnet"));
2196     EXPECT_EQ(0, countRows("dhcp6_pool"));
2197     EXPECT_EQ(0, countRows("dhcp6_pd_pool"));
2198     EXPECT_EQ(1, countRows("dhcp6_options"));
2199 }
2200 
2201 // Test that shared network can be inserted, fetched, updated and then
2202 // fetched again.
TEST_F(MySqlConfigBackendDHCPv6Test,getSharedNetwork6)2203 TEST_F(MySqlConfigBackendDHCPv6Test, getSharedNetwork6) {
2204     // Insert the server2 into the database.
2205     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[2]));
2206     {
2207         SCOPED_TRACE("CREATE audit entry for server");
2208         testNewAuditEntry("dhcp6_server",
2209                           AuditEntry::ModificationType::CREATE,
2210                           "server set");
2211     }
2212 
2213     auto shared_network = test_networks_[0];
2214     auto shared_network2 = test_networks_[2];
2215 
2216     // Insert two shared networks, one for all servers, and one for server2.
2217     EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(),
2218                                                        shared_network));
2219     EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ONE("server2"),
2220                                                        shared_network2));
2221 
2222     // We are not going to support selection of a single entry for multiple servers.
2223     EXPECT_THROW(cbptr_->getSharedNetwork6(ServerSelector::MULTIPLE({ "server1", "server2" }),
2224                                            test_networks_[0]->getName()),
2225                  isc::InvalidOperation);
2226 
2227     // Test that this shared network will be fetched for various server selectors.
2228     auto test_get_network = [this, &shared_network] (const std::string& test_case_name,
2229                                                      const ServerSelector& server_selector,
2230                                                      const std::string& expected_tag = ServerTag::ALL) {
2231         SCOPED_TRACE(test_case_name);
2232         SharedNetwork6Ptr network;
2233         ASSERT_NO_THROW(network = cbptr_->getSharedNetwork6(server_selector,
2234                                                             shared_network->getName()));
2235         ASSERT_TRUE(network);
2236 
2237         EXPECT_GT(network->getId(), 0);
2238         ASSERT_EQ(1, network->getServerTags().size());
2239         EXPECT_EQ(expected_tag, network->getServerTags().begin()->get());
2240 
2241         // The easiest way to verify whether the returned shared network matches the
2242         // inserted shared network is to convert both to text.
2243         EXPECT_EQ(shared_network->toElement()->str(), network->toElement()->str());
2244     };
2245 
2246     {
2247         SCOPED_TRACE("testing various server selectors before update");
2248         test_get_network("all servers", ServerSelector::ALL());
2249         test_get_network("one server", ServerSelector::ONE("server1"));
2250         test_get_network("any server", ServerSelector::ANY());
2251     }
2252 
2253     {
2254         SCOPED_TRACE("CREATE audit entry for a shared network");
2255         testNewAuditEntry("dhcp6_shared_network",
2256                           AuditEntry::ModificationType::CREATE,
2257                           "shared network set");
2258     }
2259 
2260     // Update shared network in the database.
2261     shared_network = test_networks_[1];
2262     EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(),
2263                                                        shared_network));
2264 
2265     {
2266         SCOPED_TRACE("testing various server selectors after update");
2267         test_get_network("all servers after update", ServerSelector::ALL());
2268         test_get_network("one server after update", ServerSelector::ONE("server1"));
2269         test_get_network("any server after update", ServerSelector::ANY());
2270     }
2271 
2272     {
2273         SCOPED_TRACE("UPDATE audit entry for a shared network");
2274         testNewAuditEntry("dhcp6_shared_network",
2275                           AuditEntry::ModificationType::UPDATE,
2276                           "shared network set");
2277     }
2278 
2279     // The server2 specific shared network should not be returned if the
2280     // server selector is not matching.
2281     EXPECT_FALSE(cbptr_->getSharedNetwork6(ServerSelector::ALL(),
2282                                            shared_network2->getName()));
2283     EXPECT_FALSE(cbptr_->getSharedNetwork6(ServerSelector::ONE("server1"),
2284                                            shared_network2->getName()));
2285 
2286     {
2287         SCOPED_TRACE("testing selectors for server2 specific shared network");
2288         shared_network = shared_network2;
2289         test_get_network("one server", ServerSelector::ONE("server2"), "server2");
2290         test_get_network("any server", ServerSelector::ANY(), "server2");
2291     }
2292 }
2293 
2294 // Test that getSharedNetwork6 throws appropriate exceptions for various
2295 // server selectors.
TEST_F(MySqlConfigBackendDHCPv6Test,getSharedNetwork6Selectors)2296 TEST_F(MySqlConfigBackendDHCPv6Test, getSharedNetwork6Selectors) {
2297     // Supported selectors.
2298     EXPECT_NO_THROW(cbptr_->getSharedNetwork6(ServerSelector::ANY(), "level1"));
2299     EXPECT_NO_THROW(cbptr_->getSharedNetwork6(ServerSelector::UNASSIGNED(), "level1"));
2300     EXPECT_NO_THROW(cbptr_->getSharedNetwork6(ServerSelector::ALL(), "level1"));
2301     EXPECT_NO_THROW(cbptr_->getSharedNetwork6(ServerSelector::ONE("server1"), "level1"));
2302 
2303     // Not supported selectors.
2304     EXPECT_THROW(cbptr_->getSharedNetwork6(ServerSelector::MULTIPLE({ "server1", "server2" }),
2305                                            "level1"),
2306                  isc::InvalidOperation);
2307 }
2308 
2309 // Test that shared network may be created and updated and the server tags
2310 // are properly assigned to it.
TEST_F(MySqlConfigBackendDHCPv6Test,createUpdateSharedNetwork6)2311 TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateSharedNetwork6) {
2312     auto shared_network = test_networks_[0];
2313 
2314     // An attempt to insert the shared network for non-existing server should fail.
2315     EXPECT_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ONE("server1"),
2316                                                     shared_network),
2317                  NullKeyError);
2318 
2319     // Insert the server1 into the database.
2320     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[0]));
2321     {
2322         SCOPED_TRACE("CREATE audit entry for server");
2323         testNewAuditEntry("dhcp6_server",
2324                           AuditEntry::ModificationType::CREATE,
2325                           "server set");
2326     }
2327 
2328     // Insert the server2 into the database.
2329     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[2]));
2330     {
2331         SCOPED_TRACE("CREATE audit entry for server");
2332         testNewAuditEntry("dhcp6_server",
2333                           AuditEntry::ModificationType::CREATE,
2334                           "server set");
2335     }
2336 
2337     EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(),
2338                                                        shared_network));
2339     {
2340         SCOPED_TRACE("CREATE audit entry for shared network and ALL servers");
2341         testNewAuditEntry("dhcp6_shared_network",
2342                           AuditEntry::ModificationType::CREATE,
2343                           "shared network set");
2344     }
2345 
2346     EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::MULTIPLE({ "server1", "server2" }),
2347                                                        shared_network));
2348     {
2349         SCOPED_TRACE("UPDATE audit entry for shared network and MULTIPLE servers");
2350         testNewAuditEntry("dhcp6_shared_network",
2351                           AuditEntry::ModificationType::UPDATE,
2352                           "shared network set");
2353     }
2354 
2355     SharedNetwork6Ptr network;
2356     ASSERT_NO_THROW(network = cbptr_->getSharedNetwork6(ServerSelector::ANY(),
2357                                                         shared_network->getName()));
2358     ASSERT_TRUE(network);
2359     EXPECT_TRUE(network->hasServerTag(ServerTag("server1")));
2360     EXPECT_TRUE(network->hasServerTag(ServerTag("server2")));
2361     EXPECT_FALSE(network->hasServerTag(ServerTag()));
2362 }
2363 
2364 // Test that createUpdateSharedNetwork6 throws appropriate exceptions for various
2365 // server selectors.
TEST_F(MySqlConfigBackendDHCPv6Test,createUpdateSharedNetwork6Selectors)2366 TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateSharedNetwork6Selectors) {
2367     ASSERT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[0]));
2368     ASSERT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[2]));
2369 
2370     // Supported selectors.
2371     SharedNetwork6Ptr shared_network(new SharedNetwork6("all"));
2372     EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(),
2373                                                        shared_network));
2374     shared_network.reset(new SharedNetwork6("one"));
2375     EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ONE("server1"),
2376                                                        shared_network));
2377     shared_network.reset(new SharedNetwork6("multiple"));
2378     EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::MULTIPLE({ "server1", "server2" }),
2379                                                        shared_network));
2380 
2381     // Not supported server selectors.
2382     EXPECT_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ANY(), shared_network),
2383                  isc::InvalidOperation);
2384 
2385     // Not implemented server selectors.
2386     EXPECT_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::UNASSIGNED(),
2387                                                     shared_network),
2388                  isc::NotImplemented);
2389 }
2390 
2391 // Test that the information about unspecified optional parameters gets
2392 // propagated to the database.
TEST_F(MySqlConfigBackendDHCPv6Test,getSharedNetwork6WithOptionalUnspecified)2393 TEST_F(MySqlConfigBackendDHCPv6Test, getSharedNetwork6WithOptionalUnspecified) {
2394     // Insert new shared network.
2395     SharedNetwork6Ptr shared_network = test_networks_[2];
2396     cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(), shared_network);
2397 
2398     // Fetch this shared network by name.
2399     SharedNetwork6Ptr
2400         returned_network = cbptr_->getSharedNetwork6(ServerSelector::ALL(),
2401                                                      test_networks_[2]->getName());
2402     ASSERT_TRUE(returned_network);
2403 
2404     EXPECT_TRUE(returned_network->getIface().unspecified());
2405     EXPECT_TRUE(returned_network->getIface().empty());
2406 
2407     EXPECT_TRUE(returned_network->getClientClass().unspecified());
2408     EXPECT_TRUE(returned_network->getClientClass().empty());
2409 
2410     EXPECT_TRUE(returned_network->getValid().unspecified());
2411     EXPECT_EQ(0, returned_network->getValid().get());
2412 
2413     EXPECT_TRUE(returned_network->getPreferred().unspecified());
2414     EXPECT_EQ(0, returned_network->getPreferred().get());
2415 
2416     EXPECT_TRUE(returned_network->getT1().unspecified());
2417     EXPECT_EQ(0, returned_network->getT1().get());
2418 
2419     EXPECT_TRUE(returned_network->getT2().unspecified());
2420     EXPECT_EQ(0, returned_network->getT2().get());
2421 
2422     EXPECT_TRUE(returned_network->getReservationsGlobal().unspecified());
2423     EXPECT_FALSE(returned_network->getReservationsGlobal().get());
2424 
2425     EXPECT_TRUE(returned_network->getReservationsInSubnet().unspecified());
2426     EXPECT_TRUE(returned_network->getReservationsInSubnet().get());
2427 
2428     EXPECT_TRUE(returned_network->getReservationsOutOfPool().unspecified());
2429     EXPECT_FALSE(returned_network->getReservationsOutOfPool().get());
2430 
2431     EXPECT_TRUE(returned_network->getCalculateTeeTimes().unspecified());
2432     EXPECT_FALSE(returned_network->getCalculateTeeTimes().get());
2433 
2434     EXPECT_TRUE(returned_network->getT1Percent().unspecified());
2435     EXPECT_EQ(0.0, returned_network->getT1Percent().get());
2436 
2437     EXPECT_TRUE(returned_network->getT2Percent().unspecified());
2438     EXPECT_EQ(0.0, returned_network->getT2Percent().get());
2439 
2440     EXPECT_TRUE(returned_network->getRapidCommit().unspecified());
2441     EXPECT_FALSE(returned_network->getRapidCommit().get());
2442 
2443     EXPECT_FALSE(returned_network->getDdnsSendUpdates().unspecified());
2444     EXPECT_TRUE(returned_network->getDdnsSendUpdates().get());
2445 
2446     EXPECT_FALSE(returned_network->getDdnsOverrideNoUpdate().unspecified());
2447     EXPECT_TRUE(returned_network->getDdnsOverrideNoUpdate().get());
2448 
2449     EXPECT_FALSE(returned_network->getDdnsOverrideClientUpdate().unspecified());
2450     EXPECT_FALSE(returned_network->getDdnsOverrideClientUpdate().get());
2451 
2452     EXPECT_FALSE(returned_network->getDdnsReplaceClientNameMode().unspecified());
2453     EXPECT_EQ(D2ClientConfig::ReplaceClientNameMode::RCM_WHEN_PRESENT,
2454               returned_network->getDdnsReplaceClientNameMode().get());
2455 
2456     EXPECT_FALSE(returned_network->getDdnsGeneratedPrefix().unspecified());
2457     EXPECT_EQ("myhost", returned_network->getDdnsGeneratedPrefix().get());
2458 
2459     EXPECT_FALSE(returned_network->getDdnsQualifyingSuffix().unspecified());
2460     EXPECT_EQ("example.org", returned_network->getDdnsQualifyingSuffix().get());
2461 }
2462 
2463 // Test that deleteSharedNetworkSubnets6 with not ANY selector throw.
TEST_F(MySqlConfigBackendDHCPv6Test,deleteSharedNetworkSubnets6)2464 TEST_F(MySqlConfigBackendDHCPv6Test, deleteSharedNetworkSubnets6) {
2465     EXPECT_THROW(cbptr_->deleteSharedNetworkSubnets6(ServerSelector::UNASSIGNED(),
2466                                                      test_networks_[1]->getName()),
2467                  isc::InvalidOperation);
2468     EXPECT_THROW(cbptr_->deleteSharedNetworkSubnets6(ServerSelector::ALL(),
2469                                                      test_networks_[1]->getName()),
2470                  isc::InvalidOperation);
2471     EXPECT_THROW(cbptr_->deleteSharedNetworkSubnets6(ServerSelector::ONE("server1"),
2472                                                      test_networks_[1]->getName()),
2473                  isc::InvalidOperation);
2474     EXPECT_THROW(cbptr_->deleteSharedNetworkSubnets6(ServerSelector::MULTIPLE({ "server1", "server2" }),
2475                                                      test_networks_[1]->getName()),
2476                  isc::InvalidOperation);
2477 }
2478 
2479 // Test that all shared networks can be fetched.
TEST_F(MySqlConfigBackendDHCPv6Test,getAllSharedNetworks6)2480 TEST_F(MySqlConfigBackendDHCPv6Test, getAllSharedNetworks6) {
2481     // Insert test shared networks into the database. Note that the second shared
2482     // network will overwrite the first shared network as they use the same name.
2483     for (auto network : test_networks_) {
2484         cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(), network);
2485 
2486         // That shared network overrides the first one so the audit entry should
2487         // indicate an update.
2488         if ((network->getName() == "level1") && (!audit_entries_["all"].empty())) {
2489             SCOPED_TRACE("UPDATE audit entry for the shared network " +
2490                          network->getName());
2491             testNewAuditEntry("dhcp6_shared_network",
2492                               AuditEntry::ModificationType::UPDATE,
2493                               "shared network set");
2494 
2495         } else {
2496             SCOPED_TRACE("CREATE audit entry for the shared network " +
2497                          network->getName());
2498             testNewAuditEntry("dhcp6_shared_network",
2499                               AuditEntry::ModificationType::CREATE,
2500                               "shared network set");
2501         }
2502     }
2503 
2504     // Fetch all shared networks.
2505     SharedNetwork6Collection networks =
2506         cbptr_->getAllSharedNetworks6(ServerSelector::ALL());
2507     ASSERT_EQ(test_networks_.size() - 1, networks.size());
2508 
2509     // All shared networks should also be returned for explicitly specified
2510     // server tag.
2511     networks = cbptr_->getAllSharedNetworks6(ServerSelector::ONE("server1"));
2512     ASSERT_EQ(test_networks_.size() - 1, networks.size());
2513 
2514     // See if shared networks are returned ok.
2515     for (auto i = 0; i < networks.size(); ++i) {
2516         EXPECT_EQ(test_networks_[i + 1]->toElement()->str(),
2517                   networks[i]->toElement()->str());
2518         ASSERT_EQ(1, networks[i]->getServerTags().size());
2519         EXPECT_EQ("all", networks[i]->getServerTags().begin()->get());
2520     }
2521 
2522     // Add some subnets.
2523     test_networks_[1]->add(test_subnets_[0]);
2524     test_subnets_[2]->setSharedNetworkName("level2");
2525     test_networks_[2]->add(test_subnets_[3]);
2526     cbptr_->createUpdateSubnet6(ServerSelector::ALL(), test_subnets_[0]);
2527     cbptr_->createUpdateSubnet6(ServerSelector::ALL(), test_subnets_[2]);
2528     cbptr_->createUpdateSubnet6(ServerSelector::ALL(), test_subnets_[3]);
2529 
2530     // Both ways to attach a subnet are equivalent.
2531     Subnet6Ptr subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
2532                                            test_subnets_[0]->getID());
2533     ASSERT_TRUE(subnet);
2534     EXPECT_EQ("level1", subnet->getSharedNetworkName());
2535 
2536     {
2537         SCOPED_TRACE("CREATE audit entry for subnets");
2538         testNewAuditEntry("dhcp6_subnet",
2539                           AuditEntry::ModificationType::CREATE,
2540                           "subnet set", ServerSelector::ALL(), 3);
2541     }
2542 
2543     // Deleting non-existing shared network should return 0.
2544     EXPECT_EQ(0, cbptr_->deleteSharedNetwork6(ServerSelector::ALL(),
2545                                               "big-fish"));
2546     // All shared networks should be still there.
2547     ASSERT_EQ(test_networks_.size() - 1, networks.size());
2548 
2549     // Should not delete the shared network for explicit server tag
2550     // because our shared network is for all servers.
2551     EXPECT_EQ(0, cbptr_->deleteSharedNetwork6(ServerSelector::ONE("server1"),
2552                                               test_networks_[1]->getName()));
2553 
2554     // Same for all shared networks.
2555     EXPECT_EQ(0, cbptr_->deleteAllSharedNetworks6(ServerSelector::ONE("server1")));
2556 
2557     // Delete first shared network with it subnets and verify it is gone.
2558     // Begin by its subnet.
2559     EXPECT_EQ(1, cbptr_->deleteSharedNetworkSubnets6(ServerSelector::ANY(),
2560                                                      test_networks_[1]->getName()));
2561 
2562     {
2563         SCOPED_TRACE("DELETE audit entry for subnets of the first shared network");
2564         testNewAuditEntry("dhcp6_subnet",
2565                           AuditEntry::ModificationType::DELETE,
2566                           "deleted all subnets for a shared network");
2567     }
2568 
2569     // Check that the subnet is gone..
2570     subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
2571                                 test_subnets_[0]->getID());
2572     EXPECT_FALSE(subnet);
2573 
2574     // And after the shared network itself.
2575     EXPECT_EQ(1, cbptr_->deleteSharedNetwork6(ServerSelector::ALL(),
2576                                               test_networks_[1]->getName()));
2577     networks = cbptr_->getAllSharedNetworks6(ServerSelector::ALL());
2578     ASSERT_EQ(test_networks_.size() - 2, networks.size());
2579 
2580     {
2581         SCOPED_TRACE("DELETE audit entry for the first shared network");
2582         testNewAuditEntry("dhcp6_shared_network",
2583                           AuditEntry::ModificationType::DELETE,
2584                           "shared network deleted");
2585     }
2586 
2587     // Delete all.
2588     EXPECT_EQ(2, cbptr_->deleteAllSharedNetworks6(ServerSelector::ALL()));
2589     networks = cbptr_->getAllSharedNetworks6(ServerSelector::ALL());
2590     ASSERT_TRUE(networks.empty());
2591 
2592     {
2593         SCOPED_TRACE("DELETE audit entry for the remaining two shared networks");
2594         // The last parameter indicates that we expect two new audit entries.
2595         testNewAuditEntry("dhcp6_shared_network",
2596                           AuditEntry::ModificationType::DELETE,
2597                           "deleted all shared networks",
2598                           ServerSelector::ALL(), 2);
2599     }
2600 
2601     // Check that subnets are still there but detached.
2602     subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
2603                                 test_subnets_[2]->getID());
2604     ASSERT_TRUE(subnet);
2605     EXPECT_TRUE(subnet->getSharedNetworkName().empty());
2606     subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
2607                                 test_subnets_[3]->getID());
2608     ASSERT_TRUE(subnet);
2609     EXPECT_TRUE(subnet->getSharedNetworkName().empty());
2610 }
2611 
2612 // Test that getAllSharedNetworks6 throws appropriate exceptions for various
2613 // server selectors.
TEST_F(MySqlConfigBackendDHCPv6Test,getAllSharedNetworks6Selectors)2614 TEST_F(MySqlConfigBackendDHCPv6Test, getAllSharedNetworks6Selectors) {
2615     // Supported selectors.
2616     EXPECT_NO_THROW(cbptr_->getAllSharedNetworks6(ServerSelector::UNASSIGNED()));
2617     EXPECT_NO_THROW(cbptr_->getAllSharedNetworks6(ServerSelector::ALL()));
2618     EXPECT_NO_THROW(cbptr_->getAllSharedNetworks6(ServerSelector::ONE("server1")));
2619     EXPECT_NO_THROW(cbptr_->getAllSharedNetworks6(ServerSelector::MULTIPLE({ "server1", "server2" })));
2620 
2621     // Not supported selectors.
2622     EXPECT_THROW(cbptr_->getAllSharedNetworks6(ServerSelector::ANY()),
2623                  isc::InvalidOperation);
2624 }
2625 
2626 // Test that shared networks with different server associations are returned.
TEST_F(MySqlConfigBackendDHCPv6Test,getAllSharedNetworks6WithServerTags)2627 TEST_F(MySqlConfigBackendDHCPv6Test, getAllSharedNetworks6WithServerTags) {
2628     auto shared_network1 = test_networks_[0];
2629     auto shared_network2 = test_networks_[2];
2630     auto shared_network3 = test_networks_[3];
2631 
2632     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[0]));
2633     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[2]));
2634 
2635     EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(),
2636                                                        shared_network1));
2637     EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ONE("server1"),
2638                                                        shared_network2));
2639     EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::MULTIPLE({ "server1", "server2" }),
2640                                                        shared_network3));
2641 
2642     SharedNetwork6Collection networks;
2643 
2644     // All three networks are associated with the server1.
2645     EXPECT_NO_THROW(networks = cbptr_->getAllSharedNetworks6(ServerSelector::ONE("server1")));
2646     EXPECT_EQ(3, networks.size());
2647 
2648     // First network is associated with all servers.
2649     auto returned_network = SharedNetworkFetcher6::get(networks, "level1");
2650     ASSERT_TRUE(returned_network);
2651     EXPECT_TRUE(returned_network->hasAllServerTag());
2652     EXPECT_FALSE(returned_network->hasServerTag(ServerTag("server1")));
2653     EXPECT_FALSE(returned_network->hasServerTag(ServerTag("server2")));
2654 
2655     // Second network is only associated with the server1.
2656     returned_network = SharedNetworkFetcher6::get(networks, "level2");
2657     ASSERT_TRUE(returned_network);
2658     EXPECT_FALSE(returned_network->hasAllServerTag());
2659     EXPECT_TRUE(returned_network->hasServerTag(ServerTag("server1")));
2660     EXPECT_FALSE(returned_network->hasServerTag(ServerTag("server2")));
2661 
2662     // Third network is associated with both server1 and server2.
2663     returned_network = SharedNetworkFetcher6::get(networks, "level3");
2664     ASSERT_TRUE(returned_network);
2665     EXPECT_FALSE(returned_network->hasAllServerTag());
2666     EXPECT_TRUE(returned_network->hasServerTag(ServerTag("server1")));
2667     EXPECT_TRUE(returned_network->hasServerTag(ServerTag("server2")));
2668 
2669     // For server2 we should only get two shared networks, i.e. first and last.
2670     EXPECT_NO_THROW(networks = cbptr_->getAllSharedNetworks6(ServerSelector::ONE("server2")));
2671     EXPECT_EQ(2, networks.size());
2672 
2673     // First shared network is associated with all servers.
2674     returned_network = SharedNetworkFetcher6::get(networks, "level1");
2675     ASSERT_TRUE(returned_network);
2676     EXPECT_TRUE(returned_network->hasAllServerTag());
2677     EXPECT_FALSE(returned_network->hasServerTag(ServerTag("server1")));
2678     EXPECT_FALSE(returned_network->hasServerTag(ServerTag("server2")));
2679 
2680     // Last shared network is associated with server1 and server2.
2681     returned_network = SharedNetworkFetcher6::get(networks, "level3");
2682     ASSERT_TRUE(returned_network);
2683     EXPECT_FALSE(returned_network->hasAllServerTag());
2684     EXPECT_TRUE(returned_network->hasServerTag(ServerTag("server1")));
2685     EXPECT_TRUE(returned_network->hasServerTag(ServerTag("server2")));
2686 
2687     // Only the first shared network is associated with all servers.
2688     EXPECT_NO_THROW(networks = cbptr_->getAllSharedNetworks6(ServerSelector::ALL()));
2689     EXPECT_EQ(1, networks.size());
2690 
2691     returned_network = SharedNetworkFetcher6::get(networks, "level1");
2692     ASSERT_TRUE(returned_network);
2693     EXPECT_TRUE(returned_network->hasAllServerTag());
2694     EXPECT_FALSE(returned_network->hasServerTag(ServerTag("server1")));
2695     EXPECT_FALSE(returned_network->hasServerTag(ServerTag("server2")));
2696 }
2697 
2698 // Test that shared networks modified after given time can be fetched.
TEST_F(MySqlConfigBackendDHCPv6Test,getModifiedSharedNetworks6)2699 TEST_F(MySqlConfigBackendDHCPv6Test, getModifiedSharedNetworks6) {
2700     // Explicitly set timestamps of shared networks. First shared
2701     // network has a timestamp pointing to the future. Second shared
2702     // network has timestamp pointing to the past (yesterday).
2703     // Third shared network has a timestamp pointing to the
2704     // past (an hour ago).
2705     test_networks_[1]->setModificationTime(timestamps_["tomorrow"]);
2706     test_networks_[2]->setModificationTime(timestamps_["yesterday"]);
2707     test_networks_[3]->setModificationTime(timestamps_["today"]);
2708 
2709     // Insert shared networks into the database.
2710     for (int i = 1; i < test_networks_.size(); ++i) {
2711         cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(),
2712                                            test_networks_[i]);
2713     }
2714 
2715     // Fetch shared networks with timestamp later than today. Only one
2716     // shared network  should be returned.
2717     SharedNetwork6Collection
2718         networks = cbptr_->getModifiedSharedNetworks6(ServerSelector::ALL(),
2719                                                       timestamps_["after today"]);
2720     ASSERT_EQ(1, networks.size());
2721 
2722     // Fetch shared networks with timestamp later than yesterday. We
2723     // should get two shared networks.
2724     networks = cbptr_->getModifiedSharedNetworks6(ServerSelector::ALL(),
2725                                                  timestamps_["after yesterday"]);
2726     ASSERT_EQ(2, networks.size());
2727 
2728     // Fetch shared networks with timestamp later than tomorrow. Nothing
2729     // should be returned.
2730     networks = cbptr_->getModifiedSharedNetworks6(ServerSelector::ALL(),
2731                                                   timestamps_["after tomorrow"]);
2732     ASSERT_TRUE(networks.empty());
2733 }
2734 
2735 // Test that getModifiedSharedNetworks6 throws appropriate exceptions for various
2736 // server selectors.
TEST_F(MySqlConfigBackendDHCPv6Test,getModifiedSharedNetworks6Selectors)2737 TEST_F(MySqlConfigBackendDHCPv6Test, getModifiedSharedNetworks6Selectors) {
2738     // Supported selectors.
2739     EXPECT_NO_THROW(cbptr_->getModifiedSharedNetworks6(ServerSelector::UNASSIGNED(),
2740                                                        timestamps_["yesterday"]));
2741     EXPECT_NO_THROW(cbptr_->getModifiedSharedNetworks6(ServerSelector::ALL(),
2742                                                        timestamps_["yesterday"]));
2743     EXPECT_NO_THROW(cbptr_->getModifiedSharedNetworks6(ServerSelector::ONE("server1"),
2744                                                        timestamps_["yesterday"]));
2745     EXPECT_NO_THROW(cbptr_->getModifiedSharedNetworks6(ServerSelector::MULTIPLE({ "server1", "server2" }),
2746                                                        timestamps_["yesterday"]));
2747 
2748     // Not supported selectors.
2749     EXPECT_THROW(cbptr_->getModifiedSharedNetworks6(ServerSelector::ANY(),
2750                                                     timestamps_["yesterday"]),
2751                  isc::InvalidOperation);
2752 }
2753 
2754 // Test that selected shared network can be deleted.
TEST_F(MySqlConfigBackendDHCPv6Test,deleteSharedNetwork6)2755 TEST_F(MySqlConfigBackendDHCPv6Test, deleteSharedNetwork6) {
2756     // Create two servers in the database.
2757     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[0]));
2758     {
2759         SCOPED_TRACE("CREATE audit entry for server");
2760         testNewAuditEntry("dhcp6_server",
2761                           AuditEntry::ModificationType::CREATE,
2762                           "server set");
2763     }
2764 
2765     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[2]));
2766     {
2767         SCOPED_TRACE("CREATE audit entry for server");
2768         testNewAuditEntry("dhcp6_server",
2769                           AuditEntry::ModificationType::CREATE,
2770                           "server set");
2771     }
2772 
2773     auto shared_network1 = test_networks_[0];
2774     auto shared_network2 = test_networks_[2];
2775     auto shared_network3 = test_networks_[3];
2776 
2777     // Insert two shared networks, one for all servers, and one for server2.
2778     EXPECT_NO_THROW(
2779         cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(), shared_network1)
2780     );
2781     EXPECT_NO_THROW(
2782         cbptr_->createUpdateSharedNetwork6(ServerSelector::ONE("server2"), shared_network2)
2783     );
2784     EXPECT_NO_THROW(
2785         cbptr_->createUpdateSharedNetwork6(ServerSelector::MULTIPLE({ "server1", "server2" }),
2786                                            shared_network3)
2787     );
2788 
2789     auto test_no_delete = [this] (const std::string& test_case_name,
2790                                   const ServerSelector& server_selector,
2791                                   const SharedNetwork6Ptr& shared_network) {
2792         SCOPED_TRACE(test_case_name);
2793         uint64_t deleted_count = 0;
2794         EXPECT_NO_THROW(
2795             deleted_count = cbptr_->deleteSharedNetwork6(server_selector,
2796                                                          shared_network->getName())
2797         );
2798         EXPECT_EQ(0, deleted_count);
2799     };
2800 
2801     {
2802         SCOPED_TRACE("Test valid but non matching server selectors");
2803         test_no_delete("selector: one, actual: all", ServerSelector::ONE("server2"),
2804                        shared_network1);
2805         test_no_delete("selector: all, actual: one", ServerSelector::ALL(),
2806                        shared_network2);
2807         test_no_delete("selector: all, actual: multiple", ServerSelector::ALL(),
2808                        shared_network3);
2809     }
2810 
2811     // We are not going to support deletion of a single entry for multiple servers.
2812     EXPECT_THROW(cbptr_->deleteSharedNetwork6(ServerSelector::MULTIPLE({ "server1", "server2" }),
2813                                               shared_network3->getName()),
2814                  isc::InvalidOperation);
2815 
2816     // We currently don't support deleting a shared network with specifying
2817     // an unassigned server tag. Use ANY to delete any subnet instead.
2818     EXPECT_THROW(cbptr_->deleteSharedNetwork6(ServerSelector::UNASSIGNED(),
2819                                               shared_network1->getName()),
2820                  isc::NotImplemented);
2821 
2822     // Test successful deletion of a shared network.
2823     auto test_delete = [this] (const std::string& test_case_name,
2824                                const ServerSelector& server_selector,
2825                                const SharedNetwork6Ptr& shared_network) {
2826         SCOPED_TRACE(test_case_name);
2827         uint64_t deleted_count = 0;
2828         EXPECT_NO_THROW(
2829             deleted_count = cbptr_->deleteSharedNetwork6(server_selector,
2830                                                          shared_network->getName())
2831         );
2832         EXPECT_EQ(1, deleted_count);
2833 
2834         EXPECT_FALSE(cbptr_->getSharedNetwork6(server_selector,
2835                                                shared_network->getName()));
2836     };
2837 
2838     test_delete("all servers", ServerSelector::ALL(), shared_network1);
2839     test_delete("any server", ServerSelector::ANY(), shared_network2);
2840     test_delete("one server", ServerSelector::ONE("server1"), shared_network3);
2841 }
2842 
2843 // Test that deleteSharedNetwork6 throws appropriate exceptions for various
2844 // server selectors.
TEST_F(MySqlConfigBackendDHCPv6Test,deleteSharedNetwork6Selectors)2845 TEST_F(MySqlConfigBackendDHCPv6Test, deleteSharedNetwork6Selectors) {
2846     // Supported selectors.
2847     EXPECT_NO_THROW(cbptr_->deleteSharedNetwork6(ServerSelector::ANY(), "level1"));
2848     EXPECT_NO_THROW(cbptr_->deleteSharedNetwork6(ServerSelector::ALL(), "level1"));
2849     EXPECT_NO_THROW(cbptr_->deleteSharedNetwork6(ServerSelector::ONE("server1"), "level1"));
2850 
2851     // Not supported selectors.
2852     EXPECT_THROW(cbptr_->deleteSharedNetwork6(ServerSelector::MULTIPLE({ "server1", "server2" }),
2853                                            "level1"),
2854                  isc::InvalidOperation);
2855 
2856     // Not implemented selectors.
2857     EXPECT_THROW(cbptr_->deleteSharedNetwork6(ServerSelector::UNASSIGNED(), "level1"),
2858                  isc::NotImplemented);
2859 }
2860 
2861 // Test that deleteAllSharedNetworks6 throws appropriate exceptions for various
2862 // server selectors.
TEST_F(MySqlConfigBackendDHCPv6Test,deleteAllSharedNetworks6Selectors)2863 TEST_F(MySqlConfigBackendDHCPv6Test, deleteAllSharedNetworks6Selectors) {
2864     // Supported selectors.
2865     EXPECT_NO_THROW(cbptr_->deleteAllSharedNetworks6(ServerSelector::UNASSIGNED()));
2866     EXPECT_NO_THROW(cbptr_->deleteAllSharedNetworks6(ServerSelector::ALL()));
2867     EXPECT_NO_THROW(cbptr_->deleteAllSharedNetworks6(ServerSelector::ONE("server1")));
2868 
2869     // Not supported selectors.
2870     EXPECT_THROW(cbptr_->deleteAllSharedNetworks6(ServerSelector::ANY()),
2871                  isc::InvalidOperation);
2872     EXPECT_THROW(cbptr_->deleteAllSharedNetworks6(ServerSelector::MULTIPLE({ "server1", "server2" })),
2873                  isc::InvalidOperation);
2874 }
2875 
2876 // Test that it is possible to retrieve and delete orphaned shared network.
TEST_F(MySqlConfigBackendDHCPv6Test,unassignedSharedNetwork)2877 TEST_F(MySqlConfigBackendDHCPv6Test, unassignedSharedNetwork) {
2878     // Create the server.
2879     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[0]));
2880 
2881     // Create the shared networks and associate them with the server1.
2882     auto shared_network = test_networks_[0];
2883     auto shared_network2 = test_networks_[2];
2884     EXPECT_NO_THROW(
2885         cbptr_->createUpdateSharedNetwork6(ServerSelector::ONE("server1"), shared_network)
2886     );
2887     EXPECT_NO_THROW(
2888         cbptr_->createUpdateSharedNetwork6(ServerSelector::ONE("server1"), shared_network2)
2889     );
2890 
2891     // Delete the server. The shared networks should be preserved but are
2892     // considered orphaned, i.e. do not belong to any server.
2893     uint64_t deleted_count = 0;
2894     EXPECT_NO_THROW(deleted_count = cbptr_->deleteServer6(ServerTag("server1")));
2895     EXPECT_EQ(1, deleted_count);
2896 
2897     // Trying to fetch this shared network by server tag should return no result.
2898     SharedNetwork6Ptr returned_network;
2899     EXPECT_NO_THROW(returned_network = cbptr_->getSharedNetwork6(ServerSelector::ONE("server1"),
2900                                                                  "level1"));
2901     EXPECT_FALSE(returned_network);
2902 
2903     // The same if we use other calls.
2904     SharedNetwork6Collection returned_networks;
2905     EXPECT_NO_THROW(
2906         returned_networks = cbptr_->getAllSharedNetworks6(ServerSelector::ONE("server1"))
2907     );
2908     EXPECT_TRUE(returned_networks.empty());
2909 
2910     EXPECT_NO_THROW(
2911         returned_networks = cbptr_->getModifiedSharedNetworks6(ServerSelector::ONE("server1"),
2912                                                                timestamps_["two days ago"])
2913     );
2914     EXPECT_TRUE(returned_networks.empty());
2915 
2916     // We should get the shared network if we ask for unassigned.
2917     EXPECT_NO_THROW(returned_network = cbptr_->getSharedNetwork6(ServerSelector::UNASSIGNED(),
2918                                                                  "level1"));
2919     ASSERT_TRUE(returned_network);
2920 
2921     // Also if we ask for all unassigned networks it should be returned.
2922     EXPECT_NO_THROW(returned_networks = cbptr_->getAllSharedNetworks6(ServerSelector::UNASSIGNED()));
2923     ASSERT_EQ(2, returned_networks.size());
2924 
2925     // And all modified.
2926     EXPECT_NO_THROW(
2927         returned_networks = cbptr_->getModifiedSharedNetworks6(ServerSelector::UNASSIGNED(),
2928                                                                timestamps_["two days ago"])
2929     );
2930     ASSERT_EQ(2, returned_networks.size());
2931 
2932     // If we ask for any network by name, it should be returned too.
2933     EXPECT_NO_THROW(returned_network = cbptr_->getSharedNetwork6(ServerSelector::ANY(),
2934                                                                  "level1"));
2935     ASSERT_TRUE(returned_network);
2936 
2937     // Deleting a shared network with the mismatched server tag should not affect
2938     // our shared network.
2939     EXPECT_NO_THROW(
2940         deleted_count = cbptr_->deleteSharedNetwork6(ServerSelector::ONE("server1"),
2941                                                      "level1")
2942     );
2943     EXPECT_EQ(0, deleted_count);
2944 
2945     // Also, if we delete all shared networks for server1.
2946     EXPECT_NO_THROW(
2947         deleted_count = cbptr_->deleteAllSharedNetworks6(ServerSelector::ONE("server1"))
2948     );
2949     EXPECT_EQ(0, deleted_count);
2950 
2951     // We can delete this shared network when we specify ANY and the matching name.
2952     EXPECT_NO_THROW(
2953         deleted_count = cbptr_->deleteSharedNetwork6(ServerSelector::ANY(), "level1")
2954     );
2955     EXPECT_EQ(1, deleted_count);
2956 
2957     // We can delete all second networks using UNASSIGNED selector.
2958     EXPECT_NO_THROW(
2959         deleted_count = cbptr_->deleteAllSharedNetworks6(ServerSelector::UNASSIGNED());
2960     );
2961     EXPECT_EQ(1, deleted_count);
2962 }
2963 
2964 // Test that lifetimes in shared networks are handled as expected.
TEST_F(MySqlConfigBackendDHCPv6Test,sharedNetworkLifetime)2965 TEST_F(MySqlConfigBackendDHCPv6Test, sharedNetworkLifetime) {
2966     // Insert new shared network with unspecified valid lifetime
2967     SharedNetwork6Ptr network(new SharedNetwork6("foo"));
2968     Triplet<uint32_t> unspecified;
2969     network->setPreferred(unspecified);
2970     network->setValid(unspecified);
2971     network->setIface("eth1");
2972     cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(), network);
2973 
2974     // Fetch this shared network.
2975     SharedNetwork6Ptr returned_network =
2976         cbptr_->getSharedNetwork6(ServerSelector::ALL(), "foo");
2977     ASSERT_TRUE(returned_network);
2978 
2979     // Verified returned and original shared networks match.
2980     EXPECT_EQ(network->toElement()->str(),
2981               returned_network->toElement()->str());
2982 
2983     // Update the preferred and valid lifetime.
2984     network->setPreferred( Triplet<uint32_t>(100, 200, 300));
2985     network->setValid( Triplet<uint32_t>(200, 300, 400));
2986     cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(), network);
2987 
2988     // Fetch and verify again.
2989     returned_network = cbptr_->getSharedNetwork6(ServerSelector::ALL(), "foo");
2990     ASSERT_TRUE(returned_network);
2991     EXPECT_EQ(network->toElement()->str(),
2992               returned_network->toElement()->str());
2993 }
2994 
2995 // Test that deleting a shared network triggers deletion of the options
2996 // associated with the shared network.
TEST_F(MySqlConfigBackendDHCPv6Test,sharedNetworkOptions)2997 TEST_F(MySqlConfigBackendDHCPv6Test, sharedNetworkOptions) {
2998     // Add shared network with three options.
2999     EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(), test_networks_[0]));
3000     EXPECT_EQ(3, countRows("dhcp6_options"));
3001 
3002     // Add another shared network with a single option. The numnber of options in the
3003     // database should now be 4.
3004     EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(), test_networks_[2]));
3005     EXPECT_EQ(4, countRows("dhcp6_options"));
3006 
3007     // The second shared network uses the same name as the first shared network, so
3008     // this operation should replace the existing shared network and its options.
3009     EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(), test_networks_[1]));
3010     EXPECT_EQ(1, countRows("dhcp6_options"));
3011 
3012     // Remove the shared network. This should not affect options assigned to the
3013     // other shared network.
3014     EXPECT_NO_THROW(cbptr_->deleteSharedNetwork6(ServerSelector::ALL(),
3015                                                  test_networks_[1]->getName()));
3016     EXPECT_EQ(1, countRows("dhcp6_shared_network"));
3017     EXPECT_EQ(1, countRows("dhcp6_options"));
3018 
3019     // Create the first option again. The number of options should be equal to the
3020     // sum of options associated with both shared networks.
3021     EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(), test_networks_[0]));
3022     EXPECT_EQ(4, countRows("dhcp6_options"));
3023 
3024     // Delete this shared network. This should not affect the option associated
3025     // with the remaining shared network.
3026     EXPECT_NO_THROW(cbptr_->deleteSharedNetwork6(ServerSelector::ALL(),
3027                                                  test_networks_[0]->getName()));
3028     EXPECT_EQ(1, countRows("dhcp6_shared_network"));
3029     EXPECT_EQ(1, countRows("dhcp6_options"));
3030 }
3031 
3032 // Test that option definition can be inserted, fetched, updated and then
3033 // fetched again.
TEST_F(MySqlConfigBackendDHCPv6Test,getOptionDef6)3034 TEST_F(MySqlConfigBackendDHCPv6Test, getOptionDef6) {
3035     // Insert new option definition.
3036     OptionDefinitionPtr option_def = test_option_defs_[0];
3037     cbptr_->createUpdateOptionDef6(ServerSelector::ALL(), option_def);
3038 
3039     // Fetch this option_definition by subnet identifier.
3040     OptionDefinitionPtr returned_option_def =
3041         cbptr_->getOptionDef6(ServerSelector::ALL(),
3042                               test_option_defs_[0]->getCode(),
3043                               test_option_defs_[0]->getOptionSpaceName());
3044     ASSERT_TRUE(returned_option_def);
3045     EXPECT_GT(returned_option_def->getId(), 0);
3046     ASSERT_EQ(1, returned_option_def->getServerTags().size());
3047     EXPECT_EQ("all", returned_option_def->getServerTags().begin()->get());
3048 
3049     EXPECT_TRUE(returned_option_def->equals(*option_def));
3050 
3051     {
3052         SCOPED_TRACE("CREATE audit entry for an option definition");
3053         testNewAuditEntry("dhcp6_option_def",
3054                           AuditEntry::ModificationType::CREATE,
3055                           "option definition set");
3056     }
3057 
3058     // Update the option definition in the database.
3059     OptionDefinitionPtr option_def2 = test_option_defs_[1];
3060     cbptr_->createUpdateOptionDef6(ServerSelector::ALL(), option_def2);
3061 
3062     // Fetch updated option definition and see if it matches.
3063     returned_option_def = cbptr_->getOptionDef6(ServerSelector::ALL(),
3064                                                 test_option_defs_[1]->getCode(),
3065                                                 test_option_defs_[1]->getOptionSpaceName());
3066     EXPECT_TRUE(returned_option_def->equals(*option_def2));
3067 
3068     // Fetching option definition for an explicitly specified server tag
3069     // should succeed too.
3070     returned_option_def = cbptr_->getOptionDef6(ServerSelector::ONE("server1"),
3071                                                 test_option_defs_[1]->getCode(),
3072                                                 test_option_defs_[1]->getOptionSpaceName());
3073     EXPECT_TRUE(returned_option_def->equals(*option_def2));
3074 
3075     {
3076         SCOPED_TRACE("UPDATE audit entry for an option definition");
3077         testNewAuditEntry("dhcp6_option_def",
3078                           AuditEntry::ModificationType::UPDATE,
3079                           "option definition set");
3080     }
3081 }
3082 
3083 // This test verifies that it is possible to differentiate between the
3084 // option definitions by server tag and that the option definition
3085 // specified for the particular server overrides the definition for
3086 // all servers.
TEST_F(MySqlConfigBackendDHCPv6Test,optionDefs6WithServerTags)3087 TEST_F(MySqlConfigBackendDHCPv6Test, optionDefs6WithServerTags) {
3088     OptionDefinitionPtr option1 = test_option_defs_[0];
3089     OptionDefinitionPtr option2 = test_option_defs_[1];
3090     OptionDefinitionPtr option3 = test_option_defs_[4];
3091 
3092     // An attempt to create option definition for non-existing server should
3093     // fail.
3094     EXPECT_THROW(cbptr_->createUpdateOptionDef6(ServerSelector::ONE("server1"),
3095                                                 option1),
3096                  NullKeyError);
3097 
3098     // Create two servers.
3099     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[1]));
3100     {
3101         SCOPED_TRACE("server1 is created");
3102         testNewAuditEntry("dhcp6_server",
3103                           AuditEntry::ModificationType::CREATE,
3104                           "server set");
3105     }
3106 
3107     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[2]));
3108     {
3109         SCOPED_TRACE("server2 is created");
3110         testNewAuditEntry("dhcp6_server",
3111                           AuditEntry::ModificationType::CREATE,
3112                           "server set");
3113     }
3114 
3115     // This time creation of the option definition for the server1 should pass.
3116     EXPECT_NO_THROW(cbptr_->createUpdateOptionDef6(ServerSelector::ONE("server1"),
3117                                                    option1));
3118     {
3119         SCOPED_TRACE("option definition for server1 is set");
3120         // The value of 3 means there should be 3 audit entries available for the
3121         // server1, two that indicate creation of the servers and one that we
3122         // validate, which sets the option definition.
3123         testNewAuditEntry("dhcp6_option_def",
3124                           AuditEntry::ModificationType::CREATE,
3125                           "option definition set",
3126                           ServerSelector::ONE("server1"),
3127                           3, 1);
3128     }
3129 
3130     // Creation of the option definition for the server2 should also pass.
3131     EXPECT_NO_THROW(cbptr_->createUpdateOptionDef6(ServerSelector::ONE("server2"),
3132                                                    option2));
3133     {
3134         SCOPED_TRACE("option definition for server2 is set");
3135         // Same as in case of the server1, there should be 3 audit entries and
3136         // we validate one of them.
3137         testNewAuditEntry("dhcp6_option_def",
3138                           AuditEntry::ModificationType::CREATE,
3139                           "option definition set",
3140                           ServerSelector::ONE("server2"),
3141                           3, 1);
3142     }
3143 
3144     // Finally, creation of the option definition for all servers should
3145     // also pass.
3146     EXPECT_NO_THROW(cbptr_->createUpdateOptionDef6(ServerSelector::ALL(),
3147                                                    option3));
3148     {
3149         SCOPED_TRACE("option definition for server2 is set");
3150         // There should be one new audit entry for all servers. It logs
3151         // the insertion of the option definition.
3152         testNewAuditEntry("dhcp6_option_def",
3153                           AuditEntry::ModificationType::CREATE,
3154                           "option definition set",
3155                           ServerSelector::ALL(),
3156                           1, 1);
3157     }
3158 
3159     OptionDefinitionPtr returned_option_def;
3160 
3161     // Try to fetch the option definition specified for all servers. It should
3162     // return the third one.
3163     EXPECT_NO_THROW(
3164         returned_option_def = cbptr_->getOptionDef6(ServerSelector::ALL(),
3165                                                     option3->getCode(),
3166                                                     option3->getOptionSpaceName())
3167     );
3168     ASSERT_TRUE(returned_option_def);
3169     EXPECT_TRUE(returned_option_def->equals(*option3));
3170 
3171     // Try to fetch the option definition specified for server1. It should
3172     // override the definition for all servers.
3173     EXPECT_NO_THROW(
3174         returned_option_def = cbptr_->getOptionDef6(ServerSelector::ONE("server1"),
3175                                                     option1->getCode(),
3176                                                     option1->getOptionSpaceName())
3177     );
3178     ASSERT_TRUE(returned_option_def);
3179     EXPECT_TRUE(returned_option_def->equals(*option1));
3180 
3181     // The same in case of the server2.
3182     EXPECT_NO_THROW(
3183         returned_option_def = cbptr_->getOptionDef6(ServerSelector::ONE("server2"),
3184                                                     option2->getCode(),
3185                                                     option2->getOptionSpaceName())
3186     );
3187     ASSERT_TRUE(returned_option_def);
3188     EXPECT_TRUE(returned_option_def->equals(*option2));
3189 
3190     OptionDefContainer returned_option_defs;
3191 
3192     // Try to fetch the collection of the option definitions for server1, server2
3193     // and server3. The server3 does not have an explicit option definition, so
3194     // for this server we should get the definition associated with "all" servers.
3195     EXPECT_NO_THROW(
3196         returned_option_defs = cbptr_->getAllOptionDefs6(ServerSelector::
3197                                                          MULTIPLE({ "server1", "server2",
3198                                                                     "server3" }));
3199     );
3200     ASSERT_EQ(3, returned_option_defs.size());
3201 
3202     // Check that expected option definitions have been returned.
3203     auto current_option = returned_option_defs.begin();
3204     EXPECT_TRUE((*current_option)->equals(*option1));
3205     EXPECT_TRUE((*(++current_option))->equals(*option2));
3206     EXPECT_TRUE((*(++current_option))->equals(*option3));
3207 
3208     // Try to fetch the collection of options specified for all servers.
3209     // This excludes the options specific to server1 and server2. It returns
3210     // only the common ones.
3211     EXPECT_NO_THROW(
3212         returned_option_defs = cbptr_->getAllOptionDefs6(ServerSelector::ALL());
3213 
3214     );
3215     ASSERT_EQ(1, returned_option_defs.size());
3216     EXPECT_TRUE((*returned_option_defs.begin())->equals(*option3));
3217 
3218     // Delete the server1. It should remove associations of this server with the
3219     // option definitions and the option definition itself.
3220     EXPECT_NO_THROW(cbptr_->deleteServer6(ServerTag("server1")));
3221     EXPECT_NO_THROW(
3222         returned_option_defs = cbptr_->getAllOptionDefs6(ServerSelector::ONE("server1"));
3223 
3224     );
3225     ASSERT_EQ(1, returned_option_defs.size());
3226     EXPECT_TRUE((*returned_option_defs.begin())->equals(*option3));
3227 
3228     {
3229         SCOPED_TRACE("DELETE audit entry for the option definition after server deletion");
3230         testNewAuditEntry("dhcp6_option_def",
3231                           AuditEntry::ModificationType::DELETE,
3232                           "deleting a server", ServerSelector::ONE("server1"),
3233                           2, 1);
3234     }
3235 
3236     // Attempt to delete option definition for server1.
3237     uint64_t deleted_num = 0;
3238     EXPECT_NO_THROW(deleted_num = cbptr_->deleteOptionDef6(ServerSelector::ONE("server1"),
3239                                                            option1->getCode(),
3240                                                            option1->getOptionSpaceName()));
3241     EXPECT_EQ(0, deleted_num);
3242 
3243     // Deleting the existing option definition for server2 should succeed.
3244     EXPECT_NO_THROW(deleted_num = cbptr_->deleteOptionDef6(ServerSelector::ONE("server2"),
3245                                                            option2->getCode(),
3246                                                            option2->getOptionSpaceName()));
3247     EXPECT_EQ(1, deleted_num);
3248 
3249     // Create this option definition again to test that deletion of all servers
3250     // removes it too.
3251     EXPECT_NO_THROW(cbptr_->createUpdateOptionDef6(ServerSelector::ONE("server2"),
3252                                                    option2));
3253 
3254     // Delete all servers, except 'all'.
3255     EXPECT_NO_THROW(deleted_num = cbptr_->deleteAllServers6());
3256     EXPECT_NO_THROW(
3257         returned_option_defs = cbptr_->getAllOptionDefs6(ServerSelector::ALL());
3258     );
3259     EXPECT_EQ(1, deleted_num);
3260     EXPECT_EQ(1, returned_option_defs.size());
3261     EXPECT_TRUE((*returned_option_defs.begin())->equals(*option3));
3262 
3263     {
3264         SCOPED_TRACE("DELETE audit entry for the option definition after deletion of"
3265                      " all servers");
3266         testNewAuditEntry("dhcp6_option_def",
3267                           AuditEntry::ModificationType::DELETE,
3268                           "deleting all servers", ServerSelector::ONE("server2"),
3269                           4, 1);
3270     }
3271 }
3272 
3273 // Test that all option definitions can be fetched.
TEST_F(MySqlConfigBackendDHCPv6Test,getAllOptionDefs6)3274 TEST_F(MySqlConfigBackendDHCPv6Test, getAllOptionDefs6) {
3275     // Insert test option definitions into the database. Note that the second
3276     // option definition will overwrite the first option definition as they use
3277     // the same code and space.
3278     size_t updates_num = 0;
3279     for (auto option_def : test_option_defs_) {
3280         cbptr_->createUpdateOptionDef6(ServerSelector::ALL(), option_def);
3281 
3282         // That option definition overrides the first one so the audit entry should
3283         // indicate an update.
3284         if (option_def->getName() == "bar") {
3285             SCOPED_TRACE("UPDATE audit entry for the option definition " +
3286                          option_def->getName());
3287             testNewAuditEntry("dhcp6_option_def",
3288                               AuditEntry::ModificationType::UPDATE,
3289                               "option definition set");
3290             ++updates_num;
3291 
3292         } else {
3293             SCOPED_TRACE("CREATE audit entry for the option definition " +
3294                          option_def->getName());
3295             testNewAuditEntry("dhcp6_option_def",
3296                               AuditEntry::ModificationType::CREATE,
3297                               "option definition set");
3298         }
3299     }
3300 
3301     // Fetch all option_definitions.
3302     OptionDefContainer option_defs = cbptr_->getAllOptionDefs6(ServerSelector::ALL());
3303     ASSERT_EQ(test_option_defs_.size() - updates_num, option_defs.size());
3304 
3305     // All option definitions should also be returned for explicitly specified
3306     // server tag.
3307     option_defs = cbptr_->getAllOptionDefs6(ServerSelector::ONE("server1"));
3308     ASSERT_EQ(test_option_defs_.size() - updates_num, option_defs.size());
3309 
3310     // See if option definitions are returned ok.
3311     for (auto def = option_defs.begin(); def != option_defs.end(); ++def) {
3312         ASSERT_EQ(1, (*def)->getServerTags().size());
3313         EXPECT_EQ("all", (*def)->getServerTags().begin()->get());
3314         bool success = false;
3315         for (auto i = 1; i < test_option_defs_.size(); ++i) {
3316             if ((*def)->equals(*test_option_defs_[i])) {
3317                 success = true;
3318             }
3319         }
3320         ASSERT_TRUE(success) << "failed for option definition " << (*def)->getCode()
3321             << ", option space " << (*def)->getOptionSpaceName();
3322     }
3323 
3324     // Deleting non-existing option definition should return 0.
3325     EXPECT_EQ(0, cbptr_->deleteOptionDef6(ServerSelector::ALL(),
3326                                           99, "non-exiting-space"));
3327     // All option definitions should be still there.
3328     ASSERT_EQ(test_option_defs_.size() - updates_num, option_defs.size());
3329 
3330     // Should not delete option definition for explicit server tag
3331     // because our option definition is for all servers.
3332     EXPECT_EQ(0, cbptr_->deleteOptionDef6(ServerSelector::ONE("server1"),
3333                                           test_option_defs_[1]->getCode(),
3334                                           test_option_defs_[1]->getOptionSpaceName()));
3335 
3336     // Same for all option definitions.
3337     EXPECT_EQ(0, cbptr_->deleteAllOptionDefs6(ServerSelector::ONE("server1")));
3338 
3339     // Delete one of the option definitions and see if it is gone.
3340     EXPECT_EQ(1, cbptr_->deleteOptionDef6(ServerSelector::ALL(),
3341                                           test_option_defs_[2]->getCode(),
3342                                           test_option_defs_[2]->getOptionSpaceName()));
3343     ASSERT_FALSE(cbptr_->getOptionDef6(ServerSelector::ALL(),
3344                                        test_option_defs_[2]->getCode(),
3345                                        test_option_defs_[2]->getOptionSpaceName()));
3346 
3347     {
3348         SCOPED_TRACE("DELETE audit entry for the first option definition");
3349         testNewAuditEntry("dhcp6_option_def",
3350                           AuditEntry::ModificationType::DELETE,
3351                           "option definition deleted");
3352     }
3353 
3354     // Delete all remaining option definitions.
3355     EXPECT_EQ(2, cbptr_->deleteAllOptionDefs6(ServerSelector::ALL()));
3356     option_defs = cbptr_->getAllOptionDefs6(ServerSelector::ALL());
3357     ASSERT_TRUE(option_defs.empty());
3358 
3359     {
3360         SCOPED_TRACE("DELETE audit entries for the remaining option definitions");
3361         // The last parameter indicates that we expect two new audit entries.
3362         testNewAuditEntry("dhcp6_option_def",
3363                           AuditEntry::ModificationType::DELETE,
3364                           "deleted all option definitions",
3365                           ServerSelector::ALL(), 2);
3366     }
3367 }
3368 
3369 // Test that option definitions modified after given time can be fetched.
TEST_F(MySqlConfigBackendDHCPv6Test,getModifiedOptionDefs6)3370 TEST_F(MySqlConfigBackendDHCPv6Test, getModifiedOptionDefs6) {
3371     // Explicitly set timestamps of option definitions. First option
3372     // definition has a timestamp pointing to the future. Second option
3373     // definition has timestamp pointing to the past (yesterday).
3374     // Third option definitions has a timestamp pointing to the
3375     // past (an hour ago).
3376     test_option_defs_[1]->setModificationTime(timestamps_["tomorrow"]);
3377     test_option_defs_[2]->setModificationTime(timestamps_["yesterday"]);
3378     test_option_defs_[3]->setModificationTime(timestamps_["today"]);
3379 
3380     // Insert option definitions into the database.
3381     for (int i = 1; i < test_networks_.size(); ++i) {
3382         cbptr_->createUpdateOptionDef6(ServerSelector::ALL(),
3383                                        test_option_defs_[i]);
3384     }
3385 
3386     // Fetch option definitions with timestamp later than today. Only one
3387     // option definition should be returned.
3388     OptionDefContainer
3389         option_defs = cbptr_->getModifiedOptionDefs6(ServerSelector::ALL(),
3390                                                      timestamps_["after today"]);
3391     ASSERT_EQ(1, option_defs.size());
3392 
3393     // Fetch option definitions with timestamp later than yesterday. We
3394     // should get two option definitions.
3395     option_defs = cbptr_->getModifiedOptionDefs6(ServerSelector::ALL(),
3396                                                  timestamps_["after yesterday"]);
3397     ASSERT_EQ(2, option_defs.size());
3398 
3399     // Fetch option definitions with timestamp later than tomorrow. Nothing
3400     // should be returned.
3401     option_defs = cbptr_->getModifiedOptionDefs6(ServerSelector::ALL(),
3402                                               timestamps_["after tomorrow"]);
3403     ASSERT_TRUE(option_defs.empty());
3404 }
3405 
3406 // This test verifies that global option can be added, updated and deleted.
TEST_F(MySqlConfigBackendDHCPv6Test,createUpdateDeleteOption6)3407 TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateDeleteOption6) {
3408     // Add option to the database.
3409     OptionDescriptorPtr opt_posix_timezone = test_options_[0];
3410     cbptr_->createUpdateOption6(ServerSelector::ALL(),
3411                                 opt_posix_timezone);
3412 
3413     // Make sure we can retrieve this option and that it is equal to the
3414     // option we have inserted into the database.
3415     OptionDescriptorPtr returned_opt_posix_timezone =
3416         cbptr_->getOption6(ServerSelector::ALL(),
3417                            opt_posix_timezone->option_->getType(),
3418                            opt_posix_timezone->space_name_);
3419     ASSERT_TRUE(returned_opt_posix_timezone);
3420 
3421     {
3422         SCOPED_TRACE("verify created option");
3423         testOptionsEquivalent(*opt_posix_timezone,
3424                               *returned_opt_posix_timezone);
3425     }
3426 
3427     {
3428         SCOPED_TRACE("CREATE audit entry for an option");
3429         testNewAuditEntry("dhcp6_options",
3430                           AuditEntry::ModificationType::CREATE,
3431                           "global option set");
3432     }
3433 
3434     // Modify option and update it in the database.
3435     opt_posix_timezone->persistent_ = !opt_posix_timezone->persistent_;
3436     cbptr_->createUpdateOption6(ServerSelector::ALL(),
3437                                 opt_posix_timezone);
3438 
3439     // Retrieve the option again and make sure that updates were
3440     // properly propagated to the database. Use explicit server selector
3441     // which should also return this option.
3442     returned_opt_posix_timezone = cbptr_->getOption6(ServerSelector::ONE("server1"),
3443                                                      opt_posix_timezone->option_->getType(),
3444                                                      opt_posix_timezone->space_name_);
3445     ASSERT_TRUE(returned_opt_posix_timezone);
3446 
3447     {
3448         SCOPED_TRACE("verify updated option");
3449         testOptionsEquivalent(*opt_posix_timezone,
3450                               *returned_opt_posix_timezone);
3451     }
3452 
3453     {
3454         SCOPED_TRACE("UPDATE audit entry for an option");
3455         testNewAuditEntry("dhcp6_options",
3456                           AuditEntry::ModificationType::UPDATE,
3457                           "global option set");
3458     }
3459 
3460     // Deleting an option with explicitly specified server tag should fail.
3461     EXPECT_EQ(0, cbptr_->deleteOption6(ServerSelector::ONE("server1"),
3462                                        opt_posix_timezone->option_->getType(),
3463                                        opt_posix_timezone->space_name_));
3464 
3465     // Deleting option for all servers should succeed.
3466     EXPECT_EQ(1, cbptr_->deleteOption6(ServerSelector::ALL(),
3467                                        opt_posix_timezone->option_->getType(),
3468                                        opt_posix_timezone->space_name_));
3469 
3470     EXPECT_FALSE(cbptr_->getOption6(ServerSelector::ALL(),
3471                                     opt_posix_timezone->option_->getType(),
3472                                     opt_posix_timezone->space_name_));
3473 
3474     {
3475         SCOPED_TRACE("DELETE audit entry for an option");
3476         testNewAuditEntry("dhcp6_options",
3477                           AuditEntry::ModificationType::DELETE,
3478                           "global option deleted");
3479     }
3480 }
3481 
3482 // This test verifies that it is possible to differentiate between the
3483 // global options by server tag and that the option specified for the
3484 // particular server overrides the value specified for all servers.
TEST_F(MySqlConfigBackendDHCPv6Test,globalOptions6WithServerTags)3485 TEST_F(MySqlConfigBackendDHCPv6Test, globalOptions6WithServerTags) {
3486     OptionDescriptorPtr opt_timezone1 = test_options_[0];
3487     OptionDescriptorPtr opt_timezone2 = test_options_[6];
3488     OptionDescriptorPtr opt_timezone3 = test_options_[7];
3489 
3490     EXPECT_THROW(cbptr_->createUpdateOption6(ServerSelector::ONE("server1"),
3491                                              opt_timezone1),
3492                  NullKeyError);
3493 
3494     // Create two servers.
3495     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[1]));
3496     {
3497         SCOPED_TRACE("server1 is created");
3498         testNewAuditEntry("dhcp6_server",
3499                           AuditEntry::ModificationType::CREATE,
3500                           "server set");
3501     }
3502 
3503     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[2]));
3504     {
3505         SCOPED_TRACE("server2 is created");
3506         testNewAuditEntry("dhcp6_server",
3507                           AuditEntry::ModificationType::CREATE,
3508                           "server set");
3509     }
3510 
3511     EXPECT_NO_THROW(cbptr_->createUpdateOption6(ServerSelector::ONE("server1"),
3512                                                 opt_timezone1));
3513     {
3514         SCOPED_TRACE("global option for server1 is set");
3515         // The value of 3 means there should be 3 audit entries available for the
3516         // server1, two that indicate creation of the servers and one that we
3517         // validate, which sets the global option.
3518         testNewAuditEntry("dhcp6_options",
3519                           AuditEntry::ModificationType::CREATE,
3520                           "global option set",
3521                           ServerSelector::ONE("server1"),
3522                           3, 1);
3523 
3524     }
3525 
3526     EXPECT_NO_THROW(cbptr_->createUpdateOption6(ServerSelector::ONE("server2"),
3527                                                 opt_timezone2));
3528     {
3529         SCOPED_TRACE("global option for server2 is set");
3530         // Same as in case of the server1, there should be 3 audit entries and
3531         // we validate one of them.
3532         testNewAuditEntry("dhcp6_options",
3533                           AuditEntry::ModificationType::CREATE,
3534                           "global option set",
3535                           ServerSelector::ONE("server2"),
3536                           3, 1);
3537 
3538     }
3539 
3540     EXPECT_NO_THROW(cbptr_->createUpdateOption6(ServerSelector::ALL(),
3541                                                 opt_timezone3));
3542     {
3543         SCOPED_TRACE("global option for all servers is set");
3544         // There should be one new audit entry for all servers. It logs
3545         // the insertion of the global option.
3546         testNewAuditEntry("dhcp6_options",
3547                           AuditEntry::ModificationType::CREATE,
3548                           "global option set",
3549                           ServerSelector::ALL(),
3550                           1, 1);
3551 
3552     }
3553 
3554     OptionDescriptorPtr returned_option;
3555 
3556     // Try to fetch the option specified for all servers. It should return
3557     // the third option.
3558     EXPECT_NO_THROW(
3559         returned_option = cbptr_->getOption6(ServerSelector::ALL(),
3560                                              opt_timezone3->option_->getType(),
3561                                              opt_timezone3->space_name_);
3562     );
3563     ASSERT_TRUE(returned_option);
3564     testOptionsEquivalent(*opt_timezone3, *returned_option);
3565 
3566     // Try to fetch the option specified for the server1. It should override the
3567     // option specified for all servers.
3568     EXPECT_NO_THROW(
3569         returned_option = cbptr_->getOption6(ServerSelector::ONE("server1"),
3570                                              opt_timezone1->option_->getType(),
3571                                              opt_timezone1->space_name_);
3572     );
3573     ASSERT_TRUE(returned_option);
3574     testOptionsEquivalent(*opt_timezone1, *returned_option);
3575 
3576     // The same in case of the server2.
3577     EXPECT_NO_THROW(
3578         returned_option = cbptr_->getOption6(ServerSelector::ONE("server2"),
3579                                              opt_timezone2->option_->getType(),
3580                                              opt_timezone2->space_name_);
3581     );
3582     ASSERT_TRUE(returned_option);
3583     testOptionsEquivalent(*opt_timezone2, *returned_option);
3584 
3585     OptionContainer returned_options;
3586 
3587     // Try to fetch the collection of global options for the server1, server2
3588     // and server3. The server3 does not have an explicit value so for this server
3589     // we should get the option associated with "all" servers.
3590     EXPECT_NO_THROW(
3591         returned_options = cbptr_->getAllOptions6(ServerSelector::
3592                                                   MULTIPLE({ "server1", "server2",
3593                                                              "server3" }));
3594     );
3595     ASSERT_EQ(3, returned_options.size());
3596 
3597     // Check that expected options have been returned.
3598     auto current_option = returned_options.begin();
3599     testOptionsEquivalent(*opt_timezone1, *current_option);
3600     testOptionsEquivalent(*opt_timezone2, *(++current_option));
3601     testOptionsEquivalent(*opt_timezone3, *(++current_option));
3602 
3603     // Try to fetch the collection of options specified for all servers.
3604     // This excludes the options specific to server1 and server2. It returns
3605     // only the common ones.
3606     EXPECT_NO_THROW(
3607         returned_options = cbptr_->getAllOptions6(ServerSelector::ALL());
3608     );
3609     ASSERT_EQ(1, returned_options.size());
3610     testOptionsEquivalent(*opt_timezone3, *returned_options.begin());
3611 
3612     // Delete the server1. It should remove associations of this server with the
3613     // option and the option itself.
3614     EXPECT_NO_THROW(cbptr_->deleteServer6(ServerTag("server1")));
3615     EXPECT_NO_THROW(
3616         returned_options = cbptr_->getAllOptions6(ServerSelector::ONE("server1"));
3617     );
3618     ASSERT_EQ(1, returned_options.size());
3619     testOptionsEquivalent(*opt_timezone3, *returned_options.begin());
3620 
3621     {
3622         SCOPED_TRACE("DELETE audit entry for the global option after server deletion");
3623         testNewAuditEntry("dhcp6_options",
3624                           AuditEntry::ModificationType::DELETE,
3625                           "deleting a server", ServerSelector::ONE("server1"),
3626                           2, 1);
3627     }
3628 
3629     // Attempt to delete global option for server1.
3630     uint64_t deleted_num = 0;
3631     EXPECT_NO_THROW(deleted_num = cbptr_->deleteOption6(ServerSelector::ONE("server1"),
3632                                                         opt_timezone1->option_->getType(),
3633                                                         opt_timezone1->space_name_));
3634     EXPECT_EQ(0, deleted_num);
3635 
3636     // Deleting the existing option for server2 should succeed.
3637     EXPECT_NO_THROW(deleted_num = cbptr_->deleteOption6(ServerSelector::ONE("server2"),
3638                                                         opt_timezone2->option_->getType(),
3639                                                         opt_timezone2->space_name_));
3640     EXPECT_EQ(1, deleted_num);
3641 
3642     // Create this option again to test that deletion of all servers removes it too.
3643     EXPECT_NO_THROW(cbptr_->createUpdateOption6(ServerSelector::ONE("server2"),
3644                                                 opt_timezone2));
3645 
3646     // Delete all servers, except 'all'.
3647     EXPECT_NO_THROW(deleted_num = cbptr_->deleteAllServers6());
3648     EXPECT_NO_THROW(
3649         returned_options = cbptr_->getAllOptions6(ServerSelector::ALL());
3650     );
3651     EXPECT_EQ(1, deleted_num);
3652     ASSERT_EQ(1, returned_options.size());
3653     testOptionsEquivalent(*opt_timezone3, *returned_options.begin());
3654 
3655     {
3656         SCOPED_TRACE("DELETE audit entry for the global option after deletion of"
3657                      " all servers");
3658         testNewAuditEntry("dhcp6_options",
3659                           AuditEntry::ModificationType::DELETE,
3660                           "deleting all servers", ServerSelector::ONE("server2"),
3661                           4, 1);
3662     }
3663 }
3664 
3665 // This test verifies that all global options can be retrieved.
TEST_F(MySqlConfigBackendDHCPv6Test,getAllOptions6)3666 TEST_F(MySqlConfigBackendDHCPv6Test, getAllOptions6) {
3667     // Add three global options to the database.
3668     cbptr_->createUpdateOption6(ServerSelector::ALL(),
3669                                 test_options_[0]);
3670     cbptr_->createUpdateOption6(ServerSelector::ALL(),
3671                                 test_options_[1]);
3672     cbptr_->createUpdateOption6(ServerSelector::ALL(),
3673                                 test_options_[5]);
3674 
3675     // Retrieve all these options.
3676     OptionContainer returned_options = cbptr_->getAllOptions6(ServerSelector::ALL());
3677     ASSERT_EQ(3, returned_options.size());
3678 
3679     // Fetching global options with explicitly specified server tag should return
3680     // the same result.
3681     returned_options = cbptr_->getAllOptions6(ServerSelector::ONE("server1"));
3682     ASSERT_EQ(3, returned_options.size());
3683 
3684     // Get the container index used to search options by option code.
3685     const OptionContainerTypeIndex& index = returned_options.get<1>();
3686 
3687     // Verify that all options we put into the database were
3688     // returned.
3689     {
3690         SCOPED_TRACE("verify test_options_[0]");
3691         auto option0 = index.find(test_options_[0]->option_->getType());
3692         ASSERT_FALSE(option0 == index.end());
3693         testOptionsEquivalent(*test_options_[0], *option0);
3694         EXPECT_GT(option0->getId(), 0);
3695         ASSERT_EQ(1, option0->getServerTags().size());
3696         EXPECT_EQ("all", option0->getServerTags().begin()->get());
3697     }
3698 
3699     {
3700         SCOPED_TRACE("verify test_options_[1]");
3701         auto option1 = index.find(test_options_[1]->option_->getType());
3702         ASSERT_FALSE(option1 == index.end());
3703         testOptionsEquivalent(*test_options_[1], *option1);
3704         EXPECT_GT(option1->getId(), 0);
3705         ASSERT_EQ(1, option1->getServerTags().size());
3706         EXPECT_EQ("all", option1->getServerTags().begin()->get());
3707     }
3708 
3709     {
3710         SCOPED_TRACE("verify test_options_[5]");
3711         auto option5 = index.find(test_options_[5]->option_->getType());
3712         ASSERT_FALSE(option5 == index.end());
3713         testOptionsEquivalent(*test_options_[5], *option5);
3714         EXPECT_GT(option5->getId(), 0);
3715         ASSERT_EQ(1, option5->getServerTags().size());
3716         EXPECT_EQ("all", option5->getServerTags().begin()->get());
3717     }
3718 }
3719 
3720 // This test verifies that modified global options can be retrieved.
TEST_F(MySqlConfigBackendDHCPv6Test,getModifiedOptions6)3721 TEST_F(MySqlConfigBackendDHCPv6Test, getModifiedOptions6) {
3722     // Assign timestamps to the options we're going to store in the
3723     // database.
3724     test_options_[0]->setModificationTime(timestamps_["tomorrow"]);
3725     test_options_[1]->setModificationTime(timestamps_["yesterday"]);
3726     test_options_[5]->setModificationTime(timestamps_["today"]);
3727 
3728     // Put options into the database.
3729     cbptr_->createUpdateOption6(ServerSelector::ALL(),
3730                                 test_options_[0]);
3731     cbptr_->createUpdateOption6(ServerSelector::ALL(),
3732                                 test_options_[1]);
3733     cbptr_->createUpdateOption6(ServerSelector::ALL(),
3734                                 test_options_[5]);
3735 
3736     // Get options with the timestamp later than today. Only
3737     // one option should be returned.
3738     OptionContainer returned_options =
3739         cbptr_->getModifiedOptions6(ServerSelector::ALL(),
3740                                     timestamps_["after today"]);
3741     ASSERT_EQ(1, returned_options.size());
3742 
3743     // Fetching modified options with explicitly specified server selector
3744     // should return the same result.
3745     returned_options = cbptr_->getModifiedOptions6(ServerSelector::ONE("server1"),
3746                                                    timestamps_["after today"]);
3747     ASSERT_EQ(1, returned_options.size());
3748 
3749     // The returned option should be the one with the timestamp
3750     // set to tomorrow.
3751     const OptionContainerTypeIndex& index = returned_options.get<1>();
3752     auto option0 = index.find(test_options_[0]->option_->getType());
3753     ASSERT_FALSE(option0 == index.end());
3754     {
3755         SCOPED_TRACE("verify returned option");
3756         testOptionsEquivalent(*test_options_[0], *option0);
3757     }
3758 }
3759 
3760 // This test verifies that subnet level option can be added, updated and
3761 // deleted.
TEST_F(MySqlConfigBackendDHCPv6Test,createUpdateDeleteSubnetOption6)3762 TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateDeleteSubnetOption6) {
3763     // Insert new subnet.
3764     Subnet6Ptr subnet = test_subnets_[1];
3765     cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet);
3766 
3767     // Fetch this subnet by subnet identifier.
3768     Subnet6Ptr returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
3769                                                     subnet->getID());
3770     ASSERT_TRUE(returned_subnet);
3771 
3772     {
3773         SCOPED_TRACE("CREATE audit entry for a new subnet");
3774         testNewAuditEntry("dhcp6_subnet",
3775                           AuditEntry::ModificationType::CREATE,
3776                           "subnet set");
3777     }
3778 
3779     // The inserted subnet contains four options.
3780     ASSERT_EQ(4, countRows("dhcp6_options"));
3781 
3782     OptionDescriptorPtr opt_posix_timezone = test_options_[0];
3783     cbptr_->createUpdateOption6(ServerSelector::ANY(), subnet->getID(),
3784                                 opt_posix_timezone);
3785 
3786     returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
3787                                          subnet->getID());
3788     ASSERT_TRUE(returned_subnet);
3789 
3790     OptionDescriptor returned_opt_posix_timezone =
3791         returned_subnet->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_NEW_POSIX_TIMEZONE);
3792     ASSERT_TRUE(returned_opt_posix_timezone.option_);
3793 
3794     {
3795         SCOPED_TRACE("verify returned option");
3796         testOptionsEquivalent(*opt_posix_timezone, returned_opt_posix_timezone);
3797         EXPECT_GT(returned_opt_posix_timezone.getId(), 0);
3798     }
3799 
3800     {
3801         SCOPED_TRACE("UPDATE audit entry for an added subnet option");
3802         // Instead of adding an audit entry for an option we add an audit
3803         // entry for the entire subnet so as the server refreshes the
3804         // subnet with the new option. Note that the server doesn't
3805         // have means to retrieve only the newly added option.
3806         testNewAuditEntry("dhcp6_subnet",
3807                           AuditEntry::ModificationType::UPDATE,
3808                           "subnet specific option set");
3809     }
3810 
3811     // We have added one option to the existing subnet. We should now have
3812     // five options.
3813     ASSERT_EQ(5, countRows("dhcp6_options"));
3814 
3815     opt_posix_timezone->persistent_ = !opt_posix_timezone->persistent_;
3816     cbptr_->createUpdateOption6(ServerSelector::ANY(), subnet->getID(),
3817                                 opt_posix_timezone);
3818 
3819     returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
3820                                          subnet->getID());
3821     ASSERT_TRUE(returned_subnet);
3822     returned_opt_posix_timezone =
3823         returned_subnet->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_NEW_POSIX_TIMEZONE);
3824     ASSERT_TRUE(returned_opt_posix_timezone.option_);
3825 
3826     {
3827         SCOPED_TRACE("verify returned option with modified persistence");
3828         testOptionsEquivalent(*opt_posix_timezone, returned_opt_posix_timezone);
3829     }
3830 
3831     {
3832         SCOPED_TRACE("UPDATE audit entry for an updated subnet option");
3833         testNewAuditEntry("dhcp6_subnet",
3834                           AuditEntry::ModificationType::UPDATE,
3835                           "subnet specific option set");
3836     }
3837 
3838     // Updating the option should replace the existing instance with the new
3839     // instance. Therefore, we should still have five options.
3840     ASSERT_EQ(5, countRows("dhcp6_options"));
3841 
3842     // It should succeed for any server.
3843     EXPECT_EQ(1, cbptr_->deleteOption6(ServerSelector::ANY(), subnet->getID(),
3844                                        opt_posix_timezone->option_->getType(),
3845                                        opt_posix_timezone->space_name_));
3846 
3847     returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
3848                                          subnet->getID());
3849     ASSERT_TRUE(returned_subnet);
3850 
3851     EXPECT_FALSE(returned_subnet->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_NEW_POSIX_TIMEZONE).option_);
3852 
3853     {
3854         SCOPED_TRACE("UPDATE audit entry for a deleted subnet option");
3855         testNewAuditEntry("dhcp6_subnet",
3856                           AuditEntry::ModificationType::UPDATE,
3857                           "subnet specific option deleted");
3858     }
3859 
3860     // We should have only four options after deleting one of them.
3861     ASSERT_EQ(4, countRows("dhcp6_options"));
3862 }
3863 
3864 // This test verifies that option can be inserted, updated and deleted
3865 // from the pool.
TEST_F(MySqlConfigBackendDHCPv6Test,createUpdateDeletePoolOption6)3866 TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateDeletePoolOption6) {
3867     // Insert new subnet.
3868     Subnet6Ptr subnet = test_subnets_[1];
3869     cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet);
3870 
3871     {
3872         SCOPED_TRACE("CREATE audit entry for a subnet");
3873         testNewAuditEntry("dhcp6_subnet",
3874                           AuditEntry::ModificationType::CREATE,
3875                           "subnet set");
3876     }
3877 
3878     // Inserted subnet has four options.
3879     ASSERT_EQ(4, countRows("dhcp6_options"));
3880 
3881     // Add an option into the pool.
3882     const PoolPtr pool = subnet->getPool(Lease::TYPE_NA,
3883                                          IOAddress("2001:db8::10"));
3884     ASSERT_TRUE(pool);
3885     OptionDescriptorPtr opt_posix_timezone = test_options_[0];
3886     cbptr_->createUpdateOption6(ServerSelector::ANY(),
3887                                 pool->getFirstAddress(),
3888                                 pool->getLastAddress(),
3889                                 opt_posix_timezone);
3890 
3891     // Query for a subnet.
3892     Subnet6Ptr returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
3893                                                     subnet->getID());
3894     ASSERT_TRUE(returned_subnet);
3895 
3896     // The returned subnet should include our pool.
3897     const PoolPtr returned_pool = returned_subnet->getPool(Lease::TYPE_NA,
3898                                                            IOAddress("2001:db8::10"));
3899     ASSERT_TRUE(returned_pool);
3900 
3901     // The pool should contain option we added earlier.
3902     OptionDescriptor returned_opt_posix_timezone =
3903         returned_pool->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_NEW_POSIX_TIMEZONE);
3904     ASSERT_TRUE(returned_opt_posix_timezone.option_);
3905 
3906     {
3907         SCOPED_TRACE("verify returned pool option");
3908         testOptionsEquivalent(*opt_posix_timezone, returned_opt_posix_timezone);
3909         EXPECT_GT(returned_opt_posix_timezone.getId(), 0);
3910     }
3911 
3912     {
3913         SCOPED_TRACE("UPDATE audit entry for a subnet after adding an option "
3914                      "to the address pool");
3915         testNewAuditEntry("dhcp6_subnet",
3916                           AuditEntry::ModificationType::UPDATE,
3917                           "address pool specific option set");
3918     }
3919 
3920     // With the newly inserted option we should now have five options.
3921     ASSERT_EQ(5, countRows("dhcp6_options"));
3922 
3923     // Modify the option and update it in the database.
3924     opt_posix_timezone->persistent_ = !opt_posix_timezone->persistent_;
3925     cbptr_->createUpdateOption6(ServerSelector::ANY(),
3926                                 pool->getFirstAddress(),
3927                                 pool->getLastAddress(),
3928                                 opt_posix_timezone);
3929 
3930     // Fetch the subnet and the corresponding pool.
3931     returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
3932                                          subnet->getID());
3933     ASSERT_TRUE(returned_subnet);
3934     const PoolPtr returned_pool1 = returned_subnet->getPool(Lease::TYPE_NA,
3935                                                             IOAddress("2001:db8::10"));
3936     ASSERT_TRUE(returned_pool1);
3937 
3938     // Test that the option has been correctly updated in the database.
3939     returned_opt_posix_timezone =
3940         returned_pool1->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_NEW_POSIX_TIMEZONE);
3941     ASSERT_TRUE(returned_opt_posix_timezone.option_);
3942 
3943     {
3944         SCOPED_TRACE("verify updated option with modified persistence");
3945         testOptionsEquivalent(*opt_posix_timezone, returned_opt_posix_timezone);
3946     }
3947 
3948     {
3949         SCOPED_TRACE("UPDATE audit entry for a subnet when updating "
3950                      "address  pool specific option");
3951         testNewAuditEntry("dhcp6_subnet",
3952                           AuditEntry::ModificationType::UPDATE,
3953                           "address pool specific option set");
3954     }
3955 
3956     // The new option instance should replace the existing one, so we should
3957     // still have five options.
3958     ASSERT_EQ(5, countRows("dhcp6_options"));
3959 
3960     // Delete option for any server should succeed.
3961     EXPECT_EQ(1, cbptr_->deleteOption6(ServerSelector::ANY(),
3962                                        pool->getFirstAddress(),
3963                                        pool->getLastAddress(),
3964                                        opt_posix_timezone->option_->getType(),
3965                                        opt_posix_timezone->space_name_));
3966 
3967     // Fetch the subnet and the pool from the database again to make sure
3968     // that the option is really gone.
3969     returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
3970                                          subnet->getID());
3971     ASSERT_TRUE(returned_subnet);
3972     const PoolPtr returned_pool2 = returned_subnet->getPool(Lease::TYPE_NA,
3973                                                             IOAddress("2001:db8::10"));
3974     ASSERT_TRUE(returned_pool2);
3975 
3976     // Option should be gone.
3977     EXPECT_FALSE(returned_pool2->getCfgOption()->get(DHCP6_OPTION_SPACE,
3978                                                      D6O_NEW_POSIX_TIMEZONE).option_);
3979 
3980     {
3981         SCOPED_TRACE("UPDATE audit entry for a subnet when deleting "
3982                      "address pool specific option");
3983         testNewAuditEntry("dhcp6_subnet",
3984                           AuditEntry::ModificationType::UPDATE,
3985                           "address pool specific option deleted");
3986     }
3987 
3988     // The option has been deleted so the number of options should now
3989     // be down to 4.
3990     EXPECT_EQ(4, countRows("dhcp6_options"));
3991 }
3992 
3993 // This test verifies that option can be inserted, updated and deleted
3994 // from the pd pool.
TEST_F(MySqlConfigBackendDHCPv6Test,createUpdateDeletePdPoolOption6)3995 TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateDeletePdPoolOption6) {
3996     // Insert new subnet.
3997     Subnet6Ptr subnet = test_subnets_[1];
3998     cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet);
3999 
4000     {
4001         SCOPED_TRACE("CREATE audit entry for a subnet");
4002         testNewAuditEntry("dhcp6_subnet",
4003                           AuditEntry::ModificationType::CREATE,
4004                           "subnet set");
4005     }
4006 
4007     // Inserted subnet has four options.
4008     ASSERT_EQ(4, countRows("dhcp6_options"));
4009 
4010     // Add an option into the pd pool.
4011     const PoolPtr pd_pool = subnet->getPool(Lease::TYPE_PD,
4012                                             IOAddress("2001:db8:a:10::"));
4013     ASSERT_TRUE(pd_pool);
4014     OptionDescriptorPtr opt_posix_timezone = test_options_[0];
4015     int pd_pool_len = prefixLengthFromRange(pd_pool->getFirstAddress(),
4016                                             pd_pool->getLastAddress());
4017     cbptr_->createUpdateOption6(ServerSelector::ANY(),
4018                                 pd_pool->getFirstAddress(),
4019                                 static_cast<uint8_t>(pd_pool_len),
4020                                 opt_posix_timezone);
4021 
4022     // Query for a subnet.
4023     Subnet6Ptr returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
4024                                                     subnet->getID());
4025     ASSERT_TRUE(returned_subnet);
4026 
4027     // The returned subnet should include our pool.
4028     const PoolPtr returned_pd_pool =
4029         returned_subnet->getPool(Lease::TYPE_PD, IOAddress("2001:db8:a:10::"));
4030     ASSERT_TRUE(returned_pd_pool);
4031 
4032     // The pd pool should contain option we added earlier.
4033     OptionDescriptor returned_opt_posix_timezone =
4034         returned_pd_pool->getCfgOption()->get(DHCP6_OPTION_SPACE,
4035                                               D6O_NEW_POSIX_TIMEZONE);
4036     ASSERT_TRUE(returned_opt_posix_timezone.option_);
4037 
4038     {
4039         SCOPED_TRACE("verify returned pool option");
4040         testOptionsEquivalent(*opt_posix_timezone, returned_opt_posix_timezone);
4041         EXPECT_GT(returned_opt_posix_timezone.getId(), 0);
4042     }
4043 
4044     {
4045         SCOPED_TRACE("UPDATE audit entry for a subnet after adding an option "
4046                      "to the prefix delegation pool");
4047         testNewAuditEntry("dhcp6_subnet",
4048                           AuditEntry::ModificationType::UPDATE,
4049                           "prefix delegation pool specific option set");
4050     }
4051 
4052     // With the newly inserted option we should now have five options.
4053     ASSERT_EQ(5, countRows("dhcp6_options"));
4054 
4055     // Modify the option and update it in the database.
4056     opt_posix_timezone->persistent_ = !opt_posix_timezone->persistent_;
4057     cbptr_->createUpdateOption6(ServerSelector::ANY(),
4058                                 pd_pool->getFirstAddress(),
4059                                 static_cast<uint8_t>(pd_pool_len),
4060                                 opt_posix_timezone);
4061 
4062     // Fetch the subnet and the corresponding pd pool.
4063     returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
4064                                          subnet->getID());
4065     ASSERT_TRUE(returned_subnet);
4066     const PoolPtr returned_pd_pool1 =
4067         returned_subnet->getPool(Lease::TYPE_PD, IOAddress("2001:db8:a:10::"));
4068     ASSERT_TRUE(returned_pd_pool1);
4069 
4070     // Test that the option has been correctly updated in the database.
4071     returned_opt_posix_timezone =
4072         returned_pd_pool1->getCfgOption()->get(DHCP6_OPTION_SPACE,
4073                                                D6O_NEW_POSIX_TIMEZONE);
4074     ASSERT_TRUE(returned_opt_posix_timezone.option_);
4075 
4076     {
4077         SCOPED_TRACE("verify updated option with modified persistence");
4078         testOptionsEquivalent(*opt_posix_timezone, returned_opt_posix_timezone);
4079     }
4080 
4081     {
4082         SCOPED_TRACE("UPDATE audit entry for a subnet when updating "
4083                      "prefix delegation pool specific option");
4084         testNewAuditEntry("dhcp6_subnet",
4085                           AuditEntry::ModificationType::UPDATE,
4086                           "prefix delegation pool specific option set");
4087     }
4088 
4089     // The new option instance should replace the existing one, so we should
4090     // still have five options.
4091     ASSERT_EQ(5, countRows("dhcp6_options"));
4092 
4093     // Delete option for any server should succeed.
4094     EXPECT_EQ(1, cbptr_->deleteOption6(ServerSelector::ANY(),
4095                                        pd_pool->getFirstAddress(),
4096                                        static_cast<uint8_t>(pd_pool_len),
4097                                        opt_posix_timezone->option_->getType(),
4098                                        opt_posix_timezone->space_name_));
4099 
4100     // Fetch the subnet and the pool from the database again to make sure
4101     // that the option is really gone.
4102     returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
4103                                          subnet->getID());
4104     ASSERT_TRUE(returned_subnet);
4105     const PoolPtr returned_pd_pool2 =
4106         returned_subnet->getPool(Lease::TYPE_PD, IOAddress("2001:db8:a:10::"));
4107     ASSERT_TRUE(returned_pd_pool2);
4108 
4109     // Option should be gone.
4110     EXPECT_FALSE(returned_pd_pool2->getCfgOption()->get(DHCP6_OPTION_SPACE,
4111                                                         D6O_NEW_POSIX_TIMEZONE).option_);
4112 
4113     {
4114         SCOPED_TRACE("UPDATE audit entry for a subnet when deleting "
4115                      "prefix delegation pool specific option");
4116         testNewAuditEntry("dhcp6_subnet",
4117                           AuditEntry::ModificationType::UPDATE,
4118                           "prefix delegation pool specific option deleted");
4119     }
4120 
4121     // The option has been deleted so the number of options should now
4122     // be down to 4.
4123     EXPECT_EQ(4, countRows("dhcp6_options"));
4124 }
4125 
4126 // This test verifies that shared network level option can be added,
4127 // updated and deleted.
TEST_F(MySqlConfigBackendDHCPv6Test,createUpdateDeleteSharedNetworkOption6)4128 TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateDeleteSharedNetworkOption6) {
4129     // Insert new shared network.
4130     SharedNetwork6Ptr shared_network = test_networks_[1];
4131     cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(),
4132                                        shared_network);
4133 
4134     // Fetch this shared network by name.
4135     SharedNetwork6Ptr returned_network =
4136         cbptr_->getSharedNetwork6(ServerSelector::ALL(),
4137                                   shared_network->getName());
4138     ASSERT_TRUE(returned_network);
4139 
4140     {
4141         SCOPED_TRACE("CREATE audit entry for the new shared network");
4142         testNewAuditEntry("dhcp6_shared_network",
4143                           AuditEntry::ModificationType::CREATE,
4144                           "shared network set");
4145     }
4146 
4147     // The inserted shared network has no options.
4148     ASSERT_EQ(0, countRows("dhcp6_options"));
4149 
4150     OptionDescriptorPtr opt_posix_timezone = test_options_[0];
4151     cbptr_->createUpdateOption6(ServerSelector::ANY(),
4152                                 shared_network->getName(),
4153                                 opt_posix_timezone);
4154 
4155     returned_network = cbptr_->getSharedNetwork6(ServerSelector::ALL(),
4156                                                 shared_network->getName());
4157     ASSERT_TRUE(returned_network);
4158 
4159     OptionDescriptor returned_opt_posix_timezone =
4160         returned_network->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_NEW_POSIX_TIMEZONE);
4161     ASSERT_TRUE(returned_opt_posix_timezone.option_);
4162 
4163     {
4164         SCOPED_TRACE("verify returned option");
4165         testOptionsEquivalent(*opt_posix_timezone, returned_opt_posix_timezone);
4166         EXPECT_GT(returned_opt_posix_timezone.getId(), 0);
4167     }
4168 
4169     {
4170         SCOPED_TRACE("UPDATE audit entry for the added shared network option");
4171         // Instead of adding an audit entry for an option we add an audit
4172         // entry for the entire shared network so as the server refreshes the
4173         // shared network with the new option. Note that the server doesn't
4174         // have means to retrieve only the newly added option.
4175         testNewAuditEntry("dhcp6_shared_network",
4176                           AuditEntry::ModificationType::UPDATE,
4177                           "shared network specific option set");
4178     }
4179 
4180     // One option should now be stored in the database.
4181     ASSERT_EQ(1, countRows("dhcp6_options"));
4182 
4183     opt_posix_timezone->persistent_ = !opt_posix_timezone->persistent_;
4184     cbptr_->createUpdateOption6(ServerSelector::ANY(),
4185                                 shared_network->getName(),
4186                                 opt_posix_timezone);
4187 
4188     returned_network = cbptr_->getSharedNetwork6(ServerSelector::ALL(),
4189                                                  shared_network->getName());
4190     ASSERT_TRUE(returned_network);
4191     returned_opt_posix_timezone =
4192         returned_network->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_NEW_POSIX_TIMEZONE);
4193     ASSERT_TRUE(returned_opt_posix_timezone.option_);
4194 
4195     {
4196         SCOPED_TRACE("verify updated option with modified persistence");
4197         testOptionsEquivalent(*opt_posix_timezone, returned_opt_posix_timezone);
4198     }
4199 
4200     {
4201         SCOPED_TRACE("UPDATE audit entry for the updated shared network option");
4202         testNewAuditEntry("dhcp6_shared_network",
4203                           AuditEntry::ModificationType::UPDATE,
4204                           "shared network specific option set");
4205     }
4206 
4207     // The new option instance should replace the existing option instance,
4208     // so we should still have one option.
4209     ASSERT_EQ(1, countRows("dhcp6_options"));
4210 
4211     // Deleting an option for any server should succeed.
4212     EXPECT_EQ(1, cbptr_->deleteOption6(ServerSelector::ANY(),
4213                                        shared_network->getName(),
4214                                        opt_posix_timezone->option_->getType(),
4215                                        opt_posix_timezone->space_name_));
4216     returned_network = cbptr_->getSharedNetwork6(ServerSelector::ALL(),
4217                                                  shared_network->getName());
4218     ASSERT_TRUE(returned_network);
4219     EXPECT_FALSE(returned_network->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_NEW_POSIX_TIMEZONE).option_);
4220 
4221     {
4222         SCOPED_TRACE("UPDATE audit entry for the deleted shared network option");
4223         testNewAuditEntry("dhcp6_shared_network",
4224                           AuditEntry::ModificationType::UPDATE,
4225                           "shared network specific option deleted");
4226     }
4227 
4228     // After deleting the option we should be back to 0.
4229     EXPECT_EQ(0, countRows("dhcp6_options"));
4230 }
4231 
4232 // This test verifies that option id values in one subnet do
4233 // not impact options returned in subsequent subnets when
4234 // fetching subnets from the backend.
TEST_F(MySqlConfigBackendDHCPv6Test,subnetOptionIdOrder)4235 TEST_F(MySqlConfigBackendDHCPv6Test, subnetOptionIdOrder) {
4236 
4237     // Add a network with two pools with two options each.
4238     EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ALL(), test_subnets_[1]));
4239     EXPECT_EQ(2, countRows("dhcp6_pool"));
4240     EXPECT_EQ(4, countRows("dhcp6_options"));
4241 
4242     // Add second subnet with a single option. The number of options in the database
4243     // should now be 3.
4244     EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ALL(), test_subnets_[2]));
4245     EXPECT_EQ(2, countRows("dhcp6_pool"));
4246     EXPECT_EQ(5, countRows("dhcp6_options"));
4247 
4248     // Now replace the first subnet with three options and two pools.  This will cause
4249     // the option id values for this subnet to be larger than those in the second
4250     // subnet.
4251     EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ALL(), test_subnets_[0]));
4252     EXPECT_EQ(2, countRows("dhcp6_pool"));
4253     EXPECT_EQ(4, countRows("dhcp6_options"));
4254 
4255     // Now fetch all subnets.
4256     Subnet6Collection subnets;
4257     EXPECT_NO_THROW(subnets = cbptr_->getAllSubnets6(ServerSelector::ALL()));
4258     ASSERT_EQ(2, subnets.size());
4259 
4260     // Verify that the subnets returned are as expected.
4261     for (auto subnet : subnets) {
4262         ASSERT_EQ(1, subnet->getServerTags().size());
4263         EXPECT_EQ("all", subnet->getServerTags().begin()->get());
4264         if (subnet->getID() == 1024) {
4265             EXPECT_EQ(test_subnets_[0]->toElement()->str(), subnet->toElement()->str());
4266         } else if (subnet->getID() == 2048) {
4267             EXPECT_EQ(test_subnets_[2]->toElement()->str(), subnet->toElement()->str());
4268         } else {
4269             ADD_FAILURE() << "unexpected subnet id:" << subnet->getID();
4270         }
4271     }
4272 }
4273 
4274 // This test verifies that option id values in one shared network do
4275 // not impact options returned in subsequent shared networks when
4276 // fetching shared networks from the backend.
TEST_F(MySqlConfigBackendDHCPv6Test,sharedNetworkOptionIdOrder)4277 TEST_F(MySqlConfigBackendDHCPv6Test, sharedNetworkOptionIdOrder) {
4278     auto level1_options = test_networks_[0];
4279     auto level1_no_options = test_networks_[1];
4280     auto level2 = test_networks_[2];
4281 
4282     // Insert two shared networks. We insert level1 without options first,
4283     // then level2.
4284     EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(),
4285                                                        level1_no_options));
4286 
4287     EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(),
4288                                                        level2));
4289     // Fetch all shared networks.
4290     SharedNetwork6Collection networks =
4291         cbptr_->getAllSharedNetworks6(ServerSelector::ALL());
4292 
4293     ASSERT_EQ(2, networks.size());
4294 
4295     // See if shared networks are returned ok.
4296     for (auto i = 0; i < networks.size(); ++i) {
4297         if (i == 0) {
4298             // level1_no_options
4299             EXPECT_EQ(level1_no_options->toElement()->str(),
4300                   networks[i]->toElement()->str());
4301         } else {
4302             // bar
4303             EXPECT_EQ(level2->toElement()->str(),
4304                   networks[i]->toElement()->str());
4305         }
4306     }
4307 
4308     EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(),
4309                                                        level1_options));
4310 
4311     // Fetch all shared networks.
4312     networks = cbptr_->getAllSharedNetworks6(ServerSelector::ALL());
4313     ASSERT_EQ(2, networks.size());
4314 
4315     // See if shared networks are returned ok.
4316     for (auto i = 0; i < networks.size(); ++i) {
4317         if (i == 0) {
4318             // level1_no_options
4319             EXPECT_EQ(level1_options->toElement()->str(),
4320                   networks[i]->toElement()->str());
4321         } else {
4322             // bar
4323             EXPECT_EQ(level2->toElement()->str(),
4324                   networks[i]->toElement()->str());
4325         }
4326     }
4327 }
4328 
4329 // This test verifies that it is possible to create client classes, update them
4330 // and retrieve all classes for a given server.
TEST_F(MySqlConfigBackendDHCPv6Test,setAndGetAllClientClasses6)4331 TEST_F(MySqlConfigBackendDHCPv6Test, setAndGetAllClientClasses6) {
4332     // Create a server.
4333     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[0]));
4334     {
4335         SCOPED_TRACE("server1 is created");
4336         testNewAuditEntry("dhcp6_server",
4337                           AuditEntry::ModificationType::CREATE,
4338                           "server set",
4339                           ServerSelector::ONE("server1"));
4340     }
4341     // Create first class.
4342     auto class1 = test_client_classes_[0];
4343     class1->setTest("pkt6.msgtype == 1");
4344     ASSERT_NO_THROW_LOG(cbptr_->createUpdateClientClass6(ServerSelector::ALL(), class1, ""));
4345     {
4346         SCOPED_TRACE("client class foo is created");
4347         testNewAuditEntry("dhcp6_client_class",
4348                           AuditEntry::ModificationType::CREATE,
4349                           "client class set",
4350                           ServerSelector::ONE("server1"));
4351     }
4352     // Create second class.
4353     auto class2 = test_client_classes_[1];
4354     ASSERT_NO_THROW_LOG(cbptr_->createUpdateClientClass6(ServerSelector::ONE("server1"), class2, ""));
4355     {
4356         SCOPED_TRACE("client class bar is created");
4357         testNewAuditEntry("dhcp6_client_class",
4358                           AuditEntry::ModificationType::CREATE,
4359                           "client class set",
4360                           ServerSelector::ONE("server1"));
4361     }
4362     // Create third class.
4363     auto class3 = test_client_classes_[2];
4364     ASSERT_NO_THROW_LOG(cbptr_->createUpdateClientClass6(ServerSelector::ONE("server1"), class3, ""));
4365     {
4366         SCOPED_TRACE("client class foobar is created");
4367         testNewAuditEntry("dhcp6_client_class",
4368                           AuditEntry::ModificationType::CREATE,
4369                           "client class set",
4370                           ServerSelector::ONE("server1"));
4371     }
4372     // Update the third class to depend on the second class.
4373     class3->setTest("member('foo')");
4374     ASSERT_NO_THROW_LOG(cbptr_->createUpdateClientClass6(ServerSelector::ONE("server1"), class3, ""));
4375     {
4376         SCOPED_TRACE("client class bar is updated");
4377         testNewAuditEntry("dhcp6_client_class",
4378                           AuditEntry::ModificationType::UPDATE,
4379                           "client class set",
4380                           ServerSelector::ONE("server1"));
4381     }
4382     // Only the first class should be returned for the server selector ALL.
4383     auto client_classes = cbptr_->getAllClientClasses6(ServerSelector::ALL());
4384     ASSERT_EQ(1, client_classes.getClasses()->size());
4385     // All three classes should be returned for the server1.
4386     client_classes = cbptr_->getAllClientClasses6(ServerSelector::ONE("server1"));
4387     auto classes_list = client_classes.getClasses();
4388     ASSERT_EQ(3, classes_list->size());
4389     EXPECT_EQ("foo", (*classes_list->begin())->getName());
4390     EXPECT_EQ("bar", (*(classes_list->begin() + 1))->getName());
4391     EXPECT_EQ("foobar", (*(classes_list->begin() + 2))->getName());
4392 
4393     // Move the third class between the first and second class.
4394     ASSERT_NO_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ONE("server1"), class3, "foo"));
4395 
4396     // Ensure that the classes order has changed.
4397     client_classes = cbptr_->getAllClientClasses6(ServerSelector::ONE("server1"));
4398     classes_list = client_classes.getClasses();
4399     ASSERT_EQ(3, classes_list->size());
4400     EXPECT_EQ("foo", (*classes_list->begin())->getName());
4401     EXPECT_EQ("foobar", (*(classes_list->begin() + 1))->getName());
4402     EXPECT_EQ("bar", (*(classes_list->begin() + 2))->getName());
4403 
4404     // Update the foobar class without specifying its position. It should not
4405     // be moved.
4406     ASSERT_NO_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ONE("server1"), class3, ""));
4407 
4408     client_classes = cbptr_->getAllClientClasses6(ServerSelector::ONE("server1"));
4409     classes_list = client_classes.getClasses();
4410     ASSERT_EQ(3, classes_list->size());
4411     EXPECT_EQ("foo", (*classes_list->begin())->getName());
4412     EXPECT_EQ("foobar", (*(classes_list->begin() + 1))->getName());
4413     EXPECT_EQ("bar", (*(classes_list->begin() + 2))->getName());
4414 }
4415 
4416 // This test verifies that a single class can be retrieved from the database.
TEST_F(MySqlConfigBackendDHCPv6Test,getClientClass6)4417 TEST_F(MySqlConfigBackendDHCPv6Test, getClientClass6) {
4418     // Create a server.
4419     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[0]));
4420 
4421     // Add classes.
4422     auto class1 = test_client_classes_[0];
4423     EXPECT_NO_THROW(class1->getCfgOption()->add(test_options_[0]->option_,
4424                                                 test_options_[0]->persistent_,
4425                                                 test_options_[0]->space_name_));
4426     EXPECT_NO_THROW(class1->getCfgOption()->add(test_options_[1]->option_,
4427                                                 test_options_[1]->persistent_,
4428                                                 test_options_[1]->space_name_));
4429 
4430     EXPECT_NO_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ALL(), class1, ""));
4431 
4432     auto class2 = test_client_classes_[1];
4433     EXPECT_NO_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ONE("server1"), class2, ""));
4434 
4435     // Get the first client class and validate its contents.
4436     ClientClassDefPtr client_class;
4437     ASSERT_NO_THROW_LOG(client_class = cbptr_->getClientClass6(ServerSelector::ALL(), class1->getName()));
4438     ASSERT_TRUE(client_class);
4439     EXPECT_EQ("foo", client_class->getName());
4440     EXPECT_TRUE(client_class->getRequired());
4441     EXPECT_EQ(30, client_class->getValid().getMin());
4442     EXPECT_EQ(60, client_class->getValid().get());
4443     EXPECT_EQ(90, client_class->getValid().getMax());
4444 
4445     // Validate options belonging to this class.
4446     ASSERT_TRUE(client_class->getCfgOption());
4447     OptionDescriptor returned_opt_new_posix_timezone =
4448         client_class->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_NEW_POSIX_TIMEZONE);
4449     ASSERT_TRUE(returned_opt_new_posix_timezone.option_);
4450 
4451     OptionDescriptor returned_opt_preference =
4452         client_class->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_PREFERENCE);
4453     ASSERT_TRUE(returned_opt_preference.option_);
4454 
4455     // Fetch the same class using different server selectors.
4456     EXPECT_NO_THROW(client_class = cbptr_->getClientClass6(ServerSelector::ANY(),
4457                                                            class1->getName()));
4458     EXPECT_TRUE(client_class);
4459 
4460     EXPECT_NO_THROW(client_class = cbptr_->getClientClass6(ServerSelector::ONE("server1"),
4461                                                            class1->getName()));
4462     EXPECT_TRUE(client_class);
4463 
4464     EXPECT_NO_THROW(client_class = cbptr_->getClientClass6(ServerSelector::UNASSIGNED(),
4465                                                            class1->getName()));
4466     EXPECT_FALSE(client_class);
4467 
4468     // Fetch the second client class using different selectors. This time the
4469     // class should not be returned for the ALL server selector because it is
4470     // associated with the server1.
4471     EXPECT_NO_THROW(client_class = cbptr_->getClientClass6(ServerSelector::ALL(),
4472                                                            class2->getName()));
4473     EXPECT_FALSE(client_class);
4474 
4475     EXPECT_NO_THROW(client_class = cbptr_->getClientClass6(ServerSelector::ANY(),
4476                                                            class2->getName()));
4477     EXPECT_TRUE(client_class);
4478 
4479     EXPECT_NO_THROW(client_class = cbptr_->getClientClass6(ServerSelector::ONE("server1"),
4480                                                            class2->getName()));
4481     EXPECT_TRUE(client_class);
4482 
4483     EXPECT_NO_THROW(client_class = cbptr_->getClientClass6(ServerSelector::UNASSIGNED(),
4484                                                            class2->getName()));
4485     EXPECT_FALSE(client_class);
4486 }
4487 
4488 // This test verifies that client class specific DHCP options can be
4489 // modified during the class update.
TEST_F(MySqlConfigBackendDHCPv6Test,createUpdateClientClass6Options)4490 TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateClientClass6Options) {
4491     // Add class with two options and two option definitions.
4492     auto class1 = test_client_classes_[0];
4493     EXPECT_NO_THROW(class1->getCfgOption()->add(test_options_[0]->option_,
4494                                                 test_options_[0]->persistent_,
4495                                                 test_options_[0]->space_name_));
4496     EXPECT_NO_THROW(class1->getCfgOption()->add(test_options_[1]->option_,
4497                                                 test_options_[1]->persistent_,
4498                                                 test_options_[1]->space_name_));
4499     auto cfg_option_def = boost::make_shared<CfgOptionDef>();
4500     class1->setCfgOptionDef(cfg_option_def);
4501     EXPECT_NO_THROW(class1->getCfgOptionDef()->add(test_option_defs_[0]));
4502     EXPECT_NO_THROW(class1->getCfgOptionDef()->add(test_option_defs_[2]));
4503     EXPECT_NO_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ALL(), class1, ""));
4504 
4505     // Fetch the class and the options from the database.
4506     ClientClassDefPtr client_class;
4507     EXPECT_NO_THROW(client_class = cbptr_->getClientClass6(ServerSelector::ALL(), class1->getName()));
4508     ASSERT_TRUE(client_class);
4509 
4510     // Validate options belonging to the class.
4511     ASSERT_TRUE(client_class->getCfgOption());
4512     OptionDescriptor returned_opt_new_posix_timezone =
4513         client_class->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_NEW_POSIX_TIMEZONE);
4514     ASSERT_TRUE(returned_opt_new_posix_timezone.option_);
4515 
4516     OptionDescriptor returned_opt_preference =
4517         client_class->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_PREFERENCE);
4518     ASSERT_TRUE(returned_opt_preference.option_);
4519 
4520     // Validate option definitions belonging to the class.
4521     ASSERT_TRUE(client_class->getCfgOptionDef());
4522     auto returned_def_foo = client_class->getCfgOptionDef()->get(test_option_defs_[0]->getOptionSpaceName(),
4523                                                                  test_option_defs_[0]->getCode());
4524 
4525     ASSERT_TRUE(returned_def_foo);
4526     EXPECT_EQ(1234, returned_def_foo->getCode());
4527     EXPECT_EQ("foo", returned_def_foo->getName());
4528     EXPECT_EQ(DHCP6_OPTION_SPACE, returned_def_foo->getOptionSpaceName());
4529     EXPECT_EQ("espace", returned_def_foo->getEncapsulatedSpace());
4530     EXPECT_EQ(OPT_STRING_TYPE, returned_def_foo->getType());
4531     EXPECT_FALSE(returned_def_foo->getArrayType());
4532 
4533     auto returned_def_fish = client_class->getCfgOptionDef()->get(test_option_defs_[2]->getOptionSpaceName(),
4534                                                                   test_option_defs_[2]->getCode());
4535     ASSERT_TRUE(returned_def_fish);
4536     EXPECT_EQ(5235, returned_def_fish->getCode());
4537     EXPECT_EQ("fish", returned_def_fish->getName());
4538     EXPECT_EQ(DHCP6_OPTION_SPACE, returned_def_fish->getOptionSpaceName());
4539     EXPECT_TRUE(returned_def_fish->getEncapsulatedSpace().empty());
4540     EXPECT_EQ(OPT_RECORD_TYPE, returned_def_fish->getType());
4541     EXPECT_TRUE(returned_def_fish->getArrayType());
4542 
4543     // Replace client class specific option definitions. Leave only one option
4544     // definition.
4545     cfg_option_def = boost::make_shared<CfgOptionDef>();
4546     class1->setCfgOptionDef(cfg_option_def);
4547     EXPECT_NO_THROW(class1->getCfgOptionDef()->add(test_option_defs_[2]));
4548 
4549     // Delete one of the options and update the class.
4550     class1->getCfgOption()->del(test_options_[0]->space_name_,
4551                                 test_options_[0]->option_->getType());
4552     EXPECT_NO_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ALL(), class1, ""));
4553     EXPECT_NO_THROW(client_class = cbptr_->getClientClass6(ServerSelector::ALL(), class1->getName()));
4554     ASSERT_TRUE(client_class);
4555 
4556     // Ensure that the first option definition is gone.
4557     ASSERT_TRUE(client_class->getCfgOptionDef());
4558     returned_def_foo = client_class->getCfgOptionDef()->get(test_option_defs_[0]->getOptionSpaceName(),
4559                                                             test_option_defs_[0]->getCode());
4560     EXPECT_FALSE(returned_def_foo);
4561 
4562     // The second option definition should be present.
4563     returned_def_fish = client_class->getCfgOptionDef()->get(test_option_defs_[2]->getOptionSpaceName(),
4564                                                              test_option_defs_[2]->getCode());
4565     EXPECT_TRUE(returned_def_fish);
4566 
4567     // Make sure that the first option is gone.
4568     ASSERT_TRUE(client_class->getCfgOption());
4569     returned_opt_new_posix_timezone = client_class->getCfgOption()->get(DHCP6_OPTION_SPACE,
4570                                                                         D6O_NEW_POSIX_TIMEZONE);
4571     EXPECT_FALSE(returned_opt_new_posix_timezone.option_);
4572 
4573     // The second option should be there.
4574     returned_opt_preference = client_class->getCfgOption()->get(DHCP6_OPTION_SPACE,
4575                                                                 D6O_PREFERENCE);
4576     ASSERT_TRUE(returned_opt_preference.option_);
4577 }
4578 
4579 // This test verifies that modified client classes can be retrieved from the database.
TEST_F(MySqlConfigBackendDHCPv6Test,getModifiedClientClasses6)4580 TEST_F(MySqlConfigBackendDHCPv6Test, getModifiedClientClasses6) {
4581     // Create server1.
4582     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[0]));
4583 
4584     // Add three classes to the database with different timestamps.
4585     auto class1 = test_client_classes_[0];
4586     class1->setModificationTime(timestamps_["yesterday"]);
4587     EXPECT_NO_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ALL(), class1, ""));
4588 
4589     auto class2 = test_client_classes_[1];
4590     class2->setModificationTime(timestamps_["today"]);
4591     EXPECT_NO_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ALL(), class2, ""));
4592 
4593     auto class3 = test_client_classes_[2];
4594     class3->setModificationTime(timestamps_["tomorrow"]);
4595     EXPECT_NO_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ONE("server1"), class3, ""));
4596 
4597     // Get modified client classes configured for all servers.
4598     auto client_classes = cbptr_->getModifiedClientClasses6(ServerSelector::ALL(),
4599                                                             timestamps_["two days ago"]);
4600     EXPECT_EQ(2, client_classes.getClasses()->size());
4601 
4602     // Get modified client classes appropriate for server1. It includes classes
4603     // for all servers and for the server1.
4604     client_classes = cbptr_->getModifiedClientClasses6(ServerSelector::ONE("server1"),
4605                                                        timestamps_["two days ago"]);
4606     EXPECT_EQ(3, client_classes.getClasses()->size());
4607 
4608     // Get the classes again but use the timestamp equal to the modification
4609     // time of the first class.
4610     client_classes = cbptr_->getModifiedClientClasses6(ServerSelector::ONE("server1"),
4611                                                        timestamps_["yesterday"]);
4612     EXPECT_EQ(3, client_classes.getClasses()->size());
4613 
4614     // Get modified classes starting from today. It should return only two.
4615     client_classes = cbptr_->getModifiedClientClasses6(ServerSelector::ONE("server1"),
4616                                                        timestamps_["today"]);
4617     EXPECT_EQ(2, client_classes.getClasses()->size());
4618 
4619     // Get client classes modified in the future. It should return none.
4620     client_classes = cbptr_->getModifiedClientClasses6(ServerSelector::ONE("server1"),
4621                                                        timestamps_["after tomorrow"]);
4622     EXPECT_EQ(0, client_classes.getClasses()->size());
4623 
4624     // Getting modified client classes for any server is unsupported.
4625     EXPECT_THROW(cbptr_->getModifiedClientClasses6(ServerSelector::ANY(),
4626                                                    timestamps_["two days ago"]),
4627                  InvalidOperation);
4628 }
4629 
4630 // This test verifies that a specified client class can be deleted.
TEST_F(MySqlConfigBackendDHCPv6Test,deleteClientClass6)4631 TEST_F(MySqlConfigBackendDHCPv6Test, deleteClientClass6) {
4632     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[0]));
4633     {
4634         SCOPED_TRACE("server1 is created");
4635         testNewAuditEntry("dhcp6_server",
4636                           AuditEntry::ModificationType::CREATE,
4637                           "server set",
4638                           ServerSelector::ONE("server1"));
4639     }
4640     {
4641         SCOPED_TRACE("server1 is created");
4642         testNewAuditEntry("dhcp6_server",
4643                           AuditEntry::ModificationType::CREATE,
4644                           "server set",
4645                           ServerSelector::ONE("server2"));
4646     }
4647 
4648     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[2]));
4649     {
4650         SCOPED_TRACE("server1 is created and available for server1");
4651         testNewAuditEntry("dhcp6_server",
4652                           AuditEntry::ModificationType::CREATE,
4653                           "server set",
4654                           ServerSelector::ONE("server1"));
4655     }
4656     {
4657         SCOPED_TRACE("server1 is created and available for server2");
4658         testNewAuditEntry("dhcp6_server",
4659                           AuditEntry::ModificationType::CREATE,
4660                           "server set",
4661                           ServerSelector::ONE("server2"));
4662     }
4663 
4664     auto class1 = test_client_classes_[0];
4665     EXPECT_NO_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ALL(), class1, ""));
4666     {
4667         SCOPED_TRACE("client class foo is created and available for server1");
4668         testNewAuditEntry("dhcp6_client_class",
4669                           AuditEntry::ModificationType::CREATE,
4670                           "client class set",
4671                           ServerSelector::ONE("server1"));
4672     }
4673     {
4674         SCOPED_TRACE("client class foo is created and available for server 2");
4675         testNewAuditEntry("dhcp6_client_class",
4676                           AuditEntry::ModificationType::CREATE,
4677                           "client class set",
4678                           ServerSelector::ONE("server2"));
4679     }
4680 
4681     auto class2 = test_client_classes_[1];
4682     EXPECT_NO_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ONE("server1"), class2, ""));
4683     {
4684         SCOPED_TRACE("client class bar is created");
4685         testNewAuditEntry("dhcp6_client_class",
4686                           AuditEntry::ModificationType::CREATE,
4687                           "client class set",
4688                           ServerSelector::ONE("server1"));
4689     }
4690 
4691     auto class3 = test_client_classes_[2];
4692     class3->setTest("member('foo')");
4693     EXPECT_NO_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ONE("server2"), class3, ""));
4694     {
4695         SCOPED_TRACE("client class foobar is created");
4696         testNewAuditEntry("dhcp6_client_class",
4697                           AuditEntry::ModificationType::CREATE,
4698                           "client class set",
4699                           ServerSelector::ONE("server2"));
4700     }
4701 
4702     uint64_t result;
4703     EXPECT_NO_THROW(result = cbptr_->deleteClientClass6(ServerSelector::ONE("server1"),
4704                                                         class2->getName()));
4705     EXPECT_EQ(1, result);
4706     {
4707         SCOPED_TRACE("client class bar is deleted");
4708         testNewAuditEntry("dhcp6_client_class",
4709                           AuditEntry::ModificationType::DELETE,
4710                           "client class deleted",
4711                           ServerSelector::ONE("server1"));
4712     }
4713 
4714     EXPECT_NO_THROW(result = cbptr_->deleteClientClass6(ServerSelector::ONE("server2"),
4715                                                         class3->getName()));
4716     EXPECT_EQ(1, result);
4717     {
4718         SCOPED_TRACE("client class foobar is deleted");
4719         testNewAuditEntry("dhcp6_client_class",
4720                           AuditEntry::ModificationType::DELETE,
4721                           "client class deleted",
4722                           ServerSelector::ONE("server2"));
4723     }
4724 
4725     EXPECT_NO_THROW(result = cbptr_->deleteClientClass6(ServerSelector::ANY(),
4726                                                         class1->getName()));
4727     EXPECT_EQ(1, result);
4728     {
4729         SCOPED_TRACE("client class foo is deleted and no longer available for the server1");
4730         testNewAuditEntry("dhcp6_client_class",
4731                           AuditEntry::ModificationType::DELETE,
4732                           "client class deleted",
4733                           ServerSelector::ONE("server1"));
4734     }
4735     {
4736         SCOPED_TRACE("client class foo is deleted and no longer available for the server2");
4737         testNewAuditEntry("dhcp6_client_class",
4738                           AuditEntry::ModificationType::DELETE,
4739                           "client class deleted",
4740                           ServerSelector::ONE("server2"));
4741     }
4742 }
4743 
4744 // This test verifies that all client classes can be deleted using
4745 // a specified server selector.
TEST_F(MySqlConfigBackendDHCPv6Test,deleteAllClientClasses6)4746 TEST_F(MySqlConfigBackendDHCPv6Test, deleteAllClientClasses6) {
4747     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[0]));
4748     {
4749         SCOPED_TRACE("server1 is created");
4750         testNewAuditEntry("dhcp6_server",
4751                           AuditEntry::ModificationType::CREATE,
4752                           "server set",
4753                           ServerSelector::ONE("server1"));
4754     }
4755     {
4756         SCOPED_TRACE("server1 is created");
4757         testNewAuditEntry("dhcp6_server",
4758                           AuditEntry::ModificationType::CREATE,
4759                           "server set",
4760                           ServerSelector::ONE("server2"));
4761     }
4762 
4763     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[2]));
4764     {
4765         SCOPED_TRACE("server1 is created and available for server1");
4766         testNewAuditEntry("dhcp6_server",
4767                           AuditEntry::ModificationType::CREATE,
4768                           "server set",
4769                           ServerSelector::ONE("server1"));
4770     }
4771     {
4772         SCOPED_TRACE("server1 is created and available for server2");
4773         testNewAuditEntry("dhcp6_server",
4774                           AuditEntry::ModificationType::CREATE,
4775                           "server set",
4776                           ServerSelector::ONE("server2"));
4777     }
4778 
4779     auto class1 = test_client_classes_[0];
4780     EXPECT_NO_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ALL(), class1, ""));
4781     {
4782         SCOPED_TRACE("client class foo is created and available for server1");
4783         testNewAuditEntry("dhcp6_client_class",
4784                           AuditEntry::ModificationType::CREATE,
4785                           "client class set",
4786                           ServerSelector::ONE("server1"));
4787     }
4788     {
4789         SCOPED_TRACE("client class foo is created and available for server 2");
4790         testNewAuditEntry("dhcp6_client_class",
4791                           AuditEntry::ModificationType::CREATE,
4792                           "client class set",
4793                           ServerSelector::ONE("server2"));
4794     }
4795 
4796     auto class2 = test_client_classes_[1];
4797     EXPECT_NO_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ONE("server1"), class2, ""));
4798     {
4799         SCOPED_TRACE("client class bar is created");
4800         testNewAuditEntry("dhcp6_client_class",
4801                           AuditEntry::ModificationType::CREATE,
4802                           "client class set",
4803                           ServerSelector::ONE("server1"));
4804     }
4805 
4806     auto class3 = test_client_classes_[2];
4807     class3->setTest("member('foo')");
4808     EXPECT_NO_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ONE("server2"), class3, ""));
4809     {
4810         SCOPED_TRACE("client class foobar is created");
4811         testNewAuditEntry("dhcp6_client_class",
4812                           AuditEntry::ModificationType::CREATE,
4813                           "client class set",
4814                           ServerSelector::ONE("server2"));
4815     }
4816 
4817     uint64_t result;
4818 
4819     EXPECT_NO_THROW(result = cbptr_->deleteAllClientClasses6(ServerSelector::UNASSIGNED()));
4820     EXPECT_EQ(0, result);
4821 
4822     EXPECT_NO_THROW(result = cbptr_->deleteAllClientClasses6(ServerSelector::ONE("server2")));
4823     EXPECT_EQ(1, result);
4824     {
4825         SCOPED_TRACE("client classes for server2 deleted");
4826         testNewAuditEntry("dhcp6_client_class",
4827                           AuditEntry::ModificationType::DELETE,
4828                           "deleted all client classes",
4829                           ServerSelector::ONE("server2"));
4830     }
4831 
4832     EXPECT_NO_THROW(result = cbptr_->deleteAllClientClasses6(ServerSelector::ONE("server2")));
4833     EXPECT_EQ(0, result);
4834 
4835     EXPECT_NO_THROW(result = cbptr_->deleteAllClientClasses6(ServerSelector::ONE("server1")));
4836     EXPECT_EQ(1, result);
4837     {
4838         SCOPED_TRACE("client classes for server1 deleted");
4839         testNewAuditEntry("dhcp6_client_class",
4840                           AuditEntry::ModificationType::DELETE,
4841                           "deleted all client classes",
4842                           ServerSelector::ONE("server1"));
4843     }
4844 
4845     EXPECT_NO_THROW(result = cbptr_->deleteAllClientClasses6(ServerSelector::ONE("server1")));
4846     EXPECT_EQ(0, result);
4847 
4848     EXPECT_NO_THROW(result = cbptr_->deleteAllClientClasses6(ServerSelector::ALL()));
4849     EXPECT_EQ(1, result);
4850     {
4851         SCOPED_TRACE("client classes for all deleted");
4852         testNewAuditEntry("dhcp6_client_class",
4853                           AuditEntry::ModificationType::DELETE,
4854                           "deleted all client classes",
4855                           ServerSelector::ONE("server1"));
4856     }
4857 
4858     EXPECT_NO_THROW(result = cbptr_->deleteAllClientClasses6(ServerSelector::ALL()));
4859     EXPECT_EQ(0, result);
4860 
4861     // Deleting multiple objects using ANY server tag is unsupported.
4862     EXPECT_THROW(cbptr_->deleteAllClientClasses6(ServerSelector::ANY()), InvalidOperation);
4863 }
4864 
4865 // This test verifies that client class dependencies are tracked when the
4866 // classes are added to the database. It verifies that an attempt to update
4867 // a class violating the dependencies results in an error.
TEST_F(MySqlConfigBackendDHCPv6Test,clientClassDependencies6)4868 TEST_F(MySqlConfigBackendDHCPv6Test, clientClassDependencies6) {
4869     // Create a server.
4870     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[0]));
4871 
4872     // Create first class. It depends on KNOWN built-in class.
4873     auto class1 = test_client_classes_[0];
4874     class1->setTest("member('KNOWN')");
4875     EXPECT_NO_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ALL(), class1, ""));
4876 
4877     // Create second class which depends on the first class. This yelds indirect
4878     // dependency on KNOWN class.
4879     auto class2 = test_client_classes_[1];
4880     class2->setTest("member('foo')");
4881     EXPECT_NO_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ALL(), class2, ""));
4882 
4883     // Create third class depending on the second class. This also yelds indirect
4884     // dependency on KNOWN class.
4885     auto class3 = test_client_classes_[2];
4886     class3->setTest("member('bar')");
4887     EXPECT_NO_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ALL(), class3, ""));
4888 
4889     // An attempt to move the first class to the end of the class hierarchy should
4890     // fail because other classes depend on it.
4891     EXPECT_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ALL(), class1, "bar"),
4892                  DbOperationError);
4893 
4894     // Try to change the dependency of the first class. There are other classes
4895     // having indirect dependency on KNOWN class via this class. Therefore, the
4896     // update should be unsuccessful.
4897     class1->setTest("member('HA_server1')");
4898     EXPECT_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ALL(), class1, ""),
4899                  DbOperationError);
4900 
4901     // Try to change the dependency of the second class. This should result in
4902     // an error because the third class depends on it.
4903     class2->setTest("member('HA_server1')");
4904     EXPECT_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ALL(), class2, ""),
4905                  DbOperationError);
4906 
4907     // Changing the indirect dependency of the third class should succeed, because
4908     // no other classes depend on this class.
4909     class3->setTest("member('HA_server1')");
4910     EXPECT_NO_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ALL(), class3, ""));
4911 }
4912 
4913 /// This test verifies that audit entries can be retrieved from a given
4914 /// timestamp and id including when two entries can get the same timestamp.
4915 /// (either it is a common even and this should catch it, or it is a rare
4916 /// event and it does not matter).
TEST_F(MySqlConfigBackendDHCPv6Test,multipleAuditEntries)4917 TEST_F(MySqlConfigBackendDHCPv6Test, multipleAuditEntries) {
4918     // Get current time.
4919     boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
4920 
4921     // Create a server.
4922     EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[1]));
4923 
4924     // Create a global parameter and update it many times.
4925     const ServerSelector& server_selector = ServerSelector::ALL();
4926     StampedValuePtr param;
4927     ElementPtr value;
4928     for (int i = 0; i < 100; ++i) {
4929         value = Element::create(i);
4930         param = StampedValue::create("my-parameter", value);
4931         cbptr_->createUpdateGlobalParameter6(server_selector, param);
4932     }
4933 
4934     // Get all audit entries from now.
4935     AuditEntryCollection audit_entries =
4936         cbptr_->getRecentAuditEntries(server_selector, now, 0);
4937 
4938     // Check that partial retrieves return the right count.
4939     auto& mod_time_idx = audit_entries.get<AuditEntryModificationTimeIdTag>();
4940     for (auto it = mod_time_idx.begin(); it != mod_time_idx.end(); ++it) {
4941         size_t partial_size =
4942             cbptr_->getRecentAuditEntries(server_selector,
4943                                           (*it)->getModificationTime(),
4944                                           (*it)->getRevisionId()).size();
4945         EXPECT_EQ(partial_size + 1,
4946                   std::distance(it, mod_time_idx.end()));
4947     }
4948 }
4949 
4950 class MySqlConfigBackendDHCPv6DbLostCallbackTest : public ::testing::Test {
4951 public:
MySqlConfigBackendDHCPv6DbLostCallbackTest()4952     MySqlConfigBackendDHCPv6DbLostCallbackTest()
4953         : db_lost_callback_called_(0), db_recovered_callback_called_(0),
4954           db_failed_callback_called_(0),
4955           io_service_(boost::make_shared<isc::asiolink::IOService>()) {
4956         isc::db::DatabaseConnection::db_lost_callback_ = 0;
4957         isc::db::DatabaseConnection::db_recovered_callback_ = 0;
4958         isc::db::DatabaseConnection::db_failed_callback_ = 0;
4959         isc::dhcp::MySqlConfigBackendImpl::setIOService(io_service_);
4960         isc::dhcp::TimerMgr::instance()->setIOService(io_service_);
4961         isc::dhcp::CfgMgr::instance().clear();
4962     }
4963 
~MySqlConfigBackendDHCPv6DbLostCallbackTest()4964     virtual ~MySqlConfigBackendDHCPv6DbLostCallbackTest() {
4965         isc::db::DatabaseConnection::db_lost_callback_ = 0;
4966         isc::db::DatabaseConnection::db_recovered_callback_ = 0;
4967         isc::db::DatabaseConnection::db_failed_callback_ = 0;
4968         isc::dhcp::MySqlConfigBackendImpl::setIOService(isc::asiolink::IOServicePtr());
4969         isc::dhcp::TimerMgr::instance()->unregisterTimers();
4970         isc::dhcp::CfgMgr::instance().clear();
4971     }
4972 
4973     /// @brief Prepares the class for a test.
4974     ///
4975     /// Invoked by gtest prior test entry, we create the
4976     /// appropriate schema and create a basic DB manager to
4977     /// wipe out any prior instance
SetUp()4978     virtual void SetUp() {
4979         // Ensure we have the proper schema with no transient data.
4980         createMySQLSchema();
4981         isc::dhcp::CfgMgr::instance().clear();
4982         isc::dhcp::MySqlConfigBackendDHCPv6::registerBackendType();
4983     }
4984 
4985     /// @brief Pre-text exit clean up
4986     ///
4987     /// Invoked by gtest upon test exit, we destroy the schema
4988     /// we created.
TearDown()4989     virtual void TearDown() {
4990         // If data wipe enabled, delete transient data otherwise destroy the schema
4991         destroyMySQLSchema();
4992         isc::dhcp::CfgMgr::instance().clear();
4993         isc::dhcp::MySqlConfigBackendDHCPv6::unregisterBackendType();
4994     }
4995 
4996     /// @brief Method which returns the back end specific connection
4997     /// string
validConnectString()4998     virtual std::string validConnectString() {
4999         return (validMySQLConnectionString());
5000     }
5001 
5002     /// @brief Method which returns invalid back end specific connection
5003     /// string
invalidConnectString()5004     virtual std::string invalidConnectString() {
5005         return (connectionString(MYSQL_VALID_TYPE, INVALID_NAME, VALID_HOST,
5006                                  VALID_USER, VALID_PASSWORD));
5007     }
5008 
5009     /// @brief Verifies open failures do NOT invoke db lost callback
5010     ///
5011     /// The db lost callback should only be invoked after successfully
5012     /// opening the DB and then subsequently losing it. Failing to
5013     /// open should be handled directly by the application layer.
5014     void testNoCallbackOnOpenFailure();
5015 
5016     /// @brief Verifies the CB manager's behavior if DB connection is lost
5017     ///
5018     /// This function creates a CB manager with a back end that supports
5019     /// connectivity lost callback. It verifies connectivity by issuing a known
5020     /// valid query. Next it simulates connectivity lost by identifying and
5021     /// closing the socket connection to the CB backend. It then reissues the
5022     /// query and verifies that:
5023     /// -# The Query throws  DbOperationError (rather than exiting)
5024     /// -# The registered DbLostCallback was invoked
5025     /// -# The registered DbRecoveredCallback was invoked
5026     void testDbLostAndRecoveredCallback();
5027 
5028     /// @brief Verifies the CB manager's behavior if DB connection is lost
5029     ///
5030     /// This function creates a CB manager with a back end that supports
5031     /// connectivity lost callback. It verifies connectivity by issuing a known
5032     /// valid query. Next it simulates connectivity lost by identifying and
5033     /// closing the socket connection to the CB backend. It then reissues the
5034     /// query and verifies that:
5035     /// -# The Query throws  DbOperationError (rather than exiting)
5036     /// -# The registered DbLostCallback was invoked
5037     /// -# The registered DbFailedCallback was invoked
5038     void testDbLostAndFailedCallback();
5039 
5040     /// @brief Verifies the CB manager's behavior if DB connection is lost
5041     ///
5042     /// This function creates a CB manager with a back end that supports
5043     /// connectivity lost callback. It verifies connectivity by issuing a known
5044     /// valid query. Next it simulates connectivity lost by identifying and
5045     /// closing the socket connection to the CB backend. It then reissues the
5046     /// query and verifies that:
5047     /// -# The Query throws  DbOperationError (rather than exiting)
5048     /// -# The registered DbLostCallback was invoked
5049     /// -# The registered DbRecoveredCallback was invoked after two reconnect
5050     /// attempts (once failing and second triggered by timer)
5051     void testDbLostAndRecoveredAfterTimeoutCallback();
5052 
5053     /// @brief Verifies the CB manager's behavior if DB connection is lost
5054     ///
5055     /// This function creates a CB manager with a back end that supports
5056     /// connectivity lost callback. It verifies connectivity by issuing a known
5057     /// valid query. Next it simulates connectivity lost by identifying and
5058     /// closing the socket connection to the CB backend. It then reissues the
5059     /// query and verifies that:
5060     /// -# The Query throws  DbOperationError (rather than exiting)
5061     /// -# The registered DbLostCallback was invoked
5062     /// -# The registered DbFailedCallback was invoked after two reconnect
5063     /// attempts (once failing and second triggered by timer)
5064     void testDbLostAndFailedAfterTimeoutCallback();
5065 
5066     /// @brief Callback function registered with the CB manager
db_lost_callback(db::ReconnectCtlPtr)5067     bool db_lost_callback(db::ReconnectCtlPtr /* not_used */) {
5068         return (++db_lost_callback_called_);
5069     }
5070 
5071     /// @brief Flag used to detect calls to db_lost_callback function
5072     uint32_t db_lost_callback_called_;
5073 
5074     /// @brief Callback function registered with the CB manager
db_recovered_callback(db::ReconnectCtlPtr)5075     bool db_recovered_callback(db::ReconnectCtlPtr /* not_used */) {
5076         return (++db_recovered_callback_called_);
5077     }
5078 
5079     /// @brief Flag used to detect calls to db_recovered_callback function
5080     uint32_t db_recovered_callback_called_;
5081 
5082     /// @brief Callback function registered with the CB manager
db_failed_callback(db::ReconnectCtlPtr)5083     bool db_failed_callback(db::ReconnectCtlPtr /* not_used */) {
5084         return (++db_failed_callback_called_);
5085     }
5086 
5087     /// @brief Flag used to detect calls to db_failed_callback function
5088     uint32_t db_failed_callback_called_;
5089 
5090     /// The IOService object, used for all ASIO operations.
5091     isc::asiolink::IOServicePtr io_service_;
5092 };
5093 
5094 void
testNoCallbackOnOpenFailure()5095 MySqlConfigBackendDHCPv6DbLostCallbackTest::testNoCallbackOnOpenFailure() {
5096     isc::db::DatabaseConnection::db_lost_callback_ =
5097         std::bind(&MySqlConfigBackendDHCPv6DbLostCallbackTest::db_lost_callback, this, ph::_1);
5098 
5099     // Set the connectivity recovered callback.
5100     isc::db::DatabaseConnection::db_recovered_callback_ =
5101         std::bind(&MySqlConfigBackendDHCPv6DbLostCallbackTest::db_recovered_callback, this, ph::_1);
5102 
5103     // Set the connectivity failed callback.
5104     isc::db::DatabaseConnection::db_failed_callback_ =
5105         std::bind(&MySqlConfigBackendDHCPv6DbLostCallbackTest::db_failed_callback, this, ph::_1);
5106 
5107     std::string access = invalidConnectString();
5108 
5109     // Connect to the CB backend.
5110     ASSERT_THROW(ConfigBackendDHCPv6Mgr::instance().addBackend(access), DbOpenError);
5111 
5112     io_service_->poll();
5113 
5114     EXPECT_EQ(0, db_lost_callback_called_);
5115     EXPECT_EQ(0, db_recovered_callback_called_);
5116     EXPECT_EQ(0, db_failed_callback_called_);
5117 }
5118 
5119 void
testDbLostAndRecoveredCallback()5120 MySqlConfigBackendDHCPv6DbLostCallbackTest::testDbLostAndRecoveredCallback() {
5121     // Set the connectivity lost callback.
5122     isc::db::DatabaseConnection::db_lost_callback_ =
5123         std::bind(&MySqlConfigBackendDHCPv6DbLostCallbackTest::db_lost_callback, this, ph::_1);
5124 
5125     // Set the connectivity recovered callback.
5126     isc::db::DatabaseConnection::db_recovered_callback_ =
5127         std::bind(&MySqlConfigBackendDHCPv6DbLostCallbackTest::db_recovered_callback, this, ph::_1);
5128 
5129     // Set the connectivity failed callback.
5130     isc::db::DatabaseConnection::db_failed_callback_ =
5131         std::bind(&MySqlConfigBackendDHCPv6DbLostCallbackTest::db_failed_callback, this, ph::_1);
5132 
5133     std::string access = validConnectString();
5134 
5135     ConfigControlInfoPtr config_ctl_info(new ConfigControlInfo());
5136     config_ctl_info->addConfigDatabase(access);
5137     CfgMgr::instance().getCurrentCfg()->setConfigControlInfo(config_ctl_info);
5138 
5139     // Find the most recently opened socket. Our SQL client's socket should
5140     // be the next one.
5141     int last_open_socket = findLastSocketFd();
5142 
5143     // Fill holes.
5144     FillFdHoles holes(last_open_socket);
5145 
5146     // Connect to the CB backend.
5147     ASSERT_NO_THROW(ConfigBackendDHCPv6Mgr::instance().addBackend(access));
5148 
5149     // Find the SQL client socket.
5150     int sql_socket = findLastSocketFd();
5151     ASSERT_TRUE(sql_socket > last_open_socket);
5152 
5153     // Verify we can execute a query.  We don't care about the answer.
5154     ServerCollection servers;
5155     ASSERT_NO_THROW(servers = ConfigBackendDHCPv6Mgr::instance().getPool()->getAllServers6(BackendSelector()));
5156 
5157     // Now close the sql socket out from under backend client
5158     ASSERT_EQ(0, close(sql_socket));
5159 
5160     // A query should fail with DbConnectionUnusable.
5161     ASSERT_THROW(servers = ConfigBackendDHCPv6Mgr::instance().getPool()->getAllServers6(BackendSelector()),
5162                  DbConnectionUnusable);
5163 
5164     io_service_->poll();
5165 
5166     // Our lost and recovered connectivity callback should have been invoked.
5167     EXPECT_EQ(1, db_lost_callback_called_);
5168     EXPECT_EQ(1, db_recovered_callback_called_);
5169     EXPECT_EQ(0, db_failed_callback_called_);
5170 }
5171 
5172 void
testDbLostAndFailedCallback()5173 MySqlConfigBackendDHCPv6DbLostCallbackTest::testDbLostAndFailedCallback() {
5174     // Set the connectivity lost callback.
5175     isc::db::DatabaseConnection::db_lost_callback_ =
5176         std::bind(&MySqlConfigBackendDHCPv6DbLostCallbackTest::db_lost_callback, this, ph::_1);
5177 
5178     // Set the connectivity recovered callback.
5179     isc::db::DatabaseConnection::db_recovered_callback_ =
5180         std::bind(&MySqlConfigBackendDHCPv6DbLostCallbackTest::db_recovered_callback, this, ph::_1);
5181 
5182     // Set the connectivity failed callback.
5183     isc::db::DatabaseConnection::db_failed_callback_ =
5184         std::bind(&MySqlConfigBackendDHCPv6DbLostCallbackTest::db_failed_callback, this, ph::_1);
5185 
5186     std::string access = validConnectString();
5187     ConfigControlInfoPtr config_ctl_info(new ConfigControlInfo());
5188     config_ctl_info->addConfigDatabase(access);
5189     CfgMgr::instance().getCurrentCfg()->setConfigControlInfo(config_ctl_info);
5190 
5191     // Find the most recently opened socket. Our SQL client's socket should
5192     // be the next one.
5193     int last_open_socket = findLastSocketFd();
5194 
5195     // Fill holes.
5196     FillFdHoles holes(last_open_socket);
5197 
5198     // Connect to the CB backend.
5199     ASSERT_NO_THROW(ConfigBackendDHCPv6Mgr::instance().addBackend(access));
5200 
5201     // Find the SQL client socket.
5202     int sql_socket = findLastSocketFd();
5203     ASSERT_TRUE(sql_socket > last_open_socket);
5204 
5205     // Verify we can execute a query.  We don't care about the answer.
5206     ServerCollection servers;
5207     ASSERT_NO_THROW(servers = ConfigBackendDHCPv6Mgr::instance().getPool()->getAllServers6(BackendSelector()));
5208 
5209     access = invalidConnectString();
5210     CfgMgr::instance().clear();
5211     // by adding an invalid access will cause the manager factory to throw
5212     // resulting in failure to recreate the manager
5213     config_ctl_info.reset(new ConfigControlInfo());
5214     config_ctl_info->addConfigDatabase(access);
5215     CfgMgr::instance().getCurrentCfg()->setConfigControlInfo(config_ctl_info);
5216     const ConfigDbInfoList& cfg = CfgMgr::instance().getCurrentCfg()->getConfigControlInfo()->getConfigDatabases();
5217     (const_cast<ConfigDbInfoList&>(cfg))[0].setAccessString(access, true);
5218 
5219     // Now close the sql socket out from under backend client
5220     ASSERT_EQ(0, close(sql_socket));
5221 
5222     // A query should fail with DbConnectionUnusable.
5223     ASSERT_THROW(servers = ConfigBackendDHCPv6Mgr::instance().getPool()->getAllServers6(BackendSelector()),
5224                  DbConnectionUnusable);
5225 
5226     io_service_->poll();
5227 
5228     // Our lost and failed connectivity callback should have been invoked.
5229     EXPECT_EQ(1, db_lost_callback_called_);
5230     EXPECT_EQ(0, db_recovered_callback_called_);
5231     EXPECT_EQ(1, db_failed_callback_called_);
5232 }
5233 
5234 void
testDbLostAndRecoveredAfterTimeoutCallback()5235 MySqlConfigBackendDHCPv6DbLostCallbackTest::testDbLostAndRecoveredAfterTimeoutCallback() {
5236     // Set the connectivity lost callback.
5237     isc::db::DatabaseConnection::db_lost_callback_ =
5238         std::bind(&MySqlConfigBackendDHCPv6DbLostCallbackTest::db_lost_callback, this, ph::_1);
5239 
5240     // Set the connectivity recovered callback.
5241     isc::db::DatabaseConnection::db_recovered_callback_ =
5242         std::bind(&MySqlConfigBackendDHCPv6DbLostCallbackTest::db_recovered_callback, this, ph::_1);
5243 
5244     // Set the connectivity failed callback.
5245     isc::db::DatabaseConnection::db_failed_callback_ =
5246         std::bind(&MySqlConfigBackendDHCPv6DbLostCallbackTest::db_failed_callback, this, ph::_1);
5247 
5248     std::string access = validConnectString();
5249     std::string extra = " max-reconnect-tries=3 reconnect-wait-time=1";
5250     access += extra;
5251     ConfigControlInfoPtr config_ctl_info(new ConfigControlInfo());
5252     config_ctl_info->addConfigDatabase(access);
5253     CfgMgr::instance().getCurrentCfg()->setConfigControlInfo(config_ctl_info);
5254 
5255     // Find the most recently opened socket. Our SQL client's socket should
5256     // be the next one.
5257     int last_open_socket = findLastSocketFd();
5258 
5259     // Fill holes.
5260     FillFdHoles holes(last_open_socket);
5261 
5262     // Connect to the CB backend.
5263     ASSERT_NO_THROW(ConfigBackendDHCPv6Mgr::instance().addBackend(access));
5264 
5265     // Find the SQL client socket.
5266     int sql_socket = findLastSocketFd();
5267     ASSERT_TRUE(sql_socket > last_open_socket);
5268 
5269     // Verify we can execute a query.  We don't care about the answer.
5270     ServerCollection servers;
5271     ASSERT_NO_THROW(servers = ConfigBackendDHCPv6Mgr::instance().getPool()->getAllServers6(BackendSelector()));
5272 
5273     access = invalidConnectString();
5274     access += extra;
5275     CfgMgr::instance().clear();
5276     // by adding an invalid access will cause the manager factory to throw
5277     // resulting in failure to recreate the manager
5278     config_ctl_info.reset(new ConfigControlInfo());
5279     config_ctl_info->addConfigDatabase(access);
5280     CfgMgr::instance().getCurrentCfg()->setConfigControlInfo(config_ctl_info);
5281     const ConfigDbInfoList& cfg = CfgMgr::instance().getCurrentCfg()->getConfigControlInfo()->getConfigDatabases();
5282     (const_cast<ConfigDbInfoList&>(cfg))[0].setAccessString(access, true);
5283 
5284     // Now close the sql socket out from under backend client
5285     ASSERT_EQ(0, close(sql_socket));
5286 
5287     // A query should fail with DbConnectionUnusable.
5288     ASSERT_THROW(servers = ConfigBackendDHCPv6Mgr::instance().getPool()->getAllServers6(BackendSelector()),
5289                  DbConnectionUnusable);
5290 
5291     io_service_->poll();
5292 
5293     // Our lost connectivity callback should have been invoked.
5294     EXPECT_EQ(1, db_lost_callback_called_);
5295     EXPECT_EQ(0, db_recovered_callback_called_);
5296     EXPECT_EQ(0, db_failed_callback_called_);
5297 
5298     access = validConnectString();
5299     access += extra;
5300     CfgMgr::instance().clear();
5301     config_ctl_info.reset(new ConfigControlInfo());
5302     config_ctl_info->addConfigDatabase(access);
5303     CfgMgr::instance().getCurrentCfg()->setConfigControlInfo(config_ctl_info);
5304 
5305     sleep(1);
5306 
5307     io_service_->poll();
5308 
5309     // Our lost and recovered connectivity callback should have been invoked.
5310     EXPECT_EQ(2, db_lost_callback_called_);
5311     EXPECT_EQ(1, db_recovered_callback_called_);
5312     EXPECT_EQ(0, db_failed_callback_called_);
5313 
5314     sleep(1);
5315 
5316     io_service_->poll();
5317 
5318     // No callback should have been invoked.
5319     EXPECT_EQ(2, db_lost_callback_called_);
5320     EXPECT_EQ(1, db_recovered_callback_called_);
5321     EXPECT_EQ(0, db_failed_callback_called_);
5322 }
5323 
5324 void
testDbLostAndFailedAfterTimeoutCallback()5325 MySqlConfigBackendDHCPv6DbLostCallbackTest::testDbLostAndFailedAfterTimeoutCallback() {
5326     // Set the connectivity lost callback.
5327     isc::db::DatabaseConnection::db_lost_callback_ =
5328         std::bind(&MySqlConfigBackendDHCPv6DbLostCallbackTest::db_lost_callback, this, ph::_1);
5329 
5330     // Set the connectivity recovered callback.
5331     isc::db::DatabaseConnection::db_recovered_callback_ =
5332         std::bind(&MySqlConfigBackendDHCPv6DbLostCallbackTest::db_recovered_callback, this, ph::_1);
5333 
5334     // Set the connectivity failed callback.
5335     isc::db::DatabaseConnection::db_failed_callback_ =
5336         std::bind(&MySqlConfigBackendDHCPv6DbLostCallbackTest::db_failed_callback, this, ph::_1);
5337 
5338     std::string access = validConnectString();
5339     std::string extra = " max-reconnect-tries=3 reconnect-wait-time=1";
5340     access += extra;
5341     ConfigControlInfoPtr config_ctl_info(new ConfigControlInfo());
5342     config_ctl_info->addConfigDatabase(access);
5343     CfgMgr::instance().getCurrentCfg()->setConfigControlInfo(config_ctl_info);
5344 
5345     // Find the most recently opened socket. Our SQL client's socket should
5346     // be the next one.
5347     int last_open_socket = findLastSocketFd();
5348 
5349     // Fill holes.
5350     FillFdHoles holes(last_open_socket);
5351 
5352     // Connect to the CB backend.
5353     ASSERT_NO_THROW(ConfigBackendDHCPv6Mgr::instance().addBackend(access));
5354 
5355     // Find the SQL client socket.
5356     int sql_socket = findLastSocketFd();
5357     ASSERT_TRUE(sql_socket > last_open_socket);
5358 
5359     // Verify we can execute a query.  We don't care about the answer.
5360     ServerCollection servers;
5361     ASSERT_NO_THROW(servers = ConfigBackendDHCPv6Mgr::instance().getPool()->getAllServers6(BackendSelector()));
5362 
5363     access = invalidConnectString();
5364     access += extra;
5365     CfgMgr::instance().clear();
5366     // by adding an invalid access will cause the manager factory to throw
5367     // resulting in failure to recreate the manager
5368     config_ctl_info.reset(new ConfigControlInfo());
5369     config_ctl_info->addConfigDatabase(access);
5370     CfgMgr::instance().getCurrentCfg()->setConfigControlInfo(config_ctl_info);
5371     const ConfigDbInfoList& cfg = CfgMgr::instance().getCurrentCfg()->getConfigControlInfo()->getConfigDatabases();
5372     (const_cast<ConfigDbInfoList&>(cfg))[0].setAccessString(access, true);
5373 
5374     // Now close the sql socket out from under backend client
5375     ASSERT_EQ(0, close(sql_socket));
5376 
5377     // A query should fail with DbConnectionUnusable.
5378     ASSERT_THROW(servers = ConfigBackendDHCPv6Mgr::instance().getPool()->getAllServers6(BackendSelector()),
5379                  DbConnectionUnusable);
5380 
5381     io_service_->poll();
5382 
5383     // Our lost connectivity callback should have been invoked.
5384     EXPECT_EQ(1, db_lost_callback_called_);
5385     EXPECT_EQ(0, db_recovered_callback_called_);
5386     EXPECT_EQ(0, db_failed_callback_called_);
5387 
5388     sleep(1);
5389 
5390     io_service_->poll();
5391 
5392     // Our lost connectivity callback should have been invoked.
5393     EXPECT_EQ(2, db_lost_callback_called_);
5394     EXPECT_EQ(0, db_recovered_callback_called_);
5395     EXPECT_EQ(0, db_failed_callback_called_);
5396 
5397     sleep(1);
5398 
5399     io_service_->poll();
5400 
5401     // Our lost and failed connectivity callback should have been invoked.
5402     EXPECT_EQ(3, db_lost_callback_called_);
5403     EXPECT_EQ(0, db_recovered_callback_called_);
5404     EXPECT_EQ(1, db_failed_callback_called_);
5405 }
5406 
5407 /// @brief Verifies that db lost callback is not invoked on an open failure
TEST_F(MySqlConfigBackendDHCPv6DbLostCallbackTest,testNoCallbackOnOpenFailure)5408 TEST_F(MySqlConfigBackendDHCPv6DbLostCallbackTest, testNoCallbackOnOpenFailure) {
5409     MultiThreadingTest mt(false);
5410     testNoCallbackOnOpenFailure();
5411 }
5412 
5413 /// @brief Verifies that db lost callback is not invoked on an open failure
TEST_F(MySqlConfigBackendDHCPv6DbLostCallbackTest,testNoCallbackOnOpenFailureMultiThreading)5414 TEST_F(MySqlConfigBackendDHCPv6DbLostCallbackTest, testNoCallbackOnOpenFailureMultiThreading) {
5415     MultiThreadingTest mt(true);
5416     testNoCallbackOnOpenFailure();
5417 }
5418 
5419 /// @brief Verifies that loss of connectivity to MySQL is handled correctly.
TEST_F(MySqlConfigBackendDHCPv6DbLostCallbackTest,testDbLostAndRecoveredCallback)5420 TEST_F(MySqlConfigBackendDHCPv6DbLostCallbackTest, testDbLostAndRecoveredCallback) {
5421     MultiThreadingTest mt(false);
5422     testDbLostAndRecoveredCallback();
5423 }
5424 
5425 /// @brief Verifies that loss of connectivity to MySQL is handled correctly.
TEST_F(MySqlConfigBackendDHCPv6DbLostCallbackTest,testDbLostAndRecoveredCallbackMultiThreading)5426 TEST_F(MySqlConfigBackendDHCPv6DbLostCallbackTest, testDbLostAndRecoveredCallbackMultiThreading) {
5427     MultiThreadingTest mt(true);
5428     testDbLostAndRecoveredCallback();
5429 }
5430 
5431 /// @brief Verifies that loss of connectivity to MySQL is handled correctly.
TEST_F(MySqlConfigBackendDHCPv6DbLostCallbackTest,testDbLostAndFailedCallback)5432 TEST_F(MySqlConfigBackendDHCPv6DbLostCallbackTest, testDbLostAndFailedCallback) {
5433     MultiThreadingTest mt(false);
5434     testDbLostAndFailedCallback();
5435 }
5436 
5437 /// @brief Verifies that loss of connectivity to MySQL is handled correctly.
TEST_F(MySqlConfigBackendDHCPv6DbLostCallbackTest,testDbLostAndFailedCallbackMultiThreading)5438 TEST_F(MySqlConfigBackendDHCPv6DbLostCallbackTest, testDbLostAndFailedCallbackMultiThreading) {
5439     MultiThreadingTest mt(true);
5440     testDbLostAndFailedCallback();
5441 }
5442 
5443 /// @brief Verifies that loss of connectivity to MySQL is handled correctly.
TEST_F(MySqlConfigBackendDHCPv6DbLostCallbackTest,testDbLostAndRecoveredAfterTimeoutCallback)5444 TEST_F(MySqlConfigBackendDHCPv6DbLostCallbackTest, testDbLostAndRecoveredAfterTimeoutCallback) {
5445     MultiThreadingTest mt(false);
5446     testDbLostAndRecoveredAfterTimeoutCallback();
5447 }
5448 
5449 /// @brief Verifies that loss of connectivity to MySQL is handled correctly.
TEST_F(MySqlConfigBackendDHCPv6DbLostCallbackTest,testDbLostAndRecoveredAfterTimeoutCallbackMultiThreading)5450 TEST_F(MySqlConfigBackendDHCPv6DbLostCallbackTest, testDbLostAndRecoveredAfterTimeoutCallbackMultiThreading) {
5451     MultiThreadingTest mt(true);
5452     testDbLostAndRecoveredAfterTimeoutCallback();
5453 }
5454 
5455 /// @brief Verifies that loss of connectivity to MySQL is handled correctly.
TEST_F(MySqlConfigBackendDHCPv6DbLostCallbackTest,testDbLostAndFailedAfterTimeoutCallback)5456 TEST_F(MySqlConfigBackendDHCPv6DbLostCallbackTest, testDbLostAndFailedAfterTimeoutCallback) {
5457     MultiThreadingTest mt(false);
5458     testDbLostAndFailedAfterTimeoutCallback();
5459 }
5460 
5461 /// @brief Verifies that loss of connectivity to MySQL is handled correctly.
TEST_F(MySqlConfigBackendDHCPv6DbLostCallbackTest,testDbLostAndFailedAfterTimeoutCallbackMultiThreading)5462 TEST_F(MySqlConfigBackendDHCPv6DbLostCallbackTest, testDbLostAndFailedAfterTimeoutCallbackMultiThreading) {
5463     MultiThreadingTest mt(true);
5464     testDbLostAndFailedAfterTimeoutCallback();
5465 }
5466 
5467 }
5468