1 // Copyright (C) 2013-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 <asiolink/io_service.h>
10 #include <d2srv/testutils/nc_test_utils.h>
11 #include <d2/d2_update_mgr.h>
12 #include <d2/nc_add.h>
13 #include <d2/nc_remove.h>
14 #include <d2/simple_add.h>
15 #include <d2/simple_remove.h>
16 #include <process/testutils/d_test_stubs.h>
17 #include <util/time_utilities.h>
18 
19 #include <gtest/gtest.h>
20 #include <algorithm>
21 #include <vector>
22 
23 using namespace std;
24 using namespace isc;
25 using namespace isc::dhcp_ddns;
26 using namespace isc::d2;
27 using namespace isc::process;
28 using namespace isc::util;
29 
30 namespace {
31 
32 /// @brief Wrapper class for D2UpdateMgr providing access to non-public methods.
33 ///
34 /// This class facilitates testing by making non-public methods accessible so
35 /// they can be invoked directly in test routines.
36 class D2UpdateMgrWrapper : public D2UpdateMgr {
37 public:
38     /// @brief Constructor
39     ///
40     /// Parameters match those needed by D2UpdateMgr.
D2UpdateMgrWrapper(D2QueueMgrPtr & queue_mgr,D2CfgMgrPtr & cfg_mgr,asiolink::IOServicePtr & io_service,const size_t max_transactions=MAX_TRANSACTIONS_DEFAULT)41     D2UpdateMgrWrapper(D2QueueMgrPtr& queue_mgr, D2CfgMgrPtr& cfg_mgr,
42                        asiolink::IOServicePtr& io_service,
43                        const size_t max_transactions = MAX_TRANSACTIONS_DEFAULT)
44         : D2UpdateMgr(queue_mgr, cfg_mgr, io_service, max_transactions) {
45     }
46 
47     /// @brief Destructor
~D2UpdateMgrWrapper()48     virtual ~D2UpdateMgrWrapper() {
49     }
50 
51     // Expose the protected methods to be tested.
52     using D2UpdateMgr::checkFinishedTransactions;
53     using D2UpdateMgr::pickNextJob;
54     using D2UpdateMgr::makeTransaction;
55 };
56 
57 /// @brief Defines a pointer to a D2UpdateMgr instance.
58 typedef boost::shared_ptr<D2UpdateMgrWrapper> D2UpdateMgrWrapperPtr;
59 
60 /// @brief Test fixture for testing D2UpdateMgr.
61 ///
62 /// Note this class uses D2UpdateMgrWrapper class to exercise non-public
63 /// aspects of D2UpdateMgr. D2UpdateMgr depends on both D2QueueMgr and
64 /// D2CfgMgr.  This fixture provides an instance of each, plus a canned,
65 /// valid DHCP_DDNS configuration sufficient to test D2UpdateMgr's basic
66 /// functions.
67 class D2UpdateMgrTest : public TimedIO, public ConfigParseTest {
68 public:
69     D2QueueMgrPtr queue_mgr_;
70     D2CfgMgrPtr cfg_mgr_;
71     D2UpdateMgrWrapperPtr update_mgr_;
72     std::vector<NameChangeRequestPtr> canned_ncrs_;
73     size_t canned_count_;
74 
D2UpdateMgrTest()75     D2UpdateMgrTest() {
76         queue_mgr_.reset(new D2QueueMgr(io_service_));
77         cfg_mgr_.reset(new D2CfgMgr());
78         update_mgr_.reset(new D2UpdateMgrWrapper(queue_mgr_, cfg_mgr_,
79                                                  io_service_));
80         makeCannedNcrs();
81         makeCannedConfig();
82     }
83 
~D2UpdateMgrTest()84     ~D2UpdateMgrTest() {
85     }
86 
87     /// @brief Creates a list of valid NameChangeRequest.
88     ///
89     /// This method builds a list of NameChangeRequests from a single
90     /// JSON string request. Each request is assigned a unique DHCID.
makeCannedNcrs()91     void makeCannedNcrs() {
92         const char* msg_str =
93         "{"
94         " \"change-type\" : 0 , "
95         " \"forward-change\" : true , "
96         " \"reverse-change\" : false , "
97         " \"fqdn\" : \"my.example.com.\" , "
98         " \"ip-address\" : \"192.168.1.2\" , "
99         " \"dhcid\" : \"0102030405060708\" , "
100         " \"lease-expires-on\" : \"20130121132405\" , "
101         " \"lease-length\" : 1300, "
102         " \"use-conflict-resolution\" : true "
103         "}";
104 
105         const char* dhcids[] = { "111111", "222222", "333333", "444444" };
106         canned_count_ = 4;
107         for (int i = 0; i < canned_count_; i++) {
108             dhcp_ddns::NameChangeRequestPtr ncr = NameChangeRequest::
109                                                   fromJSON(msg_str);
110             ncr->setDhcid(dhcids[i]);
111             ncr->setChangeType(i % 2 == 0 ?
112                                dhcp_ddns::CHG_ADD : dhcp_ddns::CHG_REMOVE);
113             canned_ncrs_.push_back(ncr);
114         }
115     }
116 
117     /// @brief Seeds configuration manager with a valid DHCP_DDNS configuration.
makeCannedConfig()118     void makeCannedConfig() {
119         std::string canned_config_ =
120                  "{ "
121                   "\"ip-address\" : \"192.168.1.33\" , "
122                   "\"port\" : 88 , "
123                   "\"tsig-keys\": [] ,"
124                   "\"forward-ddns\" : {"
125                   " \"ddns-domains\": [ "
126                   "  { \"name\": \"example.com.\" , "
127                   "   \"dns-servers\" : [ "
128                   "    { \"ip-address\": \"127.0.0.1\", \"port\" : 5301  } "
129                   "   ] },"
130                   "  { \"name\": \"org.\" , "
131                   "   \"dns-servers\" : [ "
132                   "    { \"ip-address\": \"127.0.0.1\" } "
133                   "    ] }"
134                   "  ] }, "
135                   "\"reverse-ddns\" : { "
136                   " \"ddns-domains\": [ "
137                   "  { \"name\": \"1.168.192.in-addr.arpa.\" , "
138                   "   \"dns-servers\" : [ "
139                   "    { \"ip-address\": \"127.0.0.1\", \"port\" : 5301 } "
140                   "   ] }, "
141                   "  { \"name\": \"2.0.3.0.8.B.D.0.1.0.0.2.ip6.arpa.\" , "
142                   "   \"dns-servers\" : [ "
143                   "    { \"ip-address\": \"127.0.0.1\" } "
144                   "    ] } "
145                   "  ] } }";
146 
147         // If this configuration fails to parse most tests will fail.
148         ASSERT_TRUE(fromJSON(canned_config_));
149         answer_ = cfg_mgr_->simpleParseConfig(config_set_);
150         ASSERT_TRUE(checkAnswer(0));
151     }
152 
153     /// @brief Fakes the completion of a given transaction.
154     ///
155     /// @param index index of the request from which the transaction was formed.
156     /// @param status completion status to assign to the request
completeTransaction(const size_t index,const dhcp_ddns::NameChangeStatus & status)157     void completeTransaction(const size_t index,
158                              const dhcp_ddns::NameChangeStatus& status) {
159         // add test on index
160         if (index >= canned_count_) {
161             ADD_FAILURE() << "request index is out of range: " << index;
162         }
163 
164         const dhcp_ddns::D2Dhcid key = canned_ncrs_[index]->getDhcid();
165 
166         // locate the transaction based on the request DHCID
167         TransactionList::iterator pos = update_mgr_->findTransaction(key);
168         if (pos == update_mgr_->transactionListEnd()) {
169             ADD_FAILURE() << "cannot find transaction for key: " << key.toStr();
170         }
171 
172         NameChangeTransactionPtr trans = (*pos).second;
173         // Update the status of the request
174         trans->getNcr()->setStatus(status);
175         // End the model.
176         trans->endModel();
177     }
178 
179     /// @brief Determines if any transactions are waiting for IO completion.
180     ///
181     /// @returns True if isModelWaiting() is true for at least one of the current
182     /// transactions.
anyoneWaiting()183     bool anyoneWaiting() {
184         TransactionList::iterator it = update_mgr_->transactionListBegin();
185         while (it != update_mgr_->transactionListEnd()) {
186             if (((*it).second)->isModelWaiting()) {
187                 return true;
188             }
189         }
190 
191         return false;
192     }
193 
194     /// @brief Process events until all requests have been completed.
195     ///
196     /// This method iteratively calls D2UpdateMgr::sweep and executes
197     /// IOService calls until both the request queue and transaction list
198     /// are empty or a timeout occurs.  Note that in addition to the safety
199     /// timer, the number of passes through the loop is also limited to
200     /// a given number.  This is a failsafe to guard against an infinite loop
201     /// in the test.
processAll(size_t max_passes=100)202     void processAll(size_t max_passes = 100) {
203         // Loop until all the transactions have been dequeued and run through to
204         // completion.
205         size_t passes = 0;
206         size_t handlers = 0;
207 
208         // Set the timeout to slightly more than DNSClient timeout to allow
209         // timeout processing to occur naturally.
210         size_t timeout = cfg_mgr_->getD2Params()->getDnsServerTimeout() + 100;
211         while (update_mgr_->getQueueCount() ||
212             update_mgr_->getTransactionCount()) {
213             ++passes;
214             update_mgr_->sweep();
215             // If any transactions are waiting on IO, run the service.
216             if (anyoneWaiting()) {
217                 int cnt = runTimedIO(timeout);
218 
219                 // If cnt is zero then the service stopped unexpectedly.
220                 if (cnt == 0) {
221                     ADD_FAILURE()
222                         << "processALL: IO service stopped unexpectedly,"
223                         << " passes: " << passes << ", handlers executed: "
224                         << handlers;
225                 }
226 
227                 handlers += cnt;
228             }
229 
230             // This is a last resort fail safe to ensure we don't go around
231             // forever. We cut it off the number of passes at 100 (default
232             // value).  This is roughly ten times the number for the longest
233             // test (currently, multiTransactionTimeout).
234             if (passes > max_passes) {
235                 FAIL() << "processALL failed, too many passes: "
236                        << passes <<  ", total handlers executed: " << handlers;
237             }
238         }
239     }
240 
241 };
242 
243 /// @brief Tests the D2UpdateMgr construction.
244 /// This test verifies that:
245 /// 1. Construction with invalid queue manager is not allowed
246 /// 2. Construction with invalid configuration manager is not allowed
247 /// 3. Construction with max transactions of zero is not allowed
248 /// 4. Default construction works and max transactions is defaulted properly
249 /// 5. Construction with custom max transactions works properly
TEST(D2UpdateMgr,construction)250 TEST(D2UpdateMgr, construction) {
251     asiolink::IOServicePtr io_service(new isc::asiolink::IOService());
252     D2QueueMgrPtr queue_mgr;
253     D2CfgMgrPtr cfg_mgr;
254     D2UpdateMgrPtr update_mgr;
255 
256     // Verify that constructor fails if given an invalid queue manager.
257     ASSERT_NO_THROW(cfg_mgr.reset(new D2CfgMgr()));
258     EXPECT_THROW(D2UpdateMgr(queue_mgr, cfg_mgr, io_service),
259                  D2UpdateMgrError);
260 
261     // Verify that constructor fails if given an invalid config manager.
262     ASSERT_NO_THROW(queue_mgr.reset(new D2QueueMgr(io_service)));
263     ASSERT_NO_THROW(cfg_mgr.reset());
264     EXPECT_THROW(D2UpdateMgr(queue_mgr, cfg_mgr, io_service),
265                  D2UpdateMgrError);
266 
267     ASSERT_NO_THROW(cfg_mgr.reset(new D2CfgMgr()));
268 
269     // Verify that constructor fails with invalid io_service.
270     io_service.reset();
271     EXPECT_THROW(D2UpdateMgr(queue_mgr, cfg_mgr, io_service),
272                  D2UpdateMgrError);
273     io_service.reset(new isc::asiolink::IOService());
274 
275     // Verify that max transactions cannot be zero.
276     EXPECT_THROW(D2UpdateMgr(queue_mgr, cfg_mgr, io_service, 0),
277                  D2UpdateMgrError);
278 
279     // Verify that given valid values, constructor works.
280     ASSERT_NO_THROW(update_mgr.reset(new D2UpdateMgr(queue_mgr, cfg_mgr,
281                                                       io_service)));
282 
283     // Verify that max transactions defaults properly.
284     EXPECT_EQ(D2UpdateMgr::MAX_TRANSACTIONS_DEFAULT,
285               update_mgr->getMaxTransactions());
286 
287 
288     // Verify that constructor permits custom  max transactions.
289     ASSERT_NO_THROW(update_mgr.reset(new D2UpdateMgr(queue_mgr, cfg_mgr,
290                                                      io_service, 100)));
291 
292     // Verify that max transactions is correct.
293     EXPECT_EQ(100, update_mgr->getMaxTransactions());
294 }
295 
296 /// @brief Tests the D2UpdateManager's transaction list services
297 /// This test verifies that:
298 /// 1. A transaction can be added to the list.
299 /// 2. Finding a transaction in the list by key works correctly.
300 /// 3. Looking for a non-existent transaction works properly.
301 /// 4. Attempting to add a transaction for a DHCID already in the list fails.
302 /// 5. Removing a transaction by key works properly.
303 /// 6. Attempting to remove an non-existent transaction does no harm.
TEST_F(D2UpdateMgrTest,transactionList)304 TEST_F(D2UpdateMgrTest, transactionList) {
305     // Grab a canned request for test purposes.
306     NameChangeRequestPtr& ncr = canned_ncrs_[0];
307     TransactionList::iterator pos;
308 
309     // Verify that we can add a transaction.
310     EXPECT_NO_THROW(update_mgr_->makeTransaction(ncr));
311     EXPECT_EQ(1, update_mgr_->getTransactionCount());
312 
313     // Verify that we can find a transaction by key.
314     EXPECT_NO_THROW(pos = update_mgr_->findTransaction(ncr->getDhcid()));
315     EXPECT_TRUE(pos != update_mgr_->transactionListEnd());
316 
317     // Verify that convenience method has same result.
318     EXPECT_TRUE(update_mgr_->hasTransaction(ncr->getDhcid()));
319 
320     // Verify that we will not find a transaction that isn't there.
321     dhcp_ddns::D2Dhcid bogus_id("FFFF");
322     EXPECT_NO_THROW(pos = update_mgr_->findTransaction(bogus_id));
323     EXPECT_TRUE(pos == update_mgr_->transactionListEnd());
324 
325     // Verify that convenience method has same result.
326     EXPECT_FALSE(update_mgr_->hasTransaction(bogus_id));
327 
328     // Verify that adding a transaction for the same key fails.
329     EXPECT_THROW(update_mgr_->makeTransaction(ncr), D2UpdateMgrError);
330     EXPECT_EQ(1, update_mgr_->getTransactionCount());
331 
332     // Verify the we can remove a transaction by key.
333     EXPECT_NO_THROW(update_mgr_->removeTransaction(ncr->getDhcid()));
334     EXPECT_EQ(0, update_mgr_->getTransactionCount());
335 
336     // Verify the we can try to remove a non-existent transaction without harm.
337     EXPECT_NO_THROW(update_mgr_->removeTransaction(ncr->getDhcid()));
338 }
339 
340 /// @brief Checks transaction creation when both update directions are enabled.
341 /// Verifies that when both directions are enabled and servers are matched to
342 /// the request, that the transaction is created with both directions turned on.
TEST_F(D2UpdateMgrTest,bothEnabled)343 TEST_F(D2UpdateMgrTest, bothEnabled) {
344     // Grab a canned request for test purposes.
345     NameChangeRequestPtr& ncr = canned_ncrs_[0];
346     ncr->setReverseChange(true);
347 
348     // Verify we are requesting both directions.
349     ASSERT_TRUE(ncr->isForwardChange());
350     ASSERT_TRUE(ncr->isReverseChange());
351 
352     // Verify both both directions are enabled.
353     ASSERT_TRUE(cfg_mgr_->forwardUpdatesEnabled());
354     ASSERT_TRUE(cfg_mgr_->reverseUpdatesEnabled());
355 
356     // Attempt to make a transaction.
357     ASSERT_NO_THROW(update_mgr_->makeTransaction(ncr));
358 
359     // Verify we create a transaction with both directions turned on.
360     EXPECT_EQ(1, update_mgr_->getTransactionCount());
361     EXPECT_TRUE(ncr->isForwardChange());
362     EXPECT_TRUE(ncr->isReverseChange());
363 }
364 
365 /// @brief Checks transaction creation when reverse updates are disabled.
366 /// Verifies that when reverse updates are disabled, and there matching forward
367 /// servers, that the transaction is still created but with only the forward
368 /// direction turned on.
TEST_F(D2UpdateMgrTest,reverseDisable)369 TEST_F(D2UpdateMgrTest, reverseDisable) {
370     // Make a NCR which requests both directions.
371     NameChangeRequestPtr& ncr = canned_ncrs_[0];
372     ncr->setReverseChange(true);
373 
374     // Wipe out forward domain list.
375     DdnsDomainMapPtr emptyDomains(new DdnsDomainMap());
376     cfg_mgr_->getD2CfgContext()->getReverseMgr()->setDomains(emptyDomains);
377 
378     // Verify enable methods are correct.
379     ASSERT_TRUE(cfg_mgr_->forwardUpdatesEnabled());
380     ASSERT_FALSE(cfg_mgr_->reverseUpdatesEnabled());
381 
382     // Attempt to make a transaction.
383     ASSERT_NO_THROW(update_mgr_->makeTransaction(ncr));
384 
385     // Verify we create a transaction with only forward turned on.
386     EXPECT_EQ(1, update_mgr_->getTransactionCount());
387     EXPECT_TRUE(ncr->isForwardChange());
388     EXPECT_FALSE(ncr->isReverseChange());
389 }
390 
391 /// @brief Checks transaction creation when forward updates are disabled.
392 /// Verifies that when forward updates are disabled, and there matching reverse
393 /// servers, that the transaction is still created but with only the reverse
394 /// direction turned on.
TEST_F(D2UpdateMgrTest,forwardDisabled)395 TEST_F(D2UpdateMgrTest, forwardDisabled) {
396     // Make a NCR which requests both directions.
397     NameChangeRequestPtr& ncr = canned_ncrs_[0];
398     ncr->setReverseChange(true);
399 
400     // Wipe out forward domain list.
401     DdnsDomainMapPtr emptyDomains(new DdnsDomainMap());
402     cfg_mgr_->getD2CfgContext()->getForwardMgr()->setDomains(emptyDomains);
403 
404     // Verify enable methods are correct.
405     ASSERT_FALSE(cfg_mgr_->forwardUpdatesEnabled());
406     ASSERT_TRUE(cfg_mgr_->reverseUpdatesEnabled());
407 
408     // Attempt to make a transaction.
409     ASSERT_NO_THROW(update_mgr_->makeTransaction(ncr));
410 
411     // Verify we create a transaction with only reverse turned on.
412     EXPECT_EQ(1, update_mgr_->getTransactionCount());
413     EXPECT_FALSE(ncr->isForwardChange());
414     EXPECT_TRUE(ncr->isReverseChange());
415 }
416 
417 
418 /// @brief Checks transaction creation when neither update direction is enabled.
419 /// Verifies that transactions are not created when both forward and reverse
420 /// directions are disabled.
TEST_F(D2UpdateMgrTest,bothDisabled)421 TEST_F(D2UpdateMgrTest, bothDisabled) {
422     // Grab a canned request for test purposes.
423     NameChangeRequestPtr& ncr = canned_ncrs_[0];
424     ncr->setReverseChange(true);
425     TransactionList::iterator pos;
426 
427     // Wipe out both forward and reverse domain lists.
428     DdnsDomainMapPtr emptyDomains(new DdnsDomainMap());
429     cfg_mgr_->getD2CfgContext()->getForwardMgr()->setDomains(emptyDomains);
430     cfg_mgr_->getD2CfgContext()->getReverseMgr()->setDomains(emptyDomains);
431 
432     // Verify enable methods are correct.
433     EXPECT_FALSE(cfg_mgr_->forwardUpdatesEnabled());
434     EXPECT_FALSE(cfg_mgr_->reverseUpdatesEnabled());
435 
436     // Attempt to make a transaction.
437     ASSERT_NO_THROW(update_mgr_->makeTransaction(ncr));
438 
439     // Verify that do not create a transaction.
440     EXPECT_EQ(0, update_mgr_->getTransactionCount());
441 }
442 
443 /// @brief Tests D2UpdateManager's checkFinishedTransactions method.
444 /// This test verifies that:
445 /// 1. Completed transactions are removed from the transaction list.
446 /// 2. Failed transactions are removed from the transaction list.
447 /// @todo This test will need to expand if and when checkFinishedTransactions
448 /// method expands to do more than remove them from the list.
TEST_F(D2UpdateMgrTest,checkFinishedTransaction)449 TEST_F(D2UpdateMgrTest, checkFinishedTransaction) {
450     // Ensure we have at least 4 canned requests with which to work.
451     ASSERT_TRUE(canned_count_ >= 4);
452 
453     // Create a transaction for each canned request.
454     for (int i = 0; i < canned_count_; i++) {
455         EXPECT_NO_THROW(update_mgr_->makeTransaction(canned_ncrs_[i]));
456     }
457     // Verify we have that the transaction count is correct.
458     EXPECT_EQ(canned_count_, update_mgr_->getTransactionCount());
459 
460     // Verify that all four transactions have been started.
461     TransactionList::iterator pos;
462     EXPECT_NO_THROW(pos = update_mgr_->transactionListBegin());
463     while (pos != update_mgr_->transactionListEnd()) {
464         NameChangeTransactionPtr trans = (*pos).second;
465         ASSERT_EQ(dhcp_ddns::ST_PENDING, trans->getNcrStatus());
466         ASSERT_TRUE(trans->isModelRunning());
467         ++pos;
468     }
469 
470     // Verify that invoking checkFinishedTransactions does not throw.
471     EXPECT_NO_THROW(update_mgr_->checkFinishedTransactions());
472 
473     // Since nothing is running IOService, the all four transactions should
474     // still be in the list.
475     EXPECT_EQ(canned_count_, update_mgr_->getTransactionCount());
476 
477     // Now "complete" two of the four.
478     // Simulate a successful completion.
479     completeTransaction(1, dhcp_ddns::ST_COMPLETED);
480 
481     // Simulate a failed completion.
482     completeTransaction(3, dhcp_ddns::ST_FAILED);
483 
484     // Verify that invoking checkFinishedTransactions does not throw.
485     EXPECT_NO_THROW(update_mgr_->checkFinishedTransactions());
486 
487     // Verify that the list of transactions has decreased by two.
488     EXPECT_EQ(canned_count_ - 2, update_mgr_->getTransactionCount());
489 
490     // Verify that the transaction list is correct.
491     EXPECT_TRUE(update_mgr_->hasTransaction(canned_ncrs_[0]->getDhcid()));
492     EXPECT_FALSE(update_mgr_->hasTransaction(canned_ncrs_[1]->getDhcid()));
493     EXPECT_TRUE(update_mgr_->hasTransaction(canned_ncrs_[2]->getDhcid()));
494     EXPECT_FALSE(update_mgr_->hasTransaction(canned_ncrs_[3]->getDhcid()));
495 }
496 
497 /// @brief Tests D2UpdateManager's pickNextJob method.
498 /// This test verifies that:
499 /// 1. pickNextJob will select and make transactions from NCR queue.
500 /// 2. Requests are removed from the queue once selected
501 /// 3. Requests for DHCIDs with transactions already in progress are not
502 /// selected.
503 /// 4. Requests with no matching servers are removed from the queue and
504 /// discarded.
TEST_F(D2UpdateMgrTest,pickNextJob)505 TEST_F(D2UpdateMgrTest, pickNextJob) {
506     // Ensure we have at least 4 canned requests with which to work.
507     ASSERT_TRUE(canned_count_ >= 4);
508 
509     // Put each transaction on the queue.
510     for (int i = 0; i < canned_count_; i++) {
511         ASSERT_NO_THROW(queue_mgr_->enqueue(canned_ncrs_[i]));
512     }
513 
514     // Invoke pickNextJob canned_count_ times which should create a
515     // transaction for each canned ncr.
516     for (int i = 0; i < canned_count_; i++) {
517         EXPECT_NO_THROW(update_mgr_->pickNextJob());
518         EXPECT_EQ(i + 1, update_mgr_->getTransactionCount());
519         EXPECT_TRUE(update_mgr_->hasTransaction(canned_ncrs_[i]->getDhcid()));
520     }
521 
522     // Verify that the queue has been drained.
523     EXPECT_EQ(0, update_mgr_->getQueueCount());
524 
525     // Now verify that a subsequent request for a DCHID  for which a
526     // transaction is in progress, is not dequeued.
527     // First add the "subsequent" request.
528     dhcp_ddns::NameChangeRequestPtr
529         subsequent_ncr(new dhcp_ddns::NameChangeRequest(*(canned_ncrs_[2])));
530     EXPECT_NO_THROW(queue_mgr_->enqueue(subsequent_ncr));
531     EXPECT_EQ(1, update_mgr_->getQueueCount());
532 
533     // Verify that invoking pickNextJob:
534     // 1. Does not throw
535     // 2. Does not make a new transaction
536     // 3. Does not dequeue the entry
537     EXPECT_NO_THROW(update_mgr_->pickNextJob());
538     EXPECT_EQ(canned_count_, update_mgr_->getTransactionCount());
539     EXPECT_EQ(1, update_mgr_->getQueueCount());
540 
541     // Clear out the queue and transaction list.
542     queue_mgr_->clearQueue();
543     update_mgr_->clearTransactionList();
544 
545     // Make a forward change NCR with an FQDN that has no forward match.
546     dhcp_ddns::NameChangeRequestPtr
547         bogus_ncr(new dhcp_ddns::NameChangeRequest(*(canned_ncrs_[0])));
548     bogus_ncr->setForwardChange(true);
549     bogus_ncr->setReverseChange(false);
550     bogus_ncr->setFqdn("bogus.forward.domain.com");
551 
552     // Put it on the queue up
553     ASSERT_NO_THROW(queue_mgr_->enqueue(bogus_ncr));
554 
555     // Verify that invoking pickNextJob:
556     // 1. Does not throw
557     // 2. Does not make a new transaction
558     // 3. Does dequeue the entry
559     EXPECT_NO_THROW(update_mgr_->pickNextJob());
560     EXPECT_EQ(0, update_mgr_->getTransactionCount());
561     EXPECT_EQ(0, update_mgr_->getQueueCount());
562 
563     // Make a reverse change NCR with an FQDN that has no reverse match.
564     bogus_ncr.reset(new dhcp_ddns::NameChangeRequest(*(canned_ncrs_[0])));
565     bogus_ncr->setForwardChange(false);
566     bogus_ncr->setReverseChange(true);
567     bogus_ncr->setIpAddress("77.77.77.77");
568 
569     // Verify that invoking pickNextJob:
570     // 1. does not throw
571     // 2. Does not make a new transaction
572     // 3. Does dequeue the entry
573     EXPECT_NO_THROW(update_mgr_->pickNextJob());
574     EXPECT_EQ(0, update_mgr_->getTransactionCount());
575     EXPECT_EQ(0, update_mgr_->getQueueCount());
576 }
577 
578 /// @brief Tests D2UpdateManager's sweep method.
579 /// Since sweep is primarily a wrapper around checkFinishedTransactions and
580 /// pickNextJob, along with checks on maximum transaction limits, it mostly
581 /// verifies that these three pieces work together to move process jobs.
582 /// Most of what is tested here is tested above.
TEST_F(D2UpdateMgrTest,sweep)583 TEST_F(D2UpdateMgrTest, sweep) {
584     // Ensure we have at least 4 canned requests with which to work.
585     ASSERT_TRUE(canned_count_ >= 4);
586 
587     // Set max transactions to same as current transaction count.
588     EXPECT_NO_THROW(update_mgr_->setMaxTransactions(canned_count_));
589     EXPECT_EQ(canned_count_, update_mgr_->getMaxTransactions());
590 
591     // Put each transaction on the queue.
592     for (int i = 0; i < canned_count_; i++) {
593         EXPECT_NO_THROW(queue_mgr_->enqueue(canned_ncrs_[i]));
594     }
595 
596     // Invoke sweep canned_count_ times which should create a
597     // transaction for each canned ncr.
598     for (int i = 0; i < canned_count_; i++) {
599         EXPECT_NO_THROW(update_mgr_->sweep());
600         EXPECT_EQ(i + 1, update_mgr_->getTransactionCount());
601         EXPECT_TRUE(update_mgr_->hasTransaction(canned_ncrs_[i]->getDhcid()));
602     }
603 
604     // Verify that the queue has been drained.
605     EXPECT_EQ(0, update_mgr_->getQueueCount());
606 
607     // Verify max transactions can't be less than current transaction count.
608     EXPECT_THROW(update_mgr_->setMaxTransactions(1), D2UpdateMgrError);
609 
610     // Queue up a request for a DHCID which has a transaction in progress.
611     dhcp_ddns::NameChangeRequestPtr
612         subsequent_ncr(new dhcp_ddns::NameChangeRequest(*(canned_ncrs_[2])));
613     EXPECT_NO_THROW(queue_mgr_->enqueue(subsequent_ncr));
614     EXPECT_EQ(1, update_mgr_->getQueueCount());
615 
616     // Verify that invoking sweep, does not dequeue the job nor make a
617     // transaction for it.
618     EXPECT_NO_THROW(update_mgr_->sweep());
619     EXPECT_EQ(canned_count_, update_mgr_->getTransactionCount());
620     EXPECT_EQ(1, update_mgr_->getQueueCount());
621 
622     // Mark the transaction complete.
623     completeTransaction(2, dhcp_ddns::ST_COMPLETED);
624 
625     // Verify that invoking sweep, cleans up the completed transaction,
626     // dequeues the queued job and adds its transaction to the list.
627     EXPECT_NO_THROW(update_mgr_->sweep());
628     EXPECT_EQ(canned_count_, update_mgr_->getTransactionCount());
629     EXPECT_EQ(0, update_mgr_->getQueueCount());
630 
631     // Queue up a request from a new DHCID.
632     dhcp_ddns::NameChangeRequestPtr
633         another_ncr(new dhcp_ddns::NameChangeRequest(*(canned_ncrs_[0])));
634     another_ncr->setDhcid("AABBCCDDEEFF");
635     EXPECT_NO_THROW(queue_mgr_->enqueue(another_ncr));
636     EXPECT_EQ(1, update_mgr_->getQueueCount());
637 
638     // Verify that sweep does not dequeue the new request as we are at
639     // maximum transaction count.
640     EXPECT_NO_THROW(update_mgr_->sweep());
641     EXPECT_EQ(canned_count_, update_mgr_->getTransactionCount());
642     EXPECT_EQ(1, update_mgr_->getQueueCount());
643 
644     // Set max transactions to same as current transaction count.
645     EXPECT_NO_THROW(update_mgr_->setMaxTransactions(canned_count_ + 1));
646 
647     // Verify that invoking sweep, dequeues the request and creates
648     // a transaction for it.
649     EXPECT_NO_THROW(update_mgr_->sweep());
650     EXPECT_EQ(canned_count_ + 1, update_mgr_->getTransactionCount());
651     EXPECT_EQ(0, update_mgr_->getQueueCount());
652 
653     // Verify that clearing transaction list works.
654     EXPECT_NO_THROW(update_mgr_->clearTransactionList());
655     EXPECT_EQ(0, update_mgr_->getTransactionCount());
656 }
657 
658 /// @brief Tests integration of NameAddTransaction
659 /// This test verifies that update manager can create and manage a
660 /// NameAddTransaction from start to finish.  It utilizes a fake server
661 /// which responds to all requests sent with NOERROR, simulating a
662 /// successful addition.  The transaction processes both forward and
663 /// reverse changes.
TEST_F(D2UpdateMgrTest,addTransaction)664 TEST_F(D2UpdateMgrTest, addTransaction) {
665     // Put each transaction on the queue.
666     canned_ncrs_[0]->setChangeType(dhcp_ddns::CHG_ADD);
667     canned_ncrs_[0]->setReverseChange(true);
668     ASSERT_NO_THROW(queue_mgr_->enqueue(canned_ncrs_[0]));
669 
670     // Call sweep once, this should:
671     // 1. Dequeue the request
672     // 2. Create the transaction
673     // 3. Start the transaction
674     ASSERT_NO_THROW(update_mgr_->sweep());
675 
676     // Get a copy of the transaction.
677     TransactionList::iterator pos = update_mgr_->transactionListBegin();
678     ASSERT_TRUE (pos != update_mgr_->transactionListEnd());
679     NameChangeTransactionPtr trans = (*pos).second;
680     ASSERT_TRUE(trans);
681 
682     // Verify the correct type of transaction was created.
683     NameAddTransaction* t = dynamic_cast<NameAddTransaction*>(trans.get());
684     ASSERT_TRUE(t);
685 
686     // At this point the transaction should have constructed
687     // and sent the DNS request.
688     ASSERT_TRUE(trans->getCurrentServer());
689     ASSERT_TRUE(trans->isModelRunning());
690     ASSERT_EQ(1, trans->getUpdateAttempts());
691     ASSERT_EQ(StateModel::NOP_EVT, trans->getNextEvent());
692 
693     // Create a server based on the transaction's current server, and
694     // start it listening.
695     FauxServer server(*io_service_, *(trans->getCurrentServer()));
696     server.receive(FauxServer::USE_RCODE, dns::Rcode::NOERROR());
697 
698     // Run sweep and IO until everything is done.
699     processAll();
700 
701     // Verify that model succeeded.
702     EXPECT_FALSE(trans->didModelFail());
703 
704     // Both completion flags should be true.
705     EXPECT_TRUE(trans->getForwardChangeCompleted());
706     EXPECT_TRUE(trans->getReverseChangeCompleted());
707 
708     // Verify that we went through success state.
709     EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
710               trans->getPrevState());
711     EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
712               trans->getLastEvent());
713 }
714 
715 /// @brief Tests integration of NameRemoveTransaction
716 /// This test verifies that update manager can create and manage a
717 /// NameRemoveTransaction from start to finish.  It utilizes a fake server
718 /// which responds to all requests sent with NOERROR, simulating a
719 /// successful addition.  The transaction processes both forward and
720 /// reverse changes.
TEST_F(D2UpdateMgrTest,removeTransaction)721 TEST_F(D2UpdateMgrTest, removeTransaction) {
722     // Put each transaction on the queue.
723     canned_ncrs_[0]->setChangeType(dhcp_ddns::CHG_REMOVE);
724     canned_ncrs_[0]->setReverseChange(true);
725     ASSERT_NO_THROW(queue_mgr_->enqueue(canned_ncrs_[0]));
726 
727     // Call sweep once, this should:
728     // 1. Dequeue the request
729     // 2. Create the transaction
730     // 3. Start the transaction
731     ASSERT_NO_THROW(update_mgr_->sweep());
732 
733     // Get a copy of the transaction.
734     TransactionList::iterator pos = update_mgr_->transactionListBegin();
735     ASSERT_TRUE (pos != update_mgr_->transactionListEnd());
736     NameChangeTransactionPtr trans = (*pos).second;
737     ASSERT_TRUE(trans);
738 
739     // Verify the correct type of transaction was created.
740     NameRemoveTransaction* t = dynamic_cast<NameRemoveTransaction*>(trans.get());
741     ASSERT_TRUE(t);
742 
743     // At this point the transaction should have constructed
744     // and sent the DNS request.
745     ASSERT_TRUE(trans->getCurrentServer());
746     ASSERT_TRUE(trans->isModelRunning());
747     ASSERT_EQ(1, trans->getUpdateAttempts());
748     ASSERT_EQ(StateModel::NOP_EVT, trans->getNextEvent());
749 
750     // Create a server based on the transaction's current server,
751     // and start it listening.
752     FauxServer server(*io_service_, *(trans->getCurrentServer()));
753     server.receive(FauxServer::USE_RCODE, dns::Rcode::NOERROR());
754 
755     // Run sweep and IO until everything is done.
756     processAll();
757 
758     // Verify that model succeeded.
759     EXPECT_FALSE(trans->didModelFail());
760 
761     // Both completion flags should be true.
762     EXPECT_TRUE(trans->getForwardChangeCompleted());
763     EXPECT_TRUE(trans->getReverseChangeCompleted());
764 
765     // Verify that we went through success state.
766     EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
767               trans->getPrevState());
768     EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
769               trans->getLastEvent());
770 }
771 
772 
773 /// @brief Tests handling of a transaction which fails.
774 /// This test verifies that update manager correctly concludes a transaction
775 /// which fails to complete successfully.  The failure simulated is repeated
776 /// corrupt responses from the server, which causes an exhaustion of the
777 /// available servers.
TEST_F(D2UpdateMgrTest,errorTransaction)778 TEST_F(D2UpdateMgrTest, errorTransaction) {
779     // Put each transaction on the queue.
780     ASSERT_NO_THROW(queue_mgr_->enqueue(canned_ncrs_[0]));
781 
782     // Call sweep once, this should:
783     // 1. Dequeue the request
784     // 2. Create the transaction
785     // 3. Start the transaction
786     ASSERT_NO_THROW(update_mgr_->sweep());
787 
788     // Get a copy of the transaction.
789     TransactionList::iterator pos = update_mgr_->transactionListBegin();
790     ASSERT_TRUE (pos != update_mgr_->transactionListEnd());
791     NameChangeTransactionPtr trans = (*pos).second;
792     ASSERT_TRUE(trans);
793 
794     ASSERT_TRUE(trans->isModelRunning());
795     ASSERT_EQ(1, trans->getUpdateAttempts());
796     ASSERT_EQ(StateModel::NOP_EVT, trans->getNextEvent());
797     ASSERT_TRUE(trans->getCurrentServer());
798 
799     // Create a server and start it listening.
800     FauxServer server(*io_service_, *(trans->getCurrentServer()));
801     server.receive(FauxServer::CORRUPT_RESP);
802 
803     // Run sweep and IO until everything is done.
804     processAll();
805 
806     // Verify that model succeeded.
807     EXPECT_FALSE(trans->didModelFail());
808 
809     // Both completion flags should be false.
810     EXPECT_FALSE(trans->getForwardChangeCompleted());
811     EXPECT_FALSE(trans->getReverseChangeCompleted());
812 
813     // Verify that we went through success state.
814     EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
815               trans->getPrevState());
816     EXPECT_EQ(NameChangeTransaction::NO_MORE_SERVERS_EVT,
817               trans->getLastEvent());
818 
819 
820 }
821 
822 /// @brief Tests processing of multiple transactions.
823 /// This test verifies that update manager can create and manage a multiple
824 /// transactions, concurrently.  It uses a fake server that responds to all
825 /// requests sent with NOERROR, simulating successful DNS updates. The
826 /// transactions are a mix of both adds and removes.
TEST_F(D2UpdateMgrTest,multiTransaction)827 TEST_F(D2UpdateMgrTest, multiTransaction) {
828     // Queue up all the requests.
829     int test_count = canned_count_;
830     for (int i = test_count; i > 0; i--) {
831         canned_ncrs_[i-1]->setReverseChange(true);
832         ASSERT_NO_THROW(queue_mgr_->enqueue(canned_ncrs_[i-1]));
833     }
834 
835     // Create a server and start it listening. Note this relies on the fact
836     // that all of configured servers have the same address.
837     // and start it listening.
838     asiolink::IOAddress server_ip("127.0.0.1");
839     FauxServer server(*io_service_, server_ip, 5301);
840     server.receive(FauxServer::USE_RCODE, dns::Rcode::NOERROR());
841 
842     // Run sweep and IO until everything is done.
843     processAll();
844 
845     for (int i = 0; i < test_count; i++) {
846         EXPECT_EQ(dhcp_ddns::ST_COMPLETED, canned_ncrs_[i]->getStatus());
847     }
848 }
849 
850 /// @brief Tests processing of multiple transactions.
851 /// This test verifies that update manager can create and manage a multiple
852 /// transactions, concurrently.  It uses a fake server that responds to all
853 /// requests sent with NOERROR, simulating successful DNS updates. The
854 /// transactions are a mix of both adds and removes.
TEST_F(D2UpdateMgrTest,multiTransactionTimeout)855 TEST_F(D2UpdateMgrTest, multiTransactionTimeout) {
856     // Queue up all the requests.
857     int test_count = canned_count_;
858     for (int i = test_count; i > 0; i--) {
859         canned_ncrs_[i-1]->setReverseChange(true);
860         ASSERT_NO_THROW(queue_mgr_->enqueue(canned_ncrs_[i-1]));
861     }
862 
863     // No server is running, so everything will time out.
864 
865     // Run sweep and IO until everything is done.
866     processAll();
867 
868     for (int i = 0; i < test_count; i++) {
869         EXPECT_EQ(dhcp_ddns::ST_FAILED, canned_ncrs_[i]->getStatus());
870     }
871 }
872 
873 /// @brief Tests integration of SimpleAddTransaction
874 /// This test verifies that update manager can create and manage a
875 /// SimpleAddTransaction from start to finish.  It utilizes a fake server
876 /// which responds to all requests sent with NOERROR, simulating a
877 /// successful addition.  The transaction processes both forward and
878 /// reverse changes.
TEST_F(D2UpdateMgrTest,simpleAddTransaction)879 TEST_F(D2UpdateMgrTest, simpleAddTransaction) {
880     // Put each transaction on the queue.
881     canned_ncrs_[0]->setChangeType(dhcp_ddns::CHG_ADD);
882     canned_ncrs_[0]->setReverseChange(true);
883     canned_ncrs_[0]->setConflictResolution(false);
884     ASSERT_NO_THROW(queue_mgr_->enqueue(canned_ncrs_[0]));
885 
886     // Call sweep once, this should:
887     // 1. Dequeue the request
888     // 2. Create the transaction
889     // 3. Start the transaction
890     ASSERT_NO_THROW(update_mgr_->sweep());
891 
892     // Get a copy of the transaction.
893     TransactionList::iterator pos = update_mgr_->transactionListBegin();
894     ASSERT_TRUE (pos != update_mgr_->transactionListEnd());
895     NameChangeTransactionPtr trans = (*pos).second;
896     ASSERT_TRUE(trans);
897 
898     // Verify the correct type of transaction was created.
899     SimpleAddTransaction* t = dynamic_cast<SimpleAddTransaction*>(trans.get());
900     ASSERT_TRUE(t);
901 
902     // At this point the transaction should have constructed
903     // and sent the DNS request.
904     ASSERT_TRUE(trans->getCurrentServer());
905     ASSERT_TRUE(trans->isModelRunning());
906     ASSERT_EQ(1, trans->getUpdateAttempts());
907     ASSERT_EQ(StateModel::NOP_EVT, trans->getNextEvent());
908 
909     // Create a server based on the transaction's current server, and
910     // start it listening.
911     FauxServer server(*io_service_, *(trans->getCurrentServer()));
912     server.receive(FauxServer::USE_RCODE, dns::Rcode::NOERROR());
913 
914     // Run sweep and IO until everything is done.
915     processAll();
916 
917     // Verify that model succeeded.
918     EXPECT_FALSE(trans->didModelFail());
919 
920     // Both completion flags should be true.
921     EXPECT_TRUE(trans->getForwardChangeCompleted());
922     EXPECT_TRUE(trans->getReverseChangeCompleted());
923 
924     // Verify that we went through success state.
925     EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
926               trans->getPrevState());
927     EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
928               trans->getLastEvent());
929 }
930 
931 /// @brief Tests integration of SimpleRemoveTransaction
932 /// This test verifies that update manager can create and manage a
933 /// SimpleRemoveTransaction from start to finish.  It utilizes a fake server
934 /// which responds to all requests sent with NOERROR, simulating a
935 /// successful addition.  The transaction processes both forward and
936 /// reverse changes.
TEST_F(D2UpdateMgrTest,simpleRemoveTransaction)937 TEST_F(D2UpdateMgrTest, simpleRemoveTransaction) {
938     // Put each transaction on the queue.
939     canned_ncrs_[0]->setChangeType(dhcp_ddns::CHG_REMOVE);
940     canned_ncrs_[0]->setReverseChange(true);
941     canned_ncrs_[0]->setConflictResolution(false);
942     ASSERT_NO_THROW(queue_mgr_->enqueue(canned_ncrs_[0]));
943 
944     // Call sweep once, this should:
945     // 1. Dequeue the request
946     // 2. Create the transaction
947     // 3. Start the transaction
948     ASSERT_NO_THROW(update_mgr_->sweep());
949 
950     // Get a copy of the transaction.
951     TransactionList::iterator pos = update_mgr_->transactionListBegin();
952     ASSERT_TRUE (pos != update_mgr_->transactionListEnd());
953     NameChangeTransactionPtr trans = (*pos).second;
954     ASSERT_TRUE(trans);
955 
956     // Verify the correct type of transaction was created.
957     SimpleRemoveTransaction* t = dynamic_cast<SimpleRemoveTransaction*>(trans.get());
958     ASSERT_TRUE(t);
959 
960     // At this point the transaction should have constructed
961     // and sent the DNS request.
962     ASSERT_TRUE(trans->getCurrentServer());
963     ASSERT_TRUE(trans->isModelRunning());
964     ASSERT_EQ(1, trans->getUpdateAttempts());
965     ASSERT_EQ(StateModel::NOP_EVT, trans->getNextEvent());
966 
967     // Create a server based on the transaction's current server,
968     // and start it listening.
969     FauxServer server(*io_service_, *(trans->getCurrentServer()));
970     server.receive(FauxServer::USE_RCODE, dns::Rcode::NOERROR());
971 
972     // Run sweep and IO until everything is done.
973     processAll();
974 
975     // Verify that model succeeded.
976     EXPECT_FALSE(trans->didModelFail());
977 
978     // Both completion flags should be true.
979     EXPECT_TRUE(trans->getForwardChangeCompleted());
980     EXPECT_TRUE(trans->getReverseChangeCompleted());
981 
982     // Verify that we went through success state.
983     EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
984               trans->getPrevState());
985     EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
986               trans->getLastEvent());
987 }
988 
989 }
990