1 // Copyright (C) 2012-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 
9 #include <exceptions/exceptions.h>
10 #include <dhcp/dhcp6.h>
11 #include <dhcp/tests/iface_mgr_test_config.h>
12 #include <dhcpsrv/cfgmgr.h>
13 #include <dhcpsrv/lease_mgr_factory.h>
14 #include <dhcpsrv/subnet_id.h>
15 #include <dhcpsrv/parsers/dhcp_parsers.h>
16 #include <process/logging_info.h>
17 #include <stats/stats_mgr.h>
18 #include <util/chrono_time_utils.h>
19 
20 #include <boost/scoped_ptr.hpp>
21 
22 #include <gtest/gtest.h>
23 
24 #include <iostream>
25 #include <sstream>
26 
27 #include <arpa/inet.h>
28 
29 using namespace std;
30 using namespace isc::asiolink;
31 using namespace isc::data;
32 using namespace isc::dhcp;
33 using namespace isc::dhcp::test;
34 using namespace isc::util;
35 using namespace isc::stats;
36 using namespace isc::process;
37 using namespace isc;
38 
39 // don't import the entire boost namespace.  It will unexpectedly hide uint8_t
40 // for some systems.
41 using boost::scoped_ptr;
42 
43 namespace {
44 
45 template <typename Storage>
isZeroPosition(const Storage & storage,const std::string & param_name)46 bool isZeroPosition(const Storage& storage, const std::string& param_name) {
47     Element::Position position = storage.getPosition(param_name);
48     return ((position.line_ == 0) && (position.pos_ == 0) &&
49             (position.file_.empty()));
50 }
51 
52 // This test verifies that BooleanStorage functions properly.
TEST(ValueStorageTest,BooleanTesting)53 TEST(ValueStorageTest, BooleanTesting) {
54     BooleanStorage testStore;
55 
56     // Verify that we can add and retrieve parameters.
57     testStore.setParam("firstBool", false, Element::Position("kea.conf", 123, 234));
58     testStore.setParam("secondBool", true, Element::Position("keax.conf", 10, 20));
59 
60     EXPECT_FALSE(testStore.getParam("firstBool"));
61     EXPECT_TRUE(testStore.getParam("secondBool"));
62 
63     EXPECT_EQ(123, testStore.getPosition("firstBool").line_);
64     EXPECT_EQ(234, testStore.getPosition("firstBool").pos_);
65     EXPECT_EQ("kea.conf", testStore.getPosition("firstBool").file_);
66 
67     EXPECT_EQ(10, testStore.getPosition("secondBool").line_);
68     EXPECT_EQ(20, testStore.getPosition("secondBool").pos_);
69     EXPECT_EQ("keax.conf", testStore.getPosition("secondBool").file_);
70 
71     // Verify that we can update parameters.
72     testStore.setParam("firstBool", true, Element::Position("keax.conf", 555, 111));
73     testStore.setParam("secondBool", false, Element::Position("kea.conf", 1, 3));
74 
75     EXPECT_TRUE(testStore.getParam("firstBool"));
76     EXPECT_FALSE(testStore.getParam("secondBool"));
77 
78     EXPECT_EQ(555, testStore.getPosition("firstBool").line_);
79     EXPECT_EQ(111, testStore.getPosition("firstBool").pos_);
80     EXPECT_EQ("keax.conf", testStore.getPosition("firstBool").file_);
81 
82     EXPECT_EQ(1, testStore.getPosition("secondBool").line_);
83     EXPECT_EQ(3, testStore.getPosition("secondBool").pos_);
84     EXPECT_EQ("kea.conf", testStore.getPosition("secondBool").file_);
85 
86     // Verify that we can delete a parameter and it will no longer be found.
87     testStore.delParam("firstBool");
88     EXPECT_THROW(testStore.getParam("firstBool"), isc::dhcp::DhcpConfigError);
89 
90     // Verify that the "zero" position is returned when parameter doesn't exist.
91     EXPECT_TRUE(isZeroPosition(testStore, "firstBool"));
92 
93     // Verify that the delete was safe and the store still operates.
94     EXPECT_FALSE(testStore.getParam("secondBool"));
95 
96     EXPECT_EQ(1, testStore.getPosition("secondBool").line_);
97     EXPECT_EQ(3, testStore.getPosition("secondBool").pos_);
98     EXPECT_EQ("kea.conf", testStore.getPosition("secondBool").file_);
99 
100     // Verify that looking for a parameter that never existed throws.
101     ASSERT_THROW(testStore.getParam("bogusBool"), isc::dhcp::DhcpConfigError);
102 
103     // Verify that the "zero" position is returned when parameter doesn't exist.
104     EXPECT_TRUE(isZeroPosition(testStore, "bogusBool"));
105 
106     // Verify that attempting to delete a parameter that never existed does not throw.
107     EXPECT_NO_THROW(testStore.delParam("bogusBool"));
108 
109     // Verify that we can empty the list.
110     testStore.clear();
111     EXPECT_THROW(testStore.getParam("secondBool"), isc::dhcp::DhcpConfigError);
112 
113     // Verify that the "zero" position is returned when parameter doesn't exist.
114     EXPECT_TRUE(isZeroPosition(testStore, "secondBool"));
115 }
116 
117 // This test verifies that Uint32Storage functions properly.
TEST(ValueStorageTest,Uint32Testing)118 TEST(ValueStorageTest, Uint32Testing) {
119     Uint32Storage testStore;
120 
121     uint32_t int_one = 77;
122     uint32_t int_two = 33;
123 
124     // Verify that we can add and retrieve parameters.
125     testStore.setParam("firstInt", int_one, Element::Position("kea.conf", 123, 234));
126     testStore.setParam("secondInt", int_two, Element::Position("keax.conf", 10, 20));
127 
128     EXPECT_EQ(testStore.getParam("firstInt"), int_one);
129     EXPECT_EQ(testStore.getParam("secondInt"), int_two);
130 
131     EXPECT_EQ(123, testStore.getPosition("firstInt").line_);
132     EXPECT_EQ(234, testStore.getPosition("firstInt").pos_);
133     EXPECT_EQ("kea.conf", testStore.getPosition("firstInt").file_);
134 
135     EXPECT_EQ(10, testStore.getPosition("secondInt").line_);
136     EXPECT_EQ(20, testStore.getPosition("secondInt").pos_);
137     EXPECT_EQ("keax.conf", testStore.getPosition("secondInt").file_);
138 
139     // Verify that we can update parameters.
140     testStore.setParam("firstInt", --int_one, Element::Position("keax.conf", 555, 111));
141     testStore.setParam("secondInt", ++int_two, Element::Position("kea.conf", 1, 3));
142 
143     EXPECT_EQ(testStore.getParam("firstInt"), int_one);
144     EXPECT_EQ(testStore.getParam("secondInt"), int_two);
145 
146     EXPECT_EQ(555, testStore.getPosition("firstInt").line_);
147     EXPECT_EQ(111, testStore.getPosition("firstInt").pos_);
148     EXPECT_EQ("keax.conf", testStore.getPosition("firstInt").file_);
149 
150     EXPECT_EQ(1, testStore.getPosition("secondInt").line_);
151     EXPECT_EQ(3, testStore.getPosition("secondInt").pos_);
152     EXPECT_EQ("kea.conf", testStore.getPosition("secondInt").file_);
153 
154     // Verify that we can delete a parameter and it will no longer be found.
155     testStore.delParam("firstInt");
156     EXPECT_THROW(testStore.getParam("firstInt"), isc::dhcp::DhcpConfigError);
157 
158     // Verify that the "zero" position is returned when parameter doesn't exist.
159     EXPECT_TRUE(isZeroPosition(testStore, "firstInt"));
160 
161     // Verify that the delete was safe and the store still operates.
162     EXPECT_EQ(testStore.getParam("secondInt"), int_two);
163 
164     EXPECT_EQ(1, testStore.getPosition("secondInt").line_);
165     EXPECT_EQ(3, testStore.getPosition("secondInt").pos_);
166     EXPECT_EQ("kea.conf", testStore.getPosition("secondInt").file_);
167 
168     // Verify that looking for a parameter that never existed throws.
169     ASSERT_THROW(testStore.getParam("bogusInt"), isc::dhcp::DhcpConfigError);
170 
171     // Verify that attempting to delete a parameter that never existed does not throw.
172     EXPECT_NO_THROW(testStore.delParam("bogusInt"));
173 
174     // Verify that the "zero" position is returned when parameter doesn't exist.
175     EXPECT_TRUE(isZeroPosition(testStore, "bogusInt"));
176 
177     // Verify that we can empty the list.
178     testStore.clear();
179     EXPECT_THROW(testStore.getParam("secondInt"), isc::dhcp::DhcpConfigError);
180 
181     // Verify that the "zero" position is returned when parameter doesn't exist.
182     EXPECT_TRUE(isZeroPosition(testStore, "secondInt"));
183 }
184 
185 // This test verifies that StringStorage functions properly.
TEST(ValueStorageTest,StringTesting)186 TEST(ValueStorageTest, StringTesting) {
187     StringStorage testStore;
188 
189     std::string string_one = "seventy-seven";
190     std::string string_two = "thirty-three";
191 
192     // Verify that we can add and retrieve parameters.
193     testStore.setParam("firstString", string_one,
194                        Element::Position("kea.conf", 123, 234));
195     testStore.setParam("secondString", string_two,
196                        Element::Position("keax.conf", 10, 20));
197 
198     EXPECT_EQ(testStore.getParam("firstString"), string_one);
199     EXPECT_EQ(testStore.getParam("secondString"), string_two);
200 
201     EXPECT_EQ(123, testStore.getPosition("firstString").line_);
202     EXPECT_EQ(234, testStore.getPosition("firstString").pos_);
203     EXPECT_EQ("kea.conf", testStore.getPosition("firstString").file_);
204 
205     EXPECT_EQ(10, testStore.getPosition("secondString").line_);
206     EXPECT_EQ(20, testStore.getPosition("secondString").pos_);
207     EXPECT_EQ("keax.conf", testStore.getPosition("secondString").file_);
208 
209     // Verify that we can update parameters.
210     string_one.append("-boo");
211     string_two.append("-boo");
212 
213     testStore.setParam("firstString", string_one,
214                        Element::Position("kea.conf", 555, 111));
215     testStore.setParam("secondString", string_two,
216                        Element::Position("keax.conf", 1, 3));
217 
218     EXPECT_EQ(testStore.getParam("firstString"), string_one);
219     EXPECT_EQ(testStore.getParam("secondString"), string_two);
220 
221     EXPECT_EQ(555, testStore.getPosition("firstString").line_);
222     EXPECT_EQ(111, testStore.getPosition("firstString").pos_);
223     EXPECT_EQ("kea.conf", testStore.getPosition("firstString").file_);
224 
225     EXPECT_EQ(1, testStore.getPosition("secondString").line_);
226     EXPECT_EQ(3, testStore.getPosition("secondString").pos_);
227     EXPECT_EQ("keax.conf", testStore.getPosition("secondString").file_);
228 
229     // Verify that we can delete a parameter and it will no longer be found.
230     testStore.delParam("firstString");
231     EXPECT_THROW(testStore.getParam("firstString"), isc::dhcp::DhcpConfigError);
232 
233     // Verify that the "zero" position is returned when parameter doesn't exist.
234     EXPECT_TRUE(isZeroPosition(testStore, "firstString"));
235 
236     // Verify that the delete was safe and the store still operates.
237     EXPECT_EQ(testStore.getParam("secondString"), string_two);
238 
239     EXPECT_EQ(1, testStore.getPosition("secondString").line_);
240     EXPECT_EQ(3, testStore.getPosition("secondString").pos_);
241     EXPECT_EQ("keax.conf", testStore.getPosition("secondString").file_);
242 
243     // Verify that looking for a parameter that never existed throws.
244     ASSERT_THROW(testStore.getParam("bogusString"), isc::dhcp::DhcpConfigError);
245 
246     // Verify that attempting to delete a parameter that never existed does not throw.
247     EXPECT_NO_THROW(testStore.delParam("bogusString"));
248 
249     // Verify that the "zero" position is returned when parameter doesn't exist.
250     EXPECT_TRUE(isZeroPosition(testStore, "bogusString"));
251 
252     // Verify that we can empty the list.
253     testStore.clear();
254     EXPECT_THROW(testStore.getParam("secondString"), isc::dhcp::DhcpConfigError);
255 
256     // Verify that the "zero" position is returned when parameter doesn't exist.
257     EXPECT_TRUE(isZeroPosition(testStore, "secondString"));
258 }
259 
260 
261 
262 class CfgMgrTest : public ::testing::Test {
263 public:
CfgMgrTest()264     CfgMgrTest() {
265         // make sure we start with a clean configuration
266         original_datadir_ = CfgMgr::instance().getDataDir();
267         clear();
268     }
269 
270     /// @brief generates interface-id option based on provided text
271     ///
272     /// @param text content of the option to be created
273     ///
274     /// @return pointer to the option object created
generateInterfaceId(const string & text)275     OptionPtr generateInterfaceId(const string& text) {
276         OptionBuffer buffer(text.begin(), text.end());
277         return OptionPtr(new Option(Option::V6, D6O_INTERFACE_ID, buffer));
278     }
279 
~CfgMgrTest()280     ~CfgMgrTest() {
281         // clean up after the test
282         clear();
283     }
284 
clear()285     void clear() {
286         CfgMgr::instance().setFamily(AF_INET);
287         CfgMgr::instance().setDataDir(original_datadir_);
288         CfgMgr::instance().clear();
289         LeaseMgrFactory::destroy();
290     }
291 
292     /// @brief Creates instance of the backend.
293     ///
294     /// @param family AF_INET for v4, AF_INET6 for v6
startBackend(int family=AF_INET)295     void startBackend(int family = AF_INET) {
296         try {
297             std::ostringstream s;
298             s << "type=memfile persist=false " << (family == AF_INET6 ?
299                                      "universe=6" : "universe=4");
300             LeaseMgrFactory::create(s.str());
301         } catch (const std::exception& ex) {
302             std::cerr << "*** ERROR: unable to create instance of the Memfile\n"
303                 " lease database backend: " << ex.what() << std::endl;
304             throw;
305         }
306     }
307 
308     /// used in client classification (or just empty container for other tests)
309     isc::dhcp::ClientClasses classify_;
310 
311 private:
312     /// to restore it in destructor.
313     string original_datadir_;
314 };
315 
316 // Checks that there is a configuration structure available and that
317 // it is empty by default.
TEST_F(CfgMgrTest,configuration)318 TEST_F(CfgMgrTest, configuration) {
319 
320     ConstSrvConfigPtr configuration = CfgMgr::instance().getCurrentCfg();
321     ASSERT_TRUE(configuration);
322     EXPECT_TRUE(configuration->getLoggingInfo().empty());
323 
324     configuration = CfgMgr::instance().getStagingCfg();
325     ASSERT_TRUE(configuration);
326     EXPECT_TRUE(configuration->getLoggingInfo().empty());
327 }
328 
329 // This test checks the data directory handling.
TEST_F(CfgMgrTest,dataDir)330 TEST_F(CfgMgrTest, dataDir) {
331     // It is only in DHCPv6 syntax so switch to IPv6.
332     CfgMgr::instance().setFamily(AF_INET6);
333 
334     // Default.
335     EXPECT_TRUE(CfgMgr::instance().getDataDir().unspecified());
336     ConstElementPtr json = CfgMgr::instance().getCurrentCfg()->toElement();
337     ASSERT_TRUE(json);
338     ASSERT_EQ(Element::map, json->getType());
339     ConstElementPtr dhcp = json->get("Dhcp6");
340     ASSERT_TRUE(dhcp);
341     ASSERT_EQ(Element::map, dhcp->getType());
342     ConstElementPtr datadir = dhcp->get("data-directory");
343     EXPECT_FALSE(datadir);
344 
345     // Set but not specified.
346     CfgMgr::instance().setDataDir("/tmp");
347     EXPECT_TRUE(CfgMgr::instance().getDataDir().unspecified());
348     EXPECT_EQ("/tmp", string(CfgMgr::instance().getDataDir()));
349     json = CfgMgr::instance().getCurrentCfg()->toElement();
350     ASSERT_TRUE(json);
351     ASSERT_EQ(Element::map, json->getType());
352     dhcp = json->get("Dhcp6");
353     ASSERT_TRUE(dhcp);
354     ASSERT_EQ(Element::map, dhcp->getType());
355     datadir = dhcp->get("data-directory");
356     EXPECT_FALSE(datadir);
357 
358     // Set and specified.
359     CfgMgr::instance().setDataDir("/tmp", false);
360     EXPECT_FALSE(CfgMgr::instance().getDataDir().unspecified());
361     EXPECT_EQ("/tmp", string(CfgMgr::instance().getDataDir()));
362     json = CfgMgr::instance().getCurrentCfg()->toElement();
363     ASSERT_TRUE(json);
364     ASSERT_EQ(Element::map, json->getType());
365     dhcp = json->get("Dhcp6");
366     ASSERT_TRUE(dhcp);
367     ASSERT_EQ(Element::map, dhcp->getType());
368     datadir = dhcp->get("data-directory");
369     ASSERT_TRUE(datadir);
370     ASSERT_EQ(Element::string, datadir->getType());
371     EXPECT_EQ("/tmp", datadir->stringValue());
372 
373     // Still IPv6 only.
374     CfgMgr::instance().setFamily(AF_INET);
375     EXPECT_FALSE(CfgMgr::instance().getDataDir().unspecified());
376     EXPECT_EQ("/tmp", string(CfgMgr::instance().getDataDir()));
377     json = CfgMgr::instance().getCurrentCfg()->toElement();
378     ASSERT_TRUE(json);
379     ASSERT_EQ(Element::map, json->getType());
380     dhcp = json->get("Dhcp4");
381     ASSERT_TRUE(dhcp);
382     ASSERT_EQ(Element::map, dhcp->getType());
383     datadir = dhcp->get("data-directory");
384     EXPECT_FALSE(datadir);
385 }
386 
387 // This test checks the D2ClientMgr wrapper methods.
TEST_F(CfgMgrTest,d2ClientConfig)388 TEST_F(CfgMgrTest, d2ClientConfig) {
389     // After CfgMgr construction, D2ClientMgr member should be initialized
390     // with a D2 configuration that is disabled.
391     // Verify we can Fetch the mgr.
392     D2ClientMgr& d2_mgr = CfgMgr::instance().getD2ClientMgr();
393     EXPECT_FALSE(d2_mgr.ddnsEnabled());
394 
395     // Make sure the convenience method fetches the config correctly.
396     D2ClientConfigPtr original_config = CfgMgr::instance().getD2ClientConfig();
397     ASSERT_TRUE(original_config);
398     EXPECT_FALSE(original_config->getEnableUpdates());
399 
400     // Verify that we cannot set the configuration to an empty pointer.
401     D2ClientConfigPtr new_cfg;
402     ASSERT_THROW(CfgMgr::instance().setD2ClientConfig(new_cfg), D2ClientError);
403 
404     // Create a new, enabled configuration.
405     ASSERT_NO_THROW(new_cfg.reset(new D2ClientConfig(true,
406                                   isc::asiolink::IOAddress("127.0.0.1"), 477,
407                                   isc::asiolink::IOAddress("127.0.0.1"), 478,
408                                   1024,
409                                   dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON)));
410 
411     // Verify that we can assign a new, non-empty configuration.
412     ASSERT_NO_THROW(CfgMgr::instance().setD2ClientConfig(new_cfg));
413 
414     // Verify that we can fetch the newly assigned configuration.
415     D2ClientConfigPtr updated_config = CfgMgr::instance().getD2ClientConfig();
416     ASSERT_TRUE(updated_config);
417     EXPECT_TRUE(updated_config->getEnableUpdates());
418 
419     // Make sure convenience method agrees with updated configuration.
420     EXPECT_TRUE(CfgMgr::instance().ddnsEnabled());
421 
422     // Make sure the configuration we fetched is the one we assigned,
423     // and not the original configuration.
424     EXPECT_EQ(*new_cfg, *updated_config);
425     EXPECT_NE(*original_config, *updated_config);
426 
427     // Revert to default configuration.
428     ASSERT_NO_THROW(CfgMgr::instance().setD2ClientConfig(original_config));
429 }
430 
431 // This test verifies that the configuration staging, commit and rollback works
432 // as expected.
TEST_F(CfgMgrTest,staging)433 TEST_F(CfgMgrTest, staging) {
434     CfgMgr& cfg_mgr = CfgMgr::instance();
435     // Initially, the current configuration is a default one. We are going
436     // to get the current configuration a couple of times and make sure
437     // that always the same instance is returned.
438     ConstSrvConfigPtr const_config;
439     for (int i = 0; i < 5; ++i) {
440         const_config = cfg_mgr.getCurrentCfg();
441         ASSERT_TRUE(const_config) << "Returned NULL current configuration"
442             " for iteration " << i;
443         EXPECT_EQ(0, const_config->getSequence())
444             << "Returned invalid sequence number "
445             << const_config->getSequence() << " for iteration " << i;
446     }
447 
448     // Try to get the new staging configuration. When getStagingCfg() is called
449     // for the first time the new instance of the staging configuration is
450     // returned. This instance is returned for every call to getStagingCfg()
451     // until commit is called.
452     SrvConfigPtr config;
453     for (int i = 0; i < 5; ++i) {
454         config = cfg_mgr.getStagingCfg();
455         ASSERT_TRUE(config) << "Returned NULL staging configuration for"
456             " iteration " << i;
457         // The sequence id is 1 for staging because it is ahead of current
458         // configuration having sequence number 0.
459         EXPECT_EQ(1, config->getSequence()) << "Returned invalid sequence"
460             " number " << config->getSequence() << " for iteration " << i;
461     }
462 
463     // This should change the staging configuration so as it becomes a current
464     // one.
465     auto before = boost::posix_time::second_clock::universal_time();
466     cfg_mgr.commit();
467     auto after = boost::posix_time::second_clock::universal_time();
468     const_config = cfg_mgr.getCurrentCfg();
469     ASSERT_TRUE(const_config);
470     // Sequence id equal to 1 indicates that the current configuration points
471     // to the configuration that used to be a staging configuration previously.
472     EXPECT_EQ(1, const_config->getSequence());
473     // Last commit timestamp should be between before and after.
474     auto reload = const_config->getLastCommitTime();
475     ASSERT_FALSE(reload.is_not_a_date_time());
476     EXPECT_LE(before, reload);
477     EXPECT_GE(after, reload);
478 
479     // Create a new staging configuration. It should be assigned a new
480     // sequence id.
481     config = cfg_mgr.getStagingCfg();
482     ASSERT_TRUE(config);
483     EXPECT_EQ(2, config->getSequence());
484 
485     // Let's execute commit a couple of times. The first invocation to commit
486     // changes the configuration having sequence 2 to current configuration.
487     // Other commits are no-op.
488     for (int i = 0; i < 5; ++i) {
489         cfg_mgr.commit();
490     }
491 
492     // The current configuration now have sequence number 2.
493     const_config = cfg_mgr.getCurrentCfg();
494     ASSERT_TRUE(const_config);
495     EXPECT_EQ(2, const_config->getSequence());
496 
497     // Clear configuration along with a history.
498     cfg_mgr.clear();
499 
500     // After clearing configuration we should successfully get the
501     // new staging configuration.
502     config = cfg_mgr.getStagingCfg();
503     ASSERT_TRUE(config);
504     EXPECT_EQ(1, config->getSequence());
505 
506     // Modify the staging configuration.
507     config->addLoggingInfo(LoggingInfo());
508     ASSERT_TRUE(config);
509     // The modified staging configuration should have one logger configured.
510     ASSERT_EQ(1, config->getLoggingInfo().size());
511 
512     // Rollback should remove a staging configuration, including the logger.
513     ASSERT_NO_THROW(cfg_mgr.rollback());
514 
515     // Make sure that the logger is not set. This is an indication that the
516     // rollback worked.
517     config = cfg_mgr.getStagingCfg();
518     ASSERT_TRUE(config);
519     EXPECT_EQ(0, config->getLoggingInfo().size());
520 }
521 
522 // This test verifies that it is possible to revert to an old configuration.
TEST_F(CfgMgrTest,revert)523 TEST_F(CfgMgrTest, revert) {
524     CfgMgr& cfg_mgr = CfgMgr::instance();
525     // Let's create 5 unique configurations: differing by a debug level in the
526     // range of 10 to 14.
527     for (int i = 0; i < 5; ++i) {
528         SrvConfigPtr config = cfg_mgr.getStagingCfg();
529         LoggingInfo logging_info;
530         logging_info.debuglevel_ = i + 10;
531         config->addLoggingInfo(logging_info);
532         cfg_mgr.commit();
533     }
534 
535     // Now we have 6 configurations with:
536     // - debuglevel = 99 (a default one)
537     // - debuglevel = 10
538     // - debuglevel = 11
539     // - debuglevel = 12
540     // - debuglevel = 13
541     // - debuglevel = 14 (current)
542 
543     // Hence, the maximum index of the configuration to revert is 5 (which
544     // points to the configuration with debuglevel = 99). For the index greater
545     // than 5 we should get an exception.
546     ASSERT_THROW(cfg_mgr.revert(6), isc::OutOfRange);
547     // Value of 0 also doesn't make sense.
548     ASSERT_THROW(cfg_mgr.revert(0), isc::OutOfRange);
549 
550     // We should be able to revert to configuration with debuglevel = 10.
551     ASSERT_NO_THROW(cfg_mgr.revert(4));
552     // And this configuration should be now the current one and the debuglevel
553     // of this configuration is 10.
554     EXPECT_EQ(10, cfg_mgr.getCurrentCfg()->getLoggingInfo()[0].debuglevel_);
555     EXPECT_NE(cfg_mgr.getCurrentCfg()->getSequence(), 1);
556 
557     // The new set of configuration is now as follows:
558     // - debuglevel = 99
559     // - debuglevel = 10
560     // - debuglevel = 11
561     // - debuglevel = 12
562     // - debuglevel = 13
563     // - debuglevel = 14
564     // - debuglevel = 10 (current)
565     // So, reverting to configuration having index 3 means that the debug level
566     // of the current configuration will become 12.
567     ASSERT_NO_THROW(cfg_mgr.revert(3));
568     EXPECT_EQ(12, cfg_mgr.getCurrentCfg()->getLoggingInfo()[0].debuglevel_);
569 }
570 
571 // This test verifies that the address family can be set and obtained
572 // from the configuration manager.
TEST_F(CfgMgrTest,family)573 TEST_F(CfgMgrTest, family) {
574     ASSERT_EQ(AF_INET, CfgMgr::instance().getFamily());
575 
576     CfgMgr::instance().setFamily(AF_INET6);
577     ASSERT_EQ(AF_INET6, CfgMgr::instance().getFamily());
578 
579     CfgMgr::instance().setFamily(AF_INET);
580     EXPECT_EQ(AF_INET, CfgMgr::instance().getFamily());
581 }
582 
583 // This test verifies that once the configuration is committed, statistics
584 // are updated appropriately.
TEST_F(CfgMgrTest,commitStats4)585 TEST_F(CfgMgrTest, commitStats4) {
586     CfgMgr& cfg_mgr = CfgMgr::instance();
587     StatsMgr& stats_mgr = StatsMgr::instance();
588     startBackend(AF_INET);
589 
590     // Let's prepare the "old" configuration: a subnet with id 123
591     // and pretend there were addresses assigned, so statistics are non-zero.
592     Subnet4Ptr subnet1(new Subnet4(IOAddress("192.1.2.0"), 24, 1, 2, 3, 123));
593     CfgSubnets4Ptr subnets = cfg_mgr.getStagingCfg()->getCfgSubnets4();
594     subnets->add(subnet1);
595     cfg_mgr.commit();
596     stats_mgr.addValue("subnet[123].total-addresses", static_cast<int64_t>(256));
597     stats_mgr.setValue("subnet[123].assigned-addresses", static_cast<int64_t>(150));
598 
599     // Now, let's change the configuration to something new.
600 
601     // There's a subnet 192.1.2.0/24 with ID=42
602     Subnet4Ptr subnet2(new Subnet4(IOAddress("192.1.2.0"), 24, 1, 2, 3, 42));
603 
604     // Let's make a pool with 128 addresses available.
605     PoolPtr pool(new Pool4(IOAddress("192.1.2.0"), 25)); // 128 addrs
606     subnet2->addPool(pool);
607 
608     subnets = cfg_mgr.getStagingCfg()->getCfgSubnets4();
609     subnets->add(subnet2);
610 
611     // Change the stats default limits.
612     cfg_mgr.getStagingCfg()->addConfiguredGlobal("statistic-default-sample-count",
613                                                  Element::create(15));
614     cfg_mgr.getStagingCfg()->addConfiguredGlobal("statistic-default-sample-age",
615                                                  Element::create(2));
616 
617     // Let's commit it
618     cfg_mgr.commit();
619 
620     EXPECT_EQ(15, stats_mgr.getMaxSampleCountDefault());
621     EXPECT_EQ("00:00:02", durationToText(stats_mgr.getMaxSampleAgeDefault(), 0));
622 
623     EXPECT_FALSE(stats_mgr.getObservation("subnet[123].total-addresses"));
624     EXPECT_FALSE(stats_mgr.getObservation("subnet[123].assigned-addresses"));
625 
626     ObservationPtr total_addrs;
627     EXPECT_NO_THROW(total_addrs = stats_mgr.getObservation("subnet[42].total-addresses"));
628     ASSERT_TRUE(total_addrs);
629     EXPECT_EQ(128, total_addrs->getInteger().first);
630     EXPECT_TRUE(total_addrs->getMaxSampleCount().first);
631     EXPECT_EQ(15, total_addrs->getMaxSampleCount().second);
632     EXPECT_FALSE(total_addrs->getMaxSampleAge().first);
633     EXPECT_EQ("00:00:02", durationToText(total_addrs->getMaxSampleAge().second, 0));
634 }
635 
636 // This test verifies that once the configuration is merged into the current
637 // configuration, statistics are updated appropriately.
TEST_F(CfgMgrTest,mergeIntoCurrentStats4)638 TEST_F(CfgMgrTest, mergeIntoCurrentStats4) {
639     CfgMgr& cfg_mgr = CfgMgr::instance();
640     StatsMgr& stats_mgr = StatsMgr::instance();
641     startBackend(AF_INET);
642 
643     // Let's prepare the "old" configuration: a subnet with id 123
644     // and pretend there were addresses assigned, so statistics are non-zero.
645     Subnet4Ptr subnet1(new Subnet4(IOAddress("192.1.2.0"), 24, 1, 2, 3, 123));
646     CfgSubnets4Ptr subnets = cfg_mgr.getStagingCfg()->getCfgSubnets4();
647     subnets->add(subnet1);
648     cfg_mgr.commit();
649     stats_mgr.addValue("subnet[123].total-addresses", static_cast<int64_t>(256));
650     stats_mgr.setValue("subnet[123].assigned-addresses", static_cast<int64_t>(150));
651 
652     // There should be no stats for subnet 42 at this point.
653     EXPECT_FALSE(stats_mgr.getObservation("subnet[42].total-addresses"));
654     EXPECT_FALSE(stats_mgr.getObservation("subnet[42].assigned-addresses"));
655 
656     // Now, let's create new configuration with updates.
657 
658     // There's a subnet 192.1.3.0/24 with ID=42
659     Subnet4Ptr subnet2(new Subnet4(IOAddress("192.1.3.0"), 24, 1, 2, 3, 42));
660 
661     // Let's make a pool with 128 addresses available.
662     PoolPtr pool(new Pool4(IOAddress("192.1.3.0"), 25)); // 128 addrs
663     subnet2->addPool(pool);
664 
665     // Create external configuration to be merged into current one.
666     auto external_cfg = CfgMgr::instance().createExternalCfg();
667     subnets = external_cfg->getCfgSubnets4();
668     subnets->add(subnet2);
669 
670     external_cfg->addConfiguredGlobal("statistic-default-sample-count",
671                                       Element::create(16));
672     external_cfg->addConfiguredGlobal("statistic-default-sample-age",
673                                       Element::create(3));
674 
675     // Let's merge it.
676     cfg_mgr.mergeIntoCurrentCfg(external_cfg->getSequence());
677 
678     // The stats should have been updated and so we should be able to get
679     // observations for subnet 42.
680     EXPECT_EQ(16, stats_mgr.getMaxSampleCountDefault());
681     EXPECT_EQ("00:00:03", durationToText(stats_mgr.getMaxSampleAgeDefault(), 0));
682 
683     EXPECT_TRUE(stats_mgr.getObservation("subnet[42].total-addresses"));
684     EXPECT_TRUE(stats_mgr.getObservation("subnet[42].assigned-addresses"));
685 
686     // And also for 123
687     EXPECT_TRUE(stats_mgr.getObservation("subnet[123].total-addresses"));
688     EXPECT_TRUE(stats_mgr.getObservation("subnet[123].assigned-addresses"));
689 
690     ObservationPtr total_addrs;
691     EXPECT_NO_THROW(total_addrs = stats_mgr.getObservation("subnet[42].total-addresses"));
692     ASSERT_TRUE(total_addrs);
693     EXPECT_EQ(128, total_addrs->getInteger().first);
694 }
695 
696 // This test verifies that once the configuration is cleared, the statistics
697 // are removed.
TEST_F(CfgMgrTest,clearStats4)698 TEST_F(CfgMgrTest, clearStats4) {
699     CfgMgr& cfg_mgr = CfgMgr::instance();
700     StatsMgr& stats_mgr = StatsMgr::instance();
701 
702     // Let's prepare the "old" configuration: a subnet with id 123
703     // and pretend there were addresses assigned, so statistics are non-zero.
704     Subnet4Ptr subnet1(new Subnet4(IOAddress("192.1.2.0"), 24, 1, 2, 3, 123));
705     CfgSubnets4Ptr subnets = cfg_mgr.getStagingCfg()->getCfgSubnets4();
706     subnets->add(subnet1);
707     cfg_mgr.commit();
708     stats_mgr.addValue("subnet[123].total-addresses", static_cast<int64_t>(256));
709     stats_mgr.setValue("subnet[123].assigned-addresses", static_cast<int64_t>(150));
710 
711     // The stats should be there.
712     EXPECT_TRUE(stats_mgr.getObservation("subnet[123].total-addresses"));
713     EXPECT_TRUE(stats_mgr.getObservation("subnet[123].assigned-addresses"));
714 
715     // Let's remove all configurations
716     cfg_mgr.clear();
717 
718     // The stats should not be there anymore.
719     EXPECT_FALSE(stats_mgr.getObservation("subnet[123].total-addresses"));
720     EXPECT_FALSE(stats_mgr.getObservation("subnet[123].assigned-addresses"));
721 }
722 
723 // This test verifies that once the configuration is committed, statistics
724 // are updated appropriately.
TEST_F(CfgMgrTest,commitStats6)725 TEST_F(CfgMgrTest, commitStats6) {
726     CfgMgr& cfg_mgr = CfgMgr::instance();
727     StatsMgr& stats_mgr = StatsMgr::instance();
728     startBackend(AF_INET6);
729 
730     // Let's prepare the "old" configuration: a subnet with id 123
731     // and pretend there were addresses assigned, so statistics are non-zero.
732     Subnet6Ptr subnet1(new Subnet6(IOAddress("2001:db8:1::"), 48, 1, 2, 3, 4, 123));
733     CfgSubnets6Ptr subnets = cfg_mgr.getStagingCfg()->getCfgSubnets6();
734     subnets->add(subnet1);
735     cfg_mgr.commit();
736     stats_mgr.addValue("subnet[123].total-nas", static_cast<int64_t>(256));
737     stats_mgr.setValue("subnet[123].assigned-nas", static_cast<int64_t>(150));
738 
739     stats_mgr.addValue("subnet[123].total-pds", static_cast<int64_t>(256));
740     stats_mgr.setValue("subnet[123].assigned-pds", static_cast<int64_t>(150));
741 
742     // Now, let's change the configuration to something new.
743 
744     // There's a subnet 2001:db8:2::/48 with ID=42
745     Subnet6Ptr subnet2(new Subnet6(IOAddress("2001:db8:2::"), 48, 1, 2, 3, 4, 42));
746 
747     // Let's make pools with 128 addresses and 65536 prefixes available.
748     PoolPtr pool1(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:2::"), 121)); // 128 addrs
749     PoolPtr pool2(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:3::"), 96, 112)); // 65536 prefixes
750     subnet2->addPool(pool1);
751     subnet2->addPool(pool2);
752 
753     subnets = cfg_mgr.getStagingCfg()->getCfgSubnets6();
754     subnets->add(subnet2);
755 
756     // Change the stats default limits.
757     cfg_mgr.getStagingCfg()->addConfiguredGlobal("statistic-default-sample-count",
758                                                  Element::create(14));
759     cfg_mgr.getStagingCfg()->addConfiguredGlobal("statistic-default-sample-age",
760                                                  Element::create(10));
761 
762     // Let's commit it
763     cfg_mgr.commit();
764 
765     EXPECT_EQ(14, stats_mgr.getMaxSampleCountDefault());
766     EXPECT_EQ("00:00:10", durationToText(stats_mgr.getMaxSampleAgeDefault(), 0));
767 
768     EXPECT_FALSE(stats_mgr.getObservation("subnet[123].total-nas"));
769     EXPECT_FALSE(stats_mgr.getObservation("subnet[123].assigned-nas"));
770 
771     EXPECT_FALSE(stats_mgr.getObservation("subnet[123].total-pds"));
772     EXPECT_FALSE(stats_mgr.getObservation("subnet[123].assigned-pds"));
773 
774     ObservationPtr total_addrs;
775     EXPECT_NO_THROW(total_addrs = stats_mgr.getObservation("subnet[42].total-nas"));
776     ASSERT_TRUE(total_addrs);
777     EXPECT_EQ(128, total_addrs->getInteger().first);
778     EXPECT_TRUE(total_addrs->getMaxSampleCount().first);
779     EXPECT_EQ(14, total_addrs->getMaxSampleCount().second);
780     EXPECT_FALSE(total_addrs->getMaxSampleAge().first);
781     EXPECT_EQ("00:00:10", durationToText(total_addrs->getMaxSampleAge().second, 0));
782 
783     ObservationPtr total_prfx;
784     EXPECT_NO_THROW(total_prfx = stats_mgr.getObservation("subnet[42].total-pds"));
785     ASSERT_TRUE(total_prfx);
786     EXPECT_EQ(65536, total_prfx->getInteger().first);
787     EXPECT_TRUE(total_prfx->getMaxSampleCount().first);
788     EXPECT_EQ(14, total_prfx->getMaxSampleCount().second);
789     EXPECT_FALSE(total_prfx->getMaxSampleAge().first);
790     EXPECT_EQ("00:00:10", durationToText(total_prfx->getMaxSampleAge().second, 0));
791 }
792 
793 // This test verifies that once the configuration is merged into the current
794 // configuration, statistics are updated appropriately.
795 /// @todo Enable this test once merging v6 configuration is enabled.
TEST_F(CfgMgrTest,DISABLED_mergeIntoCurrentStats6)796 TEST_F(CfgMgrTest, DISABLED_mergeIntoCurrentStats6) {
797     CfgMgr& cfg_mgr = CfgMgr::instance();
798     StatsMgr& stats_mgr = StatsMgr::instance();
799     startBackend(AF_INET6);
800 
801     // Let's prepare the "old" configuration: a subnet with id 123
802     // and pretend there were addresses assigned, so statistics are non-zero.
803     Subnet6Ptr subnet1(new Subnet6(IOAddress("2001:db8:1::"), 48, 1, 2, 3, 4, 123));
804     CfgSubnets6Ptr subnets = cfg_mgr.getStagingCfg()->getCfgSubnets6();
805     subnets->add(subnet1);
806     cfg_mgr.commit();
807     stats_mgr.addValue("subnet[123].total-nas", static_cast<int64_t>(256));
808     stats_mgr.setValue("subnet[123].assigned-nas", static_cast<int64_t>(150));
809 
810     stats_mgr.addValue("subnet[123].total-pds", static_cast<int64_t>(256));
811     stats_mgr.setValue("subnet[123].assigned-pds", static_cast<int64_t>(150));
812 
813     // There should be no stats for subnet 42 at this point.
814     EXPECT_FALSE(stats_mgr.getObservation("subnet[42].total-nas"));
815     EXPECT_FALSE(stats_mgr.getObservation("subnet[42].assigned-nas"));
816     EXPECT_FALSE(stats_mgr.getObservation("subnet[42].total-pds"));
817     EXPECT_FALSE(stats_mgr.getObservation("subnet[42].assigned-pds"));
818 
819     // Now, let's create new configuration with updates.
820 
821     // There's a subnet 2001:db8:2::/48 with ID=42
822     Subnet6Ptr subnet2(new Subnet6(IOAddress("2001:db8:2::"), 48, 1, 2, 3, 4, 42));
823 
824     // Let's make pools with 128 addresses and 65536 prefixes available.
825     PoolPtr pool1(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:2::"), 121)); // 128 addrs
826     PoolPtr pool2(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:3::"), 96, 112)); // 65536 prefixes
827     subnet2->addPool(pool1);
828     subnet2->addPool(pool2);
829 
830     // Create external configuration to be merged into current one.
831     auto external_cfg = CfgMgr::instance().createExternalCfg();
832     subnets = external_cfg->getCfgSubnets6();
833     subnets->add(subnet2);
834 
835     external_cfg->addConfiguredGlobal("statistic-default-sample-count",
836                                       Element::create(17));
837     external_cfg->addConfiguredGlobal("statistic-default-sample-age",
838                                       Element::create(4));
839 
840     // Let's merge it.
841     cfg_mgr.mergeIntoCurrentCfg(external_cfg->getSequence());
842 
843     EXPECT_EQ(17, stats_mgr.getMaxSampleCountDefault());
844     EXPECT_EQ("00:00:04", durationToText(stats_mgr.getMaxSampleAgeDefault(), 0));
845 
846     EXPECT_TRUE(stats_mgr.getObservation("subnet[42].total-nas"));
847     EXPECT_TRUE(stats_mgr.getObservation("subnet[42].assigned-nas"));
848     EXPECT_TRUE(stats_mgr.getObservation("subnet[42].total-pds"));
849     EXPECT_TRUE(stats_mgr.getObservation("subnet[42].assigned-pds"));
850 
851     EXPECT_TRUE(stats_mgr.getObservation("subnet[123].total-nas"));
852     EXPECT_TRUE(stats_mgr.getObservation("subnet[123].assigned-nas"));
853     EXPECT_TRUE(stats_mgr.getObservation("subnet[123].total-pds"));
854     EXPECT_TRUE(stats_mgr.getObservation("subnet[123].assigned-pds"));
855 
856     ObservationPtr total_addrs;
857     EXPECT_NO_THROW(total_addrs = stats_mgr.getObservation("subnet[42].total-nas"));
858     ASSERT_TRUE(total_addrs);
859     EXPECT_EQ(128, total_addrs->getInteger().first);
860 
861     EXPECT_NO_THROW(total_addrs = stats_mgr.getObservation("subnet[42].total-pds"));
862     ASSERT_TRUE(total_addrs);
863     EXPECT_EQ(65536, total_addrs->getInteger().first);
864 }
865 
866 // This test verifies that once the configuration is cleared, the v6 statistics
867 // are removed.
TEST_F(CfgMgrTest,clearStats6)868 TEST_F(CfgMgrTest, clearStats6) {
869     CfgMgr& cfg_mgr = CfgMgr::instance();
870     StatsMgr& stats_mgr = StatsMgr::instance();
871 
872     // Let's prepare the "old" configuration: a subnet with id 123
873     // and pretend there were addresses assigned, so statistics are non-zero.
874     Subnet6Ptr subnet1(new Subnet6(IOAddress("2001:db8:1::"), 48, 1, 2, 3, 4, 123));
875     CfgSubnets6Ptr subnets = cfg_mgr.getStagingCfg()->getCfgSubnets6();
876     subnets->add(subnet1);
877     cfg_mgr.commit();
878     stats_mgr.addValue("subnet[123].total-nas", static_cast<int64_t>(256));
879     stats_mgr.setValue("subnet[123].assigned-nas", static_cast<int64_t>(150));
880 
881     stats_mgr.addValue("subnet[123].total-pds", static_cast<int64_t>(256));
882     stats_mgr.setValue("subnet[123].assigned-pds", static_cast<int64_t>(150));
883 
884     // The stats should be there.
885     EXPECT_TRUE(stats_mgr.getObservation("subnet[123].total-nas"));
886     EXPECT_TRUE(stats_mgr.getObservation("subnet[123].assigned-nas"));
887 
888     EXPECT_TRUE(stats_mgr.getObservation("subnet[123].total-pds"));
889     EXPECT_TRUE(stats_mgr.getObservation("subnet[123].assigned-pds"));
890 
891     // Let's remove all configurations
892     cfg_mgr.clear();
893 
894     // The stats should not be there anymore.
895     EXPECT_FALSE(stats_mgr.getObservation("subnet[123].total-nas"));
896     EXPECT_FALSE(stats_mgr.getObservation("subnet[123].assigned-nas"));
897 
898     EXPECT_FALSE(stats_mgr.getObservation("subnet[123].total-pds"));
899     EXPECT_FALSE(stats_mgr.getObservation("subnet[123].assigned-pds"));
900 }
901 
902 // This test verifies that the external configuration can be merged into
903 // the staging configuration via CfgMgr.
TEST_F(CfgMgrTest,mergeIntoStagingCfg)904 TEST_F(CfgMgrTest, mergeIntoStagingCfg) {
905     CfgMgr& cfg_mgr = CfgMgr::instance();
906 
907     // Create first external configuration.
908     SrvConfigPtr ext_cfg1;
909     ASSERT_NO_THROW(ext_cfg1 = cfg_mgr.createExternalCfg());
910     ASSERT_TRUE(ext_cfg1);
911     // It should pick the first available sequence number.
912     EXPECT_EQ(0, ext_cfg1->getSequence());
913 
914     // Create second external configuration.
915     SrvConfigPtr ext_cfg2;
916     ASSERT_NO_THROW(ext_cfg2 = cfg_mgr.createExternalCfg());
917     ASSERT_TRUE(ext_cfg2);
918     // It should pick the next available sequence number.
919     EXPECT_EQ(1, ext_cfg2->getSequence());
920 
921     // Those must be two separate instances.
922     ASSERT_FALSE(ext_cfg1 == ext_cfg2);
923 
924     // Add a subnet which will be merged from first configuration.
925     Subnet4Ptr subnet1(new Subnet4(IOAddress("192.1.2.0"), 24, 1, 2, 3, 123));
926     ext_cfg1->getCfgSubnets4()->add(subnet1);
927 
928     // Add a subnet which will be merged from the second configuration.
929     Subnet4Ptr subnet2(new Subnet4(IOAddress("192.1.3.0"), 24, 1, 2, 3, 124));
930     ext_cfg2->getCfgSubnets4()->add(subnet2);
931 
932     // Merge first configuration.
933     ASSERT_NO_THROW(cfg_mgr.mergeIntoStagingCfg(ext_cfg1->getSequence()));
934     // Second attempt should fail because the configuration is discarded after
935     // the merge.
936     ASSERT_THROW(cfg_mgr.mergeIntoStagingCfg(ext_cfg1->getSequence()), BadValue);
937 
938     // Check that the subnet from first configuration has been merged but not
939     // from the second configuration.
940     ASSERT_TRUE(cfg_mgr.getStagingCfg()->getCfgSubnets4()->getBySubnetId(123));
941     ASSERT_FALSE(cfg_mgr.getStagingCfg()->getCfgSubnets4()->getBySubnetId(124));
942 
943     // Create another configuration instance to check what sequence it would
944     // pick. It should pick the first available one.
945     SrvConfigPtr ext_cfg3;
946     ASSERT_NO_THROW(ext_cfg3 = cfg_mgr.createExternalCfg());
947     ASSERT_TRUE(ext_cfg3);
948     EXPECT_EQ(2, ext_cfg3->getSequence());
949 
950     // Merge the second and third (empty) configuration.
951     ASSERT_NO_THROW(cfg_mgr.mergeIntoStagingCfg(ext_cfg2->getSequence()));
952     ASSERT_NO_THROW(cfg_mgr.mergeIntoStagingCfg(ext_cfg3->getSequence()));
953 
954     // Make sure that both subnets have been merged.
955     ASSERT_TRUE(cfg_mgr.getStagingCfg()->getCfgSubnets4()->getBySubnetId(123));
956     ASSERT_TRUE(cfg_mgr.getStagingCfg()->getCfgSubnets4()->getBySubnetId(124));
957 
958     // The next configuration instance should reset the sequence to 0 because
959     // there are no other configurations in CfgMgr.
960     SrvConfigPtr ext_cfg4;
961     ASSERT_NO_THROW(ext_cfg4 = cfg_mgr.createExternalCfg());
962     ASSERT_TRUE(ext_cfg4);
963     EXPECT_EQ(0, ext_cfg4->getSequence());
964 
965     // Try to commit the staging configuration.
966     ASSERT_NO_THROW(cfg_mgr.commit());
967 
968     // Make sure that both subnets are present in the current configuration.
969     EXPECT_TRUE(cfg_mgr.getCurrentCfg()->getCfgSubnets4()->getBySubnetId(123));
970     EXPECT_TRUE(cfg_mgr.getCurrentCfg()->getCfgSubnets4()->getBySubnetId(124));
971 
972     // The staging configuration should not include them.
973     EXPECT_FALSE(cfg_mgr.getStagingCfg()->getCfgSubnets4()->getBySubnetId(123));
974     EXPECT_FALSE(cfg_mgr.getStagingCfg()->getCfgSubnets4()->getBySubnetId(124));
975 }
976 
977 // This test verifies that the external configuration can be merged into
978 // the current configuration via CfgMgr.
TEST_F(CfgMgrTest,mergeIntoCurrentCfg)979 TEST_F(CfgMgrTest, mergeIntoCurrentCfg) {
980     CfgMgr& cfg_mgr = CfgMgr::instance();
981 
982     // Create first external configuration.
983     SrvConfigPtr ext_cfg1;
984     ASSERT_NO_THROW(ext_cfg1 = cfg_mgr.createExternalCfg());
985     ASSERT_TRUE(ext_cfg1);
986     // It should pick the first available sequence number.
987     EXPECT_EQ(0, ext_cfg1->getSequence());
988 
989     // Create second external configuration.
990     SrvConfigPtr ext_cfg2;
991     ASSERT_NO_THROW(ext_cfg2 = cfg_mgr.createExternalCfg());
992     ASSERT_TRUE(ext_cfg2);
993     // It should pick the next available sequence number.
994     EXPECT_EQ(1, ext_cfg2->getSequence());
995 
996     // Those must be two separate instances.
997     ASSERT_FALSE(ext_cfg1 == ext_cfg2);
998 
999     // Add a subnet which will be merged from first configuration.
1000     Subnet4Ptr subnet1(new Subnet4(IOAddress("192.1.2.0"), 24, 1, 2, 3, 123));
1001     ext_cfg1->getCfgSubnets4()->add(subnet1);
1002 
1003     // Add a subnet which will be merged from the second configuration.
1004     Subnet4Ptr subnet2(new Subnet4(IOAddress("192.1.3.0"), 24, 1, 2, 3, 124));
1005     ext_cfg2->getCfgSubnets4()->add(subnet2);
1006 
1007     // Merge first configuration.
1008     ASSERT_NO_THROW(cfg_mgr.mergeIntoCurrentCfg(ext_cfg1->getSequence()));
1009     // Second attempt should fail because the configuration is discarded after
1010     // the merge.
1011     ASSERT_THROW(cfg_mgr.mergeIntoCurrentCfg(ext_cfg1->getSequence()), BadValue);
1012 
1013     // Check that the subnet from first configuration has been merged but not
1014     // from the second configuration.
1015     ASSERT_TRUE(cfg_mgr.getCurrentCfg()->getCfgSubnets4()->getBySubnetId(123));
1016     ASSERT_FALSE(cfg_mgr.getCurrentCfg()->getCfgSubnets4()->getBySubnetId(124));
1017 
1018     // Create another configuration instance to check what sequence it would
1019     // pick. It should pick the first available one.
1020     SrvConfigPtr ext_cfg3;
1021     ASSERT_NO_THROW(ext_cfg3 = cfg_mgr.createExternalCfg());
1022     ASSERT_TRUE(ext_cfg3);
1023     EXPECT_EQ(2, ext_cfg3->getSequence());
1024 
1025     // Merge the second and third (empty) configuration.
1026     ASSERT_NO_THROW(cfg_mgr.mergeIntoCurrentCfg(ext_cfg2->getSequence()));
1027     ASSERT_NO_THROW(cfg_mgr.mergeIntoCurrentCfg(ext_cfg3->getSequence()));
1028 
1029     // Make sure that both subnets have been merged.
1030     ASSERT_TRUE(cfg_mgr.getCurrentCfg()->getCfgSubnets4()->getBySubnetId(123));
1031     ASSERT_TRUE(cfg_mgr.getCurrentCfg()->getCfgSubnets4()->getBySubnetId(124));
1032 
1033     // The next configuration instance should reset the sequence to 0 because
1034     // there are no other configurations in CfgMgr.
1035     SrvConfigPtr ext_cfg4;
1036     ASSERT_NO_THROW(ext_cfg4 = cfg_mgr.createExternalCfg());
1037     ASSERT_TRUE(ext_cfg4);
1038     EXPECT_EQ(0, ext_cfg4->getSequence());
1039 }
1040 
1041 /// @todo Add unit-tests for testing:
1042 /// - addActiveIface() with invalid interface name
1043 /// - addActiveIface() with the same interface twice
1044 /// - addActiveIface() with a bogus address
1045 ///
1046 /// This is somewhat tricky. Care should be taken here, because it is rather
1047 /// difficult to decide if interface name is valid or not. Some servers, e.g.
1048 /// dibbler, allow to specify interface names that are not currently present in
1049 /// the system. The server accepts them, but upon discovering that they are
1050 /// yet available (for different definitions of not being available), adds
1051 /// the to to-be-activated list.
1052 ///
1053 /// Cases covered by dibbler are:
1054 /// - missing interface (e.g. PPP connection that is not established yet)
1055 /// - downed interface (no link local address, no way to open sockets)
1056 /// - up, but not running interface (wifi up, but not associated)
1057 /// - tentative addresses (interface up and running, but DAD procedure is
1058 ///   still in progress)
1059 /// - weird interfaces without link-local addresses (don't ask, 6rd tunnels
1060 ///   look weird to me as well)
1061 
1062 // No specific tests for getSubnet6. That method (2 overloaded versions) is tested
1063 // in Dhcpv6SrvTest.selectSubnetAddr and Dhcpv6SrvTest.selectSubnetIface
1064 // (see src/bin/dhcp6/tests/dhcp6_srv_unittest.cc)
1065 
1066 } // end of anonymous namespace
1067