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