12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315 |
- // Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
- //
- // This Source Code Form is subject to the terms of the Mozilla Public
- // License, v. 2.0. If a copy of the MPL was not distributed with this
- // file, You can obtain one at http://mozilla.org/MPL/2.0/.
- #include <config.h>
- #include <dhcp/duid.h>
- #include <dhcp/option_data_types.h>
- #include <dhcp_ddns/ncr_msg.h>
- #include <dhcpsrv/tests/alloc_engine_utils.h>
- #include <dhcpsrv/tests/test_utils.h>
- #include <hooks/hooks_manager.h>
- #include <stats/stats_mgr.h>
- #include <gtest/gtest.h>
- #include <boost/bind.hpp>
- #include <boost/function.hpp>
- #include <boost/static_assert.hpp>
- #include <iomanip>
- #include <sstream>
- #include <time.h>
- #include <unistd.h>
- #include <vector>
- using namespace std;
- using namespace isc;
- using namespace isc::asiolink;
- using namespace isc::dhcp;
- using namespace isc::dhcp::test;
- using namespace isc::dhcp_ddns;
- using namespace isc::hooks;
- using namespace isc::stats;
- namespace {
- /// @brief Number of leases to be initialized for each test.
- ///
- /// This value is expected by some of the tests to be multiple
- /// of 10.
- const unsigned int TEST_LEASES_NUM = 100;
- /// @brief Structure wrapping a lower limit within the collection
- /// of leases.
- ///
- /// We're using this structure rather than a size_t value directly
- /// to make API of the test fixture class more readable, i.e. the
- /// struct name indicates the purpose of the value being held.
- struct LowerBound {
- /// @brief Constructor.
- ///
- /// @param lower_bound Interger value wrapped by the structure.
- explicit LowerBound(const size_t lower_bound)
- : lower_bound_(lower_bound) { };
- /// @brief Operator returning the size_t value wrapped.
- operator size_t() const {
- return (lower_bound_);
- }
- /// @brief Value wrapped in the structure.
- size_t lower_bound_;
- };
- /// @brief Structure wrapping an upper limit within the collection
- /// of leases.
- ///
- /// We're using this structure rather than a size_t value directly
- /// to make API of the test fixture class more readable, i.e. the
- /// struct name indicates the purpose of the value being held.
- struct UpperBound {
- /// @brief Constructor.
- ///
- /// @param lower_bound Interger value wrapped by the structure.
- explicit UpperBound(const size_t upper_bound)
- : upper_bound_(upper_bound) { };
- /// @brief Operator returning the size_t value wrapped.
- operator size_t() const {
- return (upper_bound_);
- }
- /// @brief Value wrapped in the structure.
- size_t upper_bound_;
- };
- /// @brief List holding addresses for executed callouts.
- std::list<IOAddress> callouts_;
- /// @brief Callout argument name for expired lease.
- std::string callout_argument_name("lease4");
- /// @brief Base test fixture class for the lease reclamation routines in the
- /// @c AllocEngine.
- ///
- /// This class implements infrastructure for testing leases reclamation
- /// routines. The lease reclamation routine has the following
- /// characteristic:
- /// - it processes multiple leases,
- /// - leases are processed in certain order,
- /// - number of processed leases may be limited by the parameters,
- /// - maxium duration of the lease reclamation routine may be limited,
- /// - reclaimed leases may be marked as reclaimed or deleted,
- /// - DNS records for some of the leases must be removed when the lease
- /// is reclaimed and DNS updates are enabled,
- /// - hooks must be invoked (if installed) for each reclaimed lease
- /// - statistics must be updated to increase the number of reclaimed
- /// leases and decrease the number of allocated leases
- ///
- /// The typical test requires many leases to be initialized and stored
- /// in the lease database for the test. The test fixture class creates
- /// these leases upon construction. It is possible to modify these
- /// leases to test various properties of the lease reclamation routine
- /// as listed above. For example: some of the leases may be marked
- /// as expired or hostname may be cleared for some of the leases to
- /// check that DNS updates are not generated for them.
- ///
- /// The tests are built around the
- /// @c ExpirationAllocEngineTest::testLeases methods. These methods
- /// verify that the certain operations have been performed by the
- /// lease reclamation routine on selected leases. The leases for which
- /// certain conditions should be met are selected using the "index
- /// algorithms". Various index algorithms are implemented in the
- /// test fixture class as static functions and the algorithm is
- /// selected by passing function pointer to the @c testLeases method.
- ///
- /// Examples of index algorithms are:
- /// - evenLeaseIndex(index) - picks even index numbers,
- /// - oddLeaseIndex(index) - picks odd index numbers,
- /// - allLeasesIndexes(index) - picks all index number.
- ///
- /// For example, the test may use the @c evenLeaseIndex algorithm
- /// to mark leases with even indexes as expired and then test whether
- /// leases with even indexes have been successfully reclaimed.
- ///
- /// The "lease algorithm" verifies if the given lease fulfils the
- /// specific conditions after reclamation. These are the examples of
- /// the lease algorithms:
- /// - leaseExists - lease still exists in the database,
- /// - leaseDoesntExist - lease removed from the database,
- /// - leaseReclaimed - lease exists but has reclaimed status,
- /// - leaseNotReclaimed - lease exists and is not in the reclaimed status,
- /// - leaseDeclined - lease exists and is in declined state,
- /// - dnsUpdateGeneratedForLease - DNS updates generated for lease,
- /// - dnsUpdateNotGeneratedForLease - DNS updates not generated for lease
- ///
- /// The combination of index algorithm and lease algorithm allows for
- /// verifying that the whole sets of leases in the lease database fulfil
- /// certain conditions. For example, it is possible to verify that
- /// after lease reclamation leases with even indexes have state set to
- /// "expired-reclaimed".
- ///
- /// See @c ExpirationAllocEngineTest::testLeases for further details.
- ///
- /// @todo These tests should be extended to cover the following cases:
- /// - declined leases - declined leases expire and should be removed
- /// from the lease database by the lease reclamation routine. See
- /// ticket #3976.
- template<typename LeasePtrType>
- class ExpirationAllocEngineTest : public ::testing::Test {
- public:
- /// @brief Type definition for the lease algorithm.
- typedef boost::function<bool (const LeasePtrType)> LeaseAlgorithmFun;
- /// @brief type definition for the lease index algorithm.
- typedef boost::function<bool (const size_t)> IndexAlgorithmFun;
- /// @brief Constructor.
- ///
- /// Clears configuration, creates new lease manager and allocation engine
- /// instances.
- ExpirationAllocEngineTest(const std::string& lease_mgr_params) {
- // Clear configuration.
- CfgMgr::instance().clear();
- D2ClientConfigPtr cfg(new D2ClientConfig());
- CfgMgr::instance().setD2ClientConfig(cfg);
- // Remove all statistics.
- StatsMgr::instance().resetAll();
- // Set the 'reclaimed-leases' statistics to '0'. This statistics
- // is used by some tests to verify that the leases reclamation
- // routine has been called.
- StatsMgr::instance().setValue("reclaimed-leases",
- static_cast<int64_t>(0));
- // Create lease manager.
- LeaseMgrFactory::create(lease_mgr_params);
- // Create allocation engine instance.
- engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
- 100, true));
- }
- /// @brief Destructor
- ///
- /// Stops D2 client (if running), clears configuration and removes
- /// an instance of the lease manager.
- virtual ~ExpirationAllocEngineTest() {
- // Stop D2 client if running and remove all queued name change
- // requests.
- D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
- if (mgr.amSending()) {
- mgr.stopSender();
- mgr.clearQueue();
- }
- // Clear configuration.
- CfgMgr::instance().clear();
- D2ClientConfigPtr cfg(new D2ClientConfig());
- CfgMgr::instance().setD2ClientConfig(cfg);
- // Remove all statistics.
- StatsMgr::instance().resetAll();
- // Kill lease manager.
- LeaseMgrFactory::destroy();
- // Remove callouts executed.
- callouts_.clear();
- }
- /// @brief Starts D2 client.
- void enableDDNS() const {
- // Start DDNS and assign no-op error handler.
- D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
- D2ClientConfigPtr cfg(new D2ClientConfig());
- cfg->enableUpdates(true);
- mgr.setD2ClientConfig(cfg);
- mgr.startSender(boost::bind(&ExpirationAllocEngineTest::d2ErrorHandler, _1, _2));
- }
- /// @brief No-op error handler for the D2 client.
- static void d2ErrorHandler(const dhcp_ddns::NameChangeSender::Result,
- dhcp_ddns::NameChangeRequestPtr&) {
- }
- /// @brief Marks a lease as expired.
- ///
- /// @param lease_index Lease index. Must be between 0 and
- /// @c TEST_LEASES_NUM.
- /// @param secs Offset of the expiration time since now. For example
- /// a value of 2 would set the lease expiration time to 2 seconds ago.
- void expire(const uint16_t lease_index, const time_t secs) {
- ASSERT_GT(leases_.size(), lease_index);
- // We set the expiration time indirectly by modifying the cltt value.
- leases_[lease_index]->cltt_ = time(NULL) - secs -
- leases_[lease_index]->valid_lft_;
- ASSERT_NO_THROW(updateLease(lease_index));
- }
- /// @brief Changes the owner of a lease.
- ///
- /// This method changes the owner of the lease. It must be implemented in
- /// the derived classes to update the unique identifier(s) in the lease to
- /// point to a different client.
- ///
- /// @param lease_index Lease index. Must be between 0 and
- /// @c TEST_LEASES_NUM.
- virtual void transferOwnership(const uint16_t lease_index) = 0;
- /// @brief Marks lease as expired-reclaimed.
- ///
- /// @param lease_index Lease index. Must be between 0 and
- /// @c TEST_LEASES_NUM.
- /// @param secs Offset of the expiration time since now. For example
- /// a value of 2 would set the lease expiration time to 2 seconds ago.
- void reclaim(const uint16_t lease_index, const time_t secs) {
- ASSERT_GT(leases_.size(), lease_index);
- leases_[lease_index]->cltt_ = time(NULL) - secs -
- leases_[lease_index]->valid_lft_;
- leases_[lease_index]->state_ = Lease::STATE_EXPIRED_RECLAIMED;
- ASSERT_NO_THROW(updateLease(lease_index));
- }
- /// @brief Declines specified lease
- ///
- /// Sets specified lease to declined state and sets its probation-period.
- /// @param lease_index Index of the lease.
- /// @param probation_time value of probation period to be set (in seconds)
- void decline(const uint16_t lease_index, const time_t probation_time) {
- ASSERT_GT(leases_.size(), lease_index);
- leases_[lease_index]->decline(probation_time);
- ASSERT_NO_THROW(updateLease(lease_index));
- }
- /// @brief Updates lease in the lease database.
- ///
- /// @param lease_index Index of the lease.
- virtual void updateLease(const unsigned int lease_index) = 0;
- /// @brief Retrieves lease from the database.
- ///
- /// @param lease_index Index of the lease.
- virtual LeasePtrType getLease(const unsigned int lease_index) const = 0;
- /// @brief Sets subnet id for a lease.
- ///
- /// It also updates statistics of assigned leases in the stats manager.
- ///
- /// @param lease_index Lease index.
- /// @param id New subnet id.
- virtual void setSubnetId(const uint16_t lease_index, const SubnetID& id) = 0;
- /// @brief Wrapper method running lease reclamation routine.
- ///
- /// @param max_leases Maximum number of leases to be reclaimed.
- /// @param timeout Maximum amount of time that the reclaimation routine
- /// may be processing expired leases, expressed in seconds.
- /// @param remove_lease A boolean value indicating if the lease should
- /// be removed when it is reclaimed (if true) or it should be left in the
- /// database in the "expired-reclaimed" state (if false).
- virtual void reclaimExpiredLeases(const size_t max_leases,
- const uint16_t timeout,
- const bool remove_lease) = 0;
- /// @brief Wrapper method for removing expired-reclaimed leases.
- ///
- /// @param secs The minimum amount of time, expressed in seconds,
- /// for the lease to be left in the "expired-reclaimed" state
- /// before it can be removed.
- virtual void deleteExpiredReclaimedLeases(const uint32_t secs) = 0;
- /// @brief Test selected leases using the specified algorithms.
- ///
- /// This function picks leases from the range of 0 thru
- /// @c TEST_LEASES_NUM and selects the ones to be verified using the
- /// specified index algorithm. Selected leases are tested using
- /// the specified lease algorithm.
- ///
- /// @param lease_algorithm Pointer to the lease algorithm function.
- /// @param index_algorithm Pointer to the index algorithm function.
- bool testLeases(const LeaseAlgorithmFun& lease_algorithm,
- const IndexAlgorithmFun& index_algorithm) const {
- // No limits are specified, so test all leases in the range of
- // 0 .. TEST_LEASES_NUM.
- return (testLeases(lease_algorithm, index_algorithm, LowerBound(0),
- UpperBound(TEST_LEASES_NUM)));
- }
- /// @brief Test selected leases using the specified algorithms.
- ///
- /// This function picks leases from the range of @c lower_bound
- /// thru @c upper_bound and selects the ones to be verified using the
- /// specified index algorithm. Selected leases are tested using the
- /// specified lease algorithm.
- ///
- /// @param lease_algorithm Pointer to the lease algorithm function.
- /// @param index_algorithm Pointer to the index algorithm function.
- /// @param lower_bound First index in the range.
- /// @param upper_bound Last + 1 index in the range.
- bool testLeases(const LeaseAlgorithmFun& lease_algorithm,
- const IndexAlgorithmFun& index_algorithm,
- const LowerBound& lower_bound,
- const UpperBound& upper_bound) const {
- // Select leases between the lower_bound and upper_bound.
- for (size_t i = lower_bound; i < upper_bound; ++i) {
- // Get the lease from the lease database.
- LeasePtrType lease = getLease(i);
- // index_algorithm(i) checks if the lease should be checked.
- // If so, check if the lease_algorithm indicates that the
- // lease fulfils a given condition, e.g. is present in the
- // database. If not, return false.
- if (index_algorithm(i) && !lease_algorithm(lease)) {
- return (false);
- }
- }
- // All leases checked, so return true.
- return (true);
- }
- /// @brief Index algorithm selecting even indexes.
- ///
- /// @param index Lease index.
- /// @return true if index is an even number.
- static bool evenLeaseIndex(const size_t index) {
- return (index % 2 == 0);
- }
- /// @brief Index algorithm selecting odd indexes.
- ///
- /// @param index Lease index.
- /// @return true if index is an odd number.
- static bool oddLeaseIndex(const size_t index) {
- return (!evenLeaseIndex(index));
- }
- /// @brief Index algorithm selecting all indexes.
- ///
- /// @param index Lease index.
- /// @return true if the index is in the range of [0 .. TEST_LEASES_NUM).
- static bool allLeaseIndexes(const size_t index) {
- return (index < TEST_LEASES_NUM);
- }
- /// @brief Lease algorithm checking if lease exists.
- ///
- /// @param lease Pointer to lease.
- /// @return true if lease pointer is non-null.
- static bool leaseExists(const LeasePtrType& lease) {
- return (static_cast<bool>(lease));
- }
- /// @brief Lease algorithm checking if lease doesn't exist.
- ///
- /// @param lease Pointer to lease.
- /// @return true if lease pointer is null.
- static bool leaseDoesntExist(const LeasePtrType& lease) {
- return (static_cast<bool>(!lease));
- }
- /// @brief Lease algorithm checking if lease state is expired-reclaimed.
- ///
- /// This algorithm also checks that the FQDN information has been removed
- /// from the lease.
- ///
- /// @param lease Pointer to lease.
- /// @return true if lease state is "expired-reclaimed" and the FQDN
- /// information has been removed from the lease.
- static bool leaseReclaimed(const LeasePtrType& lease) {
- return (lease && lease->stateExpiredReclaimed() &&
- lease->hostname_.empty() && !lease->fqdn_fwd_ &&
- !lease->fqdn_rev_);
- }
- /// @brief Lease algorithm checking if lease state is Declined.
- ///
- /// @param lease Pointer to lease.
- /// @return true if lease state is "declined".
- static bool leaseDeclined(const LeasePtrType& lease) {
- return (lease && lease->stateDeclined());
- }
- /// @brief Lease algorithm checking if lease state is not
- /// expired-reclaimed.
- ///
- /// @param lease Pointer to lease.
- /// @return true if lease state is not "expired-reclaimed".
- static bool leaseNotReclaimed(const LeasePtrType& lease) {
- return (lease && !lease->stateExpiredReclaimed());
- }
- /// @brief Lease algorithm checking if removal name change request
- /// has been generated for lease.
- ///
- /// @param lease Pointer to lease.
- /// @return true if NCR has been generated for the lease.
- static bool dnsUpdateGeneratedForLease(const LeasePtrType& lease) {
- try {
- return (static_cast<bool>(getNCRForLease(lease)));
- } catch (...) {
- // If error occurred, treat it as no match.
- return (false);
- }
- }
- /// @brief Lease algorithm checking if removal name change request
- /// hasn't been generated for lease.
- ///
- /// @param lease Pointer to lease.
- /// @return true if NCR has not been generated for the lease.
- static bool dnsUpdateNotGeneratedForLease(const LeasePtrType& lease) {
- try {
- // Iterate over the generated name change requests and try
- // to find the match with our lease (using IP address). If
- D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
- for (size_t i = 0; i < mgr.getQueueSize(); ++i) {
- const NameChangeRequestPtr& ncr = mgr.peekAt(i);
- // If match found, we treat it as if the test fails
- // because we expected no NCR.
- if (ncr->getIpAddress() == lease->addr_.toText()) {
- return (false);
- }
- }
- } catch (...) {
- return (false);
- }
- // No match found, so we're good.
- return (true);
- }
- /// @brief Lease algorithm checking if callout has been executed for
- /// the expired lease.
- ///
- /// @param lease Pointer to lease.
- /// @return true if callout has been executed for the lease.
- static bool leaseCalloutExecuted(const LeasePtrType& lease) {
- return (std::find(callouts_.begin(), callouts_.end(), lease->addr_) !=
- callouts_.end());
- }
- /// @brief Lease algorithm checking if callout hasn't been executed for
- /// the expired lease.
- ///
- /// @param lease Pointer to lease.
- /// @return true if callout hasn't been executed for the lease.
- static bool leaseCalloutNotExecuted(const LeasePtrType& lease) {
- return (!leaseCalloutExecuted(lease));
- }
- /// @brief Implements "lease{4,6}_expire" callout.
- ///
- /// @param callout_handle Callout handle.
- /// @return Zero.
- static int leaseExpireCallout(CalloutHandle& callout_handle) {
- LeasePtrType lease;
- callout_handle.getArgument(callout_argument_name, lease);
- bool remove_lease = true;
- callout_handle.getArgument("remove_lease", remove_lease);
- // Check if the remove_lease is set to false and assume that the callout
- // has been successfully executed if it is. This is mainly to test
- // that the lease reclamation routine sets this value at all.
- if (!remove_lease) {
- callouts_.push_back(lease->addr_);
- }
- return (0);
- }
- /// @brief Implements "lease{4,6}_expire callout returning skip flag.
- ///
- /// @param callout_handle Callout handle.
- /// @return Zero.
- static int leaseExpireWithSkipCallout(CalloutHandle& callout_handle) {
- leaseExpireCallout(callout_handle);
- callout_handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
- return (0);
- }
- /// @brief Implements "lease{4,6}_expire callout, which lasts at least
- /// 40ms.
- ///
- /// This callout is useful to test scenarios where the reclamation of the
- /// lease needs to take a known amount of time. If the callout is installed
- /// it will take at least 40ms for each lease. It is then possible to calculate
- /// the approximate time that the reclamation of all leases would take and
- /// test that the timeouts for the leases' reclamation work as expected.
- ///
- /// The value of 40ms is relatively high, but it has been selected to
- /// mitigate the problems with usleep on some virtual machines. On those
- /// machines the wakeup from usleep may take significant amount of time,
- /// i.e. usually around 10ms. Thus, the sleep time should be considerably
- /// higher than this delay.
- ///
- /// @param callout_handle Callout handle.
- /// @return Zero.
- static int leaseExpireWithDelayCallout(CalloutHandle& callout_handle) {
- leaseExpireCallout(callout_handle);
- // Delay the return from the callout by 40ms.
- usleep(40000);
- return (0);
- }
- /// @brief Returns removal name change request from the D2 client queue.
- ///
- /// @param lease Pointer to the lease to be matched with NCR.
- ///
- /// @return null pointer if no match found.
- static NameChangeRequestPtr getNCRForLease(const LeasePtrType& lease) {
- // Iterate over the generated name change requests and try
- // to find the match with our lease (using IP address). If
- D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
- for (size_t i = 0; i < mgr.getQueueSize(); ++i) {
- const NameChangeRequestPtr& ncr = mgr.peekAt(i);
- // If match found, return true.
- if ((ncr->getIpAddress() == lease->addr_.toText()) &&
- (ncr->getChangeType() == CHG_REMOVE)) {
- return (ncr);
- }
- }
- return (NameChangeRequestPtr());
- }
- /// @brief Returns index of the lease from the address.
- ///
- /// This method assumes that leases are ordered from the smallest to
- /// the highest address, e.g. 10.0.0.0, 10.0.0.1, 10.0.0.2 etc. The
- /// last two bytes can be used to extract index.
- ///
- /// @param address Address.
- ///
- /// @return index
- static uint16_t getLeaseIndexFromAddress(const IOAddress& address) {
- std::vector<uint8_t> bytes = address.toBytes();
- std::vector<uint8_t>::reverse_iterator bytes_it = bytes.rbegin();
- uint16_t index = static_cast<uint16_t>(*bytes_it) |
- (static_cast<uint16_t>(*(bytes_it + 1)) << 8);
- return (index);
- }
- /// @brief Generates hostname for lease index.
- ///
- /// Generates hostname in the form of "hostXXXX.example.org", where
- /// XXXX is a lease index.
- ///
- /// @param index Lease index.
- ///
- /// @return Generated hostname.
- static std::string generateHostnameForLeaseIndex(const uint16_t index) {
- std::ostringstream hostname_s;
- hostname_s << "host" << std::setw(4) << std::setfill('0')
- << index << ".example.org";
- return (hostname_s.str());
- }
- /// @brief Test that leases can be reclaimed without being removed.
- void testReclaimExpiredLeasesUpdateState() {
- for (unsigned int i = 0; i < TEST_LEASES_NUM; ++i) {
- // Mark leases with even indexes as expired.
- if (evenLeaseIndex(i)) {
- // The higher the index, the more expired the lease.
- expire(i, 10 + i);
- }
- }
- // Run leases reclamation routine on all leases. This should result
- // in setting "expired-reclaimed" state for all leases with even
- // indexes.
- ASSERT_NO_THROW(reclaimExpiredLeases(0, 0, false));
- // Leases with even indexes should be marked as reclaimed.
- EXPECT_TRUE(testLeases(&leaseReclaimed, &evenLeaseIndex));
- // Leases with odd indexes shouldn't be marked as reclaimed.
- EXPECT_TRUE(testLeases(&leaseNotReclaimed, &oddLeaseIndex));
- }
- /// @brief Test that the leases may be reclaimed by being deleted.
- void testReclaimExpiredLeasesDelete() {
- for (unsigned int i = 0; i < TEST_LEASES_NUM; ++i) {
- // Mark leases with even indexes as expired.
- if (evenLeaseIndex(i)) {
- // The higher the index, the more expired the lease.
- expire(i, 10 + i);
- }
- }
- // Run leases reclamation routine on all leases. This should result
- // in removal of all leases with even indexes.
- ASSERT_NO_THROW(reclaimExpiredLeases(0, 0, true));
- // Leases with odd indexes should be retained and their state
- // shouldn't be "expired-reclaimed".
- EXPECT_TRUE(testLeases(&leaseNotReclaimed, &oddLeaseIndex));
- // Leases with even indexes should have been removed.
- EXPECT_TRUE(testLeases(&leaseDoesntExist, &evenLeaseIndex));
- }
- /// @brief Test that it is possible to specify the limit for the number
- /// of reclaimed leases.
- void testReclaimExpiredLeasesLimit() {
- for (unsigned int i = 0; i < TEST_LEASES_NUM; ++i) {
- // Mark all leaes as expired. The higher the index the less
- // expired the lease.
- expire(i, 1000 - i);
- }
- // We will be performing lease reclamation on lease groups of 10.
- // Hence, it is convenient if the number of test leases is a
- // multiple of 10.
- const size_t reclamation_group_size = 10;
- BOOST_STATIC_ASSERT(TEST_LEASES_NUM % reclamation_group_size == 0);
- // Leases will be reclaimed in groups of 10.
- for (unsigned int i = reclamation_group_size; i < TEST_LEASES_NUM;
- i += reclamation_group_size) {
- // Reclaim 10 most expired leases out of TEST_LEASES_NUM. Since
- // leases are ordered from the most expired to the least expired
- // this should reclaim leases between 0 and 9, then 10 and 19 etc.
- ASSERT_NO_THROW(reclaimExpiredLeases(reclamation_group_size,
- 0, false));
- // Check that leases having all indexes between 0 and 9, 19, 29 etc.
- // have been reclaimed.
- EXPECT_TRUE(testLeases(&leaseReclaimed, &allLeaseIndexes,
- LowerBound(0), UpperBound(i)))
- << "check failed for i = " << i;
- // Check that all remaining leases haven't been reclaimed.
- EXPECT_TRUE(testLeases(&leaseNotReclaimed, &allLeaseIndexes,
- LowerBound(i), UpperBound(TEST_LEASES_NUM)))
- << "check failed for i = " << i;
- }
- }
- /// @brief Test that DNS updates are generated for the leases for which
- /// the DNS records exist.
- void testReclaimExpiredLeasesWithDDNS() {
- // DNS must be started for the D2 client to accept NCRs.
- ASSERT_NO_THROW(enableDDNS());
- for (unsigned int i = 0; i < TEST_LEASES_NUM; ++i) {
- // Expire all leases with even indexes.
- if (evenLeaseIndex(i)) {
- // The higher the index, the more expired the lease.
- expire(i, 10 + i);
- }
- }
- // Reclaim all expired leases.
- ASSERT_NO_THROW(reclaimExpiredLeases(0, 0, false));
- // Leases with odd indexes shouldn't be reclaimed.
- EXPECT_TRUE(testLeases(&leaseNotReclaimed, &oddLeaseIndex));
- // Leases with even indexes should be reclaimed.
- EXPECT_TRUE(testLeases(&leaseReclaimed, &evenLeaseIndex));
- // DNS updates (removal NCRs) should be generated for leases with even
- // indexes.
- EXPECT_TRUE(testLeases(&dnsUpdateGeneratedForLease, &evenLeaseIndex));
- // DNS updates (removal NCRs) shouldn't be generated for leases with
- // odd indexes.
- EXPECT_TRUE(testLeases(&dnsUpdateNotGeneratedForLease, &oddLeaseIndex));
- }
- /// @brief Test that DNS updates are only generated for the reclaimed
- /// leases (not for all leases with hostname stored).
- void testReclaimExpiredLeasesWithDDNSAndLimit() {
- // DNS must be started for the D2 client to accept NCRs.
- ASSERT_NO_THROW(enableDDNS());
- for (unsigned int i = 0; i < TEST_LEASES_NUM; ++i) {
- // Expire only leases with even indexes.
- if (evenLeaseIndex(i)) {
- // The higher the index, the more expired the lease.
- expire(i, 10 + i);
- }
- }
- const size_t reclamation_group_size = 10;
- BOOST_STATIC_ASSERT(TEST_LEASES_NUM % reclamation_group_size == 0);
- // Leases will be reclaimed in groups of 10
- for (unsigned int i = 10; i < TEST_LEASES_NUM; i += reclamation_group_size) {
- // Reclaim 10 most expired leases. Note that the leases with the
- // higher index are more expired. For example, if the
- // TEST_LEASES_NUM is equal to 100, the most expired lease will
- // be 98, then 96, 94 etc.
- ASSERT_NO_THROW(reclaimExpiredLeases(reclamation_group_size, 0,
- false));
- // After the first iteration the lower bound is 80, because there
- // will be 10 the most expired leases in this group: 80, 82, 84,
- // 86, 88, 90, 92, 94, 96, 98. For subsequent iterations
- // accordingly.
- int reclaimed_lower_bound = TEST_LEASES_NUM - 2 * i;
- // At some point the lower bound will hit the negative value, which
- // must be corrected to 0.
- if (reclaimed_lower_bound < 0) {
- reclaimed_lower_bound = 0;
- }
- // Leases between the lower bound calculated above and the upper
- // bound of all leases, and having even indexes should have been
- // reclaimed.
- EXPECT_TRUE(testLeases(&leaseReclaimed, &evenLeaseIndex,
- LowerBound(reclaimed_lower_bound),
- UpperBound(TEST_LEASES_NUM)))
- << "check failed for i = " << i;
- // For the same leases we should have generated DNS updates
- // (removal NCRs).
- EXPECT_TRUE(testLeases(&dnsUpdateGeneratedForLease, &evenLeaseIndex,
- LowerBound(reclaimed_lower_bound),
- UpperBound(TEST_LEASES_NUM)))
- << "check failed for i = " << i;
- // Leases with odd indexes (falling between the reclaimed ones)
- // shouldn't have been reclaimed, because they are not expired.
- EXPECT_TRUE(testLeases(&leaseNotReclaimed, &oddLeaseIndex,
- LowerBound(reclaimed_lower_bound),
- UpperBound(TEST_LEASES_NUM)))
- << "check failed for i = " << i;
- EXPECT_TRUE(testLeases(&dnsUpdateNotGeneratedForLease,
- &oddLeaseIndex,
- LowerBound(reclaimed_lower_bound),
- UpperBound(TEST_LEASES_NUM)))
- << "check failed for i = " << i;
- // At early stages of iterations, there should be conitnuous
- // group of leases (expired and not expired) which haven't been
- // reclaimed.
- if (reclaimed_lower_bound > 0) {
- EXPECT_TRUE(testLeases(&leaseNotReclaimed, &allLeaseIndexes,
- LowerBound(0),
- UpperBound(reclaimed_lower_bound)))
- << "check failed for i = " << i;
- EXPECT_TRUE(testLeases(&dnsUpdateNotGeneratedForLease,
- &oddLeaseIndex,
- LowerBound(0),
- UpperBound(reclaimed_lower_bound)));
- }
- }
- }
- /// @brief This test verifies that reclamation routine continues if the
- /// DNS update has failed for some leases.
- void testReclaimExpiredLeasesInvalidHostname() {
- // DNS must be started for the D2 client to accept NCRs.
- ASSERT_NO_THROW(enableDDNS());
- for (size_t i = 0; i < TEST_LEASES_NUM; ++i) {
- // Generate invalid hostname for every other lease.
- if (evenLeaseIndex(i)) {
- // Hostname with two consecutive dots is invalid and may result
- // in exception if the reclamation routine doesn't protect
- // aginst such exceptions.
- std::ostringstream hostname_s;
- hostname_s << "invalid-host" << i << "..example.com";
- leases_[i]->hostname_ = hostname_s.str();
- ASSERT_NO_THROW(updateLease(i));
- }
- // Every lease is expired.
- expire(i, 10 + i);
- }
- // Although we know that some hostnames are broken we don't want the
- // reclamation process to break when it finds a broken record.
- // It should rather continue to process other leases.
- ASSERT_NO_THROW(reclaimExpiredLeases(0, 0, false));
- // All leases should have been reclaimed. Broken DNS entry doesn't
- // warrant that we don't reclaim the lease.
- EXPECT_TRUE(testLeases(&leaseReclaimed, &allLeaseIndexes));
- // The routine should not generate DNS updates for the leases with
- // broken hostname.
- EXPECT_TRUE(testLeases(&dnsUpdateNotGeneratedForLease,
- &evenLeaseIndex));
- // But it should generate DNS updates for the leases with the correct
- // hostname.
- EXPECT_TRUE(testLeases(&dnsUpdateGeneratedForLease, &oddLeaseIndex));
- }
- /// @brief This test verfies that callouts are executed for each expired
- /// lease when installed.
- void testReclaimExpiredLeasesHooks() {
- for (unsigned int i = 0; i < TEST_LEASES_NUM; ++i) {
- if (evenLeaseIndex(i)) {
- expire(i, 1000 - i);
- }
- }
- HookLibsCollection libraries; // no libraries at this time
- HooksManager::loadLibraries(libraries);
- // Install a callout: lease4_expire or lease6_expire.
- std::ostringstream callout_name;
- callout_name << callout_argument_name << "_expire";
- EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
- callout_name.str(), leaseExpireCallout));
- ASSERT_NO_THROW(reclaimExpiredLeases(0, 0, false));
- // Callouts should be executed for leases with even indexes and these
- // leases should be reclaimed.
- EXPECT_TRUE(testLeases(&leaseCalloutExecuted, &evenLeaseIndex));
- EXPECT_TRUE(testLeases(&leaseReclaimed, &evenLeaseIndex));
- // Callouts should not be executed for leases with odd indexes and these
- // leases should not be reclaimed.
- EXPECT_TRUE(testLeases(&leaseCalloutNotExecuted, &oddLeaseIndex));
- EXPECT_TRUE(testLeases(&leaseNotReclaimed, &oddLeaseIndex));
- }
- /// @brief This test verfies that callouts are executed for each expired
- /// lease and that the lease is not reclaimed when skip flag is set.
- void testReclaimExpiredLeasesHooksWithSkip() {
- for (unsigned int i = 0; i < TEST_LEASES_NUM; ++i) {
- if (evenLeaseIndex(i)) {
- expire(i, 1000 - i);
- }
- }
- HookLibsCollection libraries; // no libraries at this time
- HooksManager::loadLibraries(libraries);
- // Install a callout: lease4_expire or lease6_expire.
- std::ostringstream callout_name;
- callout_name << callout_argument_name << "_expire";
- EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
- callout_name.str(), leaseExpireWithSkipCallout));
- ASSERT_NO_THROW(reclaimExpiredLeases(0, 0, false));
- // Callouts should have been executed for leases with even indexes.
- EXPECT_TRUE(testLeases(&leaseCalloutExecuted, &evenLeaseIndex));
- // Callouts should not be executed for leases with odd indexes.
- EXPECT_TRUE(testLeases(&leaseCalloutNotExecuted, &oddLeaseIndex));
- // Leases shouldn't be reclaimed because the callout sets the
- // skip flag for each of them.
- EXPECT_TRUE(testLeases(&leaseNotReclaimed, &allLeaseIndexes));
- }
- /// @brief This test verifies that it is possible to set the timeout for
- /// the execution of the lease reclamation routine.
- void testReclaimExpiredLeasesTimeout(const uint16_t timeout) {
- // Leases are segregated from the most expired to the least expired.
- for (unsigned int i = 0; i < TEST_LEASES_NUM; ++i) {
- expire(i, 2000 - i);
- }
- HookLibsCollection libraries;
- HooksManager::loadLibraries(libraries);
- // Install a callout: lease4_expire or lease6_expire. Each callout
- // takes at least 40ms to run (it uses usleep).
- std::ostringstream callout_name;
- callout_name << callout_argument_name << "_expire";
- EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
- callout_name.str(), leaseExpireWithDelayCallout));
- // Reclaim leases with timeout.
- ASSERT_NO_THROW(reclaimExpiredLeases(0, timeout, false));
- // We reclaimed at most (timeout / 40ms) leases.
- const uint16_t theoretical_reclaimed = static_cast<uint16_t>(timeout / 40);
- // The actual number of leases reclaimed is likely to be lower than
- // the theoretical number. For low theoretical number the adjusted
- // number is always 1. For higher number, it will be 10 less than the
- // theoretical number.
- const uint16_t adjusted_reclaimed = (theoretical_reclaimed > 10 ?
- theoretical_reclaimed - 10 : 1);
- EXPECT_TRUE(testLeases(&leaseCalloutExecuted, &allLeaseIndexes,
- LowerBound(0), UpperBound(adjusted_reclaimed)));
- EXPECT_TRUE(testLeases(&leaseReclaimed, &allLeaseIndexes,
- LowerBound(0), UpperBound(adjusted_reclaimed)));
- EXPECT_TRUE(testLeases(&leaseCalloutNotExecuted, &allLeaseIndexes,
- LowerBound(theoretical_reclaimed + 1),
- UpperBound(TEST_LEASES_NUM)));
- EXPECT_TRUE(testLeases(&leaseNotReclaimed, &allLeaseIndexes,
- LowerBound(theoretical_reclaimed + 1),
- UpperBound(TEST_LEASES_NUM)));
- }
- /// @brief This test verifies that expired-reclaimed leases are removed
- /// from the lease database.
- void testDeleteExpiredReclaimedLeases() {
- for (unsigned int i = 0; i < TEST_LEASES_NUM; ++i) {
- // Mark leases with even indexes as expired.
- if (evenLeaseIndex(i)) {
- // The higher the index, the more expired the lease.
- reclaim(i, 10 + i);
- }
- }
- // Run leases reclamation routine on all leases. This should result
- // in removal of all leases with even indexes.
- ASSERT_NO_THROW(deleteExpiredReclaimedLeases(10));
- // Leases with odd indexes shouldn't be removed from the database.
- EXPECT_TRUE(testLeases(&leaseExists, &oddLeaseIndex));
- // Leases with even indexes should have been removed.
- EXPECT_TRUE(testLeases(&leaseDoesntExist, &evenLeaseIndex));
- }
- /// @brief Test that declined expired leases can be removed.
- ///
- /// This method allows controlling remove_leases parameter when calling
- /// @ref AllocEngine::reclaimExpiredLeases4 or
- /// @ref AllocEngine::reclaimExpiredLeases6. This should not matter, as
- /// the address affinity doesn't make sense for declined leases (they don't
- /// have any useful information in them anymore), so AllocEngine should
- /// remove them all the time.
- ///
- /// @param remove see description above
- void testReclaimDeclined(bool remove) {
- for (unsigned int i = 0; i < TEST_LEASES_NUM; ++i) {
- // Mark leases with even indexes as expired.
- if (evenLeaseIndex(i)) {
- // Mark lease as declined with 100 seconds of probation-period
- // (i.e. lease is supposed to be off limits for 100 seconds)
- decline(i, 100);
- // The higher the index, the more expired the lease.
- expire(i, 10 + i);
- }
- }
- // Run leases reclamation routine on all leases. This should result
- // in removing all leases with status = declined, i.e. all
- // even leases should be gone.
- ASSERT_NO_THROW(reclaimExpiredLeases(0, 0, remove));
- // Leases with even indexes should not exist in the DB
- EXPECT_TRUE(testLeases(&leaseDoesntExist, &evenLeaseIndex));
- }
- /// @brief Test that appropriate statistics are updated when
- /// declined expired leases are processed by AllocEngine.
- ///
- /// This method works for both v4 and v6. Just make sure the correct
- /// statistic name is passed. This is the name of the assigned addresses,
- /// that is expected to be decreased once the reclaimation procedure
- /// is complete.
- ///
- /// @param stat_name name of the statistic for assigned addresses statistic
- /// ("assgined-addresses" for both v4 and "assigned-nas" for v6)
- void testReclaimDeclinedStats(const std::string& stat_name) {
- // Leases by default all belong to subnet_id_ = 1. Let's count the
- // number of declined leases.
- int subnet1_cnt = 0;
- int subnet2_cnt = 0;
- // Let's move all leases to declined,expired state.
- for (unsigned int i = 0; i < TEST_LEASES_NUM; ++i) {
- // Move the lease to declined state
- decline(i, 100);
- // And expire it, so it will be reclaimed
- expire(i, 10 + 1);
- // Move every other lease to subnet-id = 2.
- if (evenLeaseIndex(i)) {
- subnet1_cnt++;
- } else {
- subnet2_cnt++;
- setSubnetId(i, 2);
- }
- }
- StatsMgr& stats_mgr = StatsMgr::instance();
- // Let's set the global statistic. Values are arbitrary and can
- // be used to easily detect whether a given stat was decreased or
- // increased. They are sufficiently high compared to number of leases
- // to avoid any chances of going into negative.
- stats_mgr.setValue("declined-addresses", static_cast<int64_t>(1000));
- // Let's set global the counter for reclaimed declined addresses.
- stats_mgr.setValue("reclaimed-declined-addresses",
- static_cast<int64_t>(2000));
- // And those subnet specific as well
- stats_mgr.setValue(stats_mgr.generateName("subnet", 1,
- stat_name), int64_t(1000));
- stats_mgr.setValue(stats_mgr.generateName("subnet", 2,
- stat_name), int64_t(2000));
- stats_mgr.setValue(stats_mgr.generateName("subnet", 1,
- "reclaimed-declined-addresses"), int64_t(10000));
- stats_mgr.setValue(stats_mgr.generateName("subnet", 2,
- "reclaimed-declined-addresses"), int64_t(20000));
- stats_mgr.setValue(stats_mgr.generateName("subnet", 1,
- "declined-addresses"), int64_t(100));
- stats_mgr.setValue(stats_mgr.generateName("subnet", 2,
- "declined-addresses"), int64_t(200));
- // Run leases reclamation routine on all leases. This should result
- // in removal of all leases with even indexes.
- ASSERT_NO_THROW(reclaimExpiredLeases(0, 0, true));
- // Declined-addresses should be decreased from its initial value (1000)
- // for both recovered addresses from subnet1 and subnet2.
- testStatistics("declined-addresses", 1000 - subnet1_cnt - subnet2_cnt);
- // The code should bump up global counter for reclaimed declined
- // addresses.
- testStatistics("reclaimed-declined-addresses", 2000 + subnet1_cnt + subnet2_cnt);
- // subnet[X].assigned-addresses should go down. Between the time
- // of DHCPDECLINE(v4)/DECLINE(v6) reception and declined expired lease
- // reclaimation, we count this address as assigned-addresses. We decrease
- // assigned-addresses(v4)/assgined-nas(v6) when we reclaim the lease,
- // not when the packet is received. For explanation, see Duplicate
- // Addresses (DHCPDECLINE support) (v4) or Duplicate Addresses (DECLINE
- // support) sections in the User's Guide or a comment in
- // Dhcpv4Srv::declineLease or Dhcpv6Srv::declineLease.
- testStatistics("subnet[1]." + stat_name, 1000 - subnet1_cnt);
- testStatistics("subnet[2]." + stat_name, 2000 - subnet2_cnt);
- testStatistics("subnet[1].declined-addresses", 100 - subnet1_cnt);
- testStatistics("subnet[2].declined-addresses", 200 - subnet2_cnt);
- // subnet[X].reclaimed-declined-addresses should go up in each subnet
- testStatistics("subnet[1].reclaimed-declined-addresses", 10000 + subnet1_cnt);
- testStatistics("subnet[2].reclaimed-declined-addresses", 20000 + subnet1_cnt);
- }
- /// @brief Collection of leases created at construction time.
- std::vector<LeasePtrType> leases_;
- /// @brief Allocation engine instance used for tests.
- AllocEnginePtr engine_;
- };
- /// @brief Specialization of the @c ExpirationAllocEngineTest class to test
- /// reclamation of the IPv6 leases.
- class ExpirationAllocEngine6Test : public ExpirationAllocEngineTest<Lease6Ptr> {
- public:
- /// @brief Class constructor.
- ///
- /// This constructor initializes @c TEST_LEASES_NUM leases and
- /// stores them in the lease manager.
- ExpirationAllocEngine6Test();
- /// @brief Virtual destructor.
- ///
- /// Clears up static fields that may be modified by hooks.
- virtual ~ExpirationAllocEngine6Test() {
- callout_lease_.reset();
- callout_name_ = string("");
- }
- /// @brief Creates collection of leases for a test.
- ///
- /// It is called internally at the construction time.
- void createLeases();
- /// @brief Updates lease in the lease database.
- ///
- /// @param lease_index Index of the lease.
- virtual void updateLease(const unsigned int lease_index) {
- LeaseMgrFactory::instance().updateLease6(leases_[lease_index]);
- }
- /// @brief Changes the owner of a lease.
- ///
- /// This method changes the owner of the lease by modifying the DUID.
- ///
- /// @param lease_index Lease index. Must be between 0 and
- /// @c TEST_LEASES_NUM.
- virtual void transferOwnership(const uint16_t lease_index);
- /// @brief Sets subnet id for a lease.
- ///
- /// It also updates statistics of assigned leases in the stats manager.
- ///
- /// @param lease_index Lease index.
- /// @param id New subnet id.
- virtual void setSubnetId(const uint16_t lease_index, const SubnetID& id);
- /// @brief Sets type of a lease.
- ///
- /// It also updates statistics of assigned leases in the stats manager.
- ///
- /// @param lease_index Lease index.
- /// @param lease_type Lease type.
- void setLeaseType(const uint16_t lease_index, const Lease6::Type& lease_type);
- /// @brief Retrieves lease from the database.
- ///
- /// @param lease_index Index of the lease.
- virtual Lease6Ptr getLease(const unsigned int lease_index) const {
- return (LeaseMgrFactory::instance().getLease6(leases_[lease_index]->type_,
- leases_[lease_index]->addr_));
- }
- /// @brief Wrapper method running lease reclamation routine.
- ///
- /// @param max_leases Maximum number of leases to be reclaimed.
- /// @param timeout Maximum amount of time that the reclaimation routine
- /// may be processing expired leases, expressed in seconds.
- /// @param remove_lease A boolean value indicating if the lease should
- /// be removed when it is reclaimed (if true) or it should be left in the
- /// database in the "expired-reclaimed" state (if false).
- virtual void reclaimExpiredLeases(const size_t max_leases,
- const uint16_t timeout,
- const bool remove_lease) {
- engine_->reclaimExpiredLeases6(max_leases, timeout, remove_lease);
- }
- /// @brief Wrapper method for removing expired-reclaimed leases.
- ///
- /// @param secs The minimum amount of time, expressed in seconds,
- /// for the lease to be left in the "expired-reclaimed" state
- /// before it can be removed.
- virtual void deleteExpiredReclaimedLeases(const uint32_t secs) {
- engine_->deleteExpiredReclaimedLeases6(secs);
- }
- /// @brief Test that statistics is updated when leases are reclaimed.
- void testReclaimExpiredLeasesStats();
- /// @brief Test that expired leases are reclaimed before they are allocated.
- ///
- /// @param msg_type DHCPv6 message type.
- /// @param use_reclaimed Boolean parameter indicating if the leases
- /// stored in the lease database should be marked as 'expired-reclaimed'
- /// or 'expired'. This allows to test whether the allocation engine can
- /// determine that the lease has been reclaimed already and not reclaim
- /// it the second time.
- void testReclaimReusedLeases(const uint16_t msg_type, const bool use_reclaimed);
- /// @brief Callout for lease6_recover
- ///
- /// This callout stores passed parameter into static fields.
- ///
- /// @param callout_handle will be provided by hooks framework
- /// @return always 0
- static int lease6RecoverCallout(CalloutHandle& callout_handle) {
- callout_name_ = "lease6_recover";
- callout_handle.getArgument("lease6", callout_lease_);
- return (0);
- }
- /// @brief Callout for lease6_recover that sets status to SKIP
- ///
- /// This callout stores passed parameter into static fields.
- ///
- /// @param callout_handle will be provided by hooks framework
- /// @return always 0
- static int lease6RecoverSkipCallout(CalloutHandle& callout_handle) {
- // Set the next step status to SKIP
- callout_handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
- return (lease6RecoverCallout(callout_handle));
- }
- /// @brief Test install a hook callout, recovers declined leases
- ///
- /// This test: declines, then expires half of the leases, then
- /// installs a callout on lease6_recover hook, then reclaims
- /// expired leases and checks that:
- /// - the callout was indeed called
- /// - the parameter (lease6) was indeed passed as expected
- /// - checks that the leases are removed (skip=false) or
- /// - checks that the leases are still there (skip=true)
- /// @param skip should the callout set the next step status to skip?
- void
- testReclaimDeclinedHook(bool skip);
- /// The following parameters will be written by a callout
- static std::string callout_name_; ///< Stores callout name
- static Lease6Ptr callout_lease_; ///< Stores callout parameter
- };
- std::string ExpirationAllocEngine6Test::callout_name_;
- Lease6Ptr ExpirationAllocEngine6Test::callout_lease_;
- ExpirationAllocEngine6Test::ExpirationAllocEngine6Test()
- : ExpirationAllocEngineTest<Lease6Ptr>("type=memfile universe=6 persist=false") {
- createLeases();
- callout_argument_name = "lease6";
- // Let's clear any garbage previous test may have left in static fields.
- callout_name_ = string("");
- callout_lease_.reset();
- }
- void
- ExpirationAllocEngine6Test::createLeases() {
- // Create TEST_LEASES_NUM leases.
- for (uint16_t i = 0; i < TEST_LEASES_NUM; ++i) {
- // DUID
- std::ostringstream duid_s;
- duid_s << "01020304050607" << std::setw(4) << std::setfill('0') << i;
- DuidPtr duid(new DUID(DUID::fromText(duid_s.str()).getDuid()));
- // Address.
- std::ostringstream address_s;
- address_s << "2001:db8:1::" << std::setw(4) << std::setfill('0') << i;
- IOAddress address(address_s.str());
- // Create lease.
- Lease6Ptr lease(new Lease6(Lease::TYPE_NA, address, duid, 1, 50, 60, 10,
- 20, SubnetID(1), true, true,
- generateHostnameForLeaseIndex(i)));
- leases_.push_back(lease);
- // Copy the lease before adding it to the lease manager. We want to
- // make sure that modifications to the leases held in the leases_
- // container doesn't affect the leases in the lease manager.
- LeaseMgrFactory::instance().addLease(Lease6Ptr(new Lease6(*lease)));
- // Note in the statistics that this lease has been added.
- StatsMgr& stats_mgr = StatsMgr::instance();
- std::string stat_name =
- lease->type_ == Lease::TYPE_NA ? "assigned-nas" : "assigned-pds";
- stats_mgr.addValue(stats_mgr.generateName("subnet", lease->subnet_id_, stat_name),
- int64_t(1));
- }
- }
- void
- ExpirationAllocEngine6Test::transferOwnership(const uint16_t lease_index) {
- ASSERT_GT(leases_.size(), lease_index);
- std::vector<uint8_t> bytes = leases_[lease_index]->duid_->getDuid();
- if (bytes.size() > 1) {
- if (++bytes[0] == 0) {
- ++bytes[1];
- }
- }
- leases_[lease_index]->duid_.reset(new DUID(bytes));
- }
- void
- ExpirationAllocEngine6Test::setSubnetId(const uint16_t lease_index, const SubnetID& id) {
- ASSERT_GT(leases_.size(), lease_index);
- if (leases_[lease_index]->subnet_id_ != id) {
- StatsMgr& stats_mgr = StatsMgr::instance();
- std::string stats_name = (leases_[lease_index]->type_ == Lease::TYPE_NA ?
- "assigned-nas" : "assigned-pds");
- stats_mgr.addValue(stats_mgr.generateName("subnet", id, stats_name),
- int64_t(1));
- stats_mgr.addValue(stats_mgr.generateName("subnet",
- leases_[lease_index]->subnet_id_,
- stats_name),
- int64_t(-1));
- leases_[lease_index]->subnet_id_ = id;
- ASSERT_NO_THROW(updateLease(lease_index));
- }
- }
- void
- ExpirationAllocEngine6Test::setLeaseType(const uint16_t lease_index,
- const Lease6::Type& lease_type) {
- ASSERT_GT(leases_.size(), lease_index);
- if (leases_[lease_index]->type_ != lease_type) {
- StatsMgr& stats_mgr = StatsMgr::instance();
- std::string stats_name = (lease_type == Lease::TYPE_NA ?
- "assigned-nas" : "assigned-pds");
- stats_mgr.addValue(stats_mgr.generateName("subnet",
- leases_[lease_index]->subnet_id_,
- stats_name),
- int64_t(1));
- stats_name = (leases_[lease_index]->type_ == Lease::TYPE_NA ?
- "assigned-nas" : "assigned-pds");
- stats_mgr.addValue(stats_mgr.generateName("subnet",
- leases_[lease_index]->subnet_id_,
- stats_name),
- int64_t(-1));
- leases_[lease_index]->type_ = lease_type;
- ASSERT_NO_THROW(updateLease(lease_index));
- }
- }
- void
- ExpirationAllocEngine6Test::testReclaimExpiredLeasesStats() {
- // This test requires that the number of leases is an even number.
- BOOST_STATIC_ASSERT(TEST_LEASES_NUM % 2 == 0);
- for (unsigned int i = 0; i < TEST_LEASES_NUM; ++i) {
- // Mark all leaes as expired. The higher the index the less
- // expired the lease.
- expire(i, 1000 - i);
- // Modify subnet ids and lease types for some leases.
- if (evenLeaseIndex(i)) {
- setSubnetId(i, SubnetID(2));
- setLeaseType(i, Lease::TYPE_PD);
- }
- }
- // Leases will be reclaimed in groups of 8.
- const size_t reclamation_group_size = 8;
- for (unsigned int i = reclamation_group_size; i < TEST_LEASES_NUM;
- i += reclamation_group_size) {
- // Reclaim 8 most expired leases out of TEST_LEASES_NUM.
- ASSERT_NO_THROW(reclaimExpiredLeases(reclamation_group_size,
- 0, false));
- // Number of reclaimed leases should increase as we loop.
- EXPECT_TRUE(testStatistics("reclaimed-leases", i));
- // Make sure that the number of reclaimed leases is also distributed
- // across two subnets.
- EXPECT_TRUE(testStatistics("subnet[1].reclaimed-leases", i / 2));
- EXPECT_TRUE(testStatistics("subnet[2].reclaimed-leases", i / 2));
- // Number of assigned leases should decrease as we reclaim them.
- EXPECT_TRUE(testStatistics("subnet[1].assigned-nas",
- (TEST_LEASES_NUM - i) / 2));
- EXPECT_TRUE(testStatistics("subnet[2].assigned-pds",
- (TEST_LEASES_NUM - i) / 2));
- }
- }
- void
- ExpirationAllocEngine6Test::testReclaimReusedLeases(const uint16_t msg_type,
- const bool use_reclaimed) {
- BOOST_STATIC_ASSERT(TEST_LEASES_NUM < 1000);
- for (unsigned int i = 0; i < TEST_LEASES_NUM; ++i) {
- // Depending on the parameter, mark leases 'expired-reclaimed' or
- // simply 'expired'.
- if (use_reclaimed) {
- reclaim(i, 1000 - i);
- } else {
- // Mark all leases as expired.
- expire(i, 1000 - i);
- }
- // For the Renew case, we don't change the ownership of leases. We
- // will let the lease owners renew them. For other cases, we modify
- // the DUIDs to simulate reuse of expired leases.
- if (msg_type != DHCPV6_RENEW) {
- transferOwnership(i);
- }
- }
- // Create subnet and the pool. This is required by the allocation process.
- Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 64, 10, 20, 50, 60,
- SubnetID(1)));
- ASSERT_NO_THROW(subnet->addPool(Pool6Ptr(new Pool6(Lease::TYPE_NA,
- IOAddress("2001:db8:1::"),
- IOAddress("2001:db8:1::FFFF")))));
- for (unsigned int i = 0; i < TEST_LEASES_NUM; ++i) {
- // Build the context.
- AllocEngine::ClientContext6 ctx(subnet, leases_[i]->duid_,
- false, false,
- leases_[i]->hostname_,
- msg_type == DHCPV6_SOLICIT,
- Pkt6Ptr(new Pkt6(msg_type, 0x1234)));
- ctx.currentIA().iaid_ = 1;
- ctx.currentIA().hints_.push_back(std::make_pair(leases_[i]->addr_, 128));
- // Depending on the message type, we will call a different function.
- if (msg_type == DHCPV6_RENEW) {
- ASSERT_NO_THROW(engine_->renewLeases6(ctx));
- } else {
- ASSERT_NO_THROW(engine_->allocateLeases6(ctx));
- }
- }
- // The Solicit should not trigger leases reclamation. The Renew and
- // Request must trigger leases reclamation unless the lease is
- // initially reclaimed.
- if (use_reclaimed || (msg_type == DHCPV6_SOLICIT)) {
- EXPECT_TRUE(testStatistics("reclaimed-leases", 0));
- } else {
- EXPECT_TRUE(testStatistics("reclaimed-leases", TEST_LEASES_NUM));
- // Leases should have been updated in the lease database and their
- // state should not be 'expired-reclaimed' anymore.
- EXPECT_TRUE(testLeases(&leaseNotReclaimed, &allLeaseIndexes));
- }
- }
- void
- ExpirationAllocEngine6Test::testReclaimDeclinedHook(bool skip) {
- for (unsigned int i = 0; i < TEST_LEASES_NUM; ++i) {
- // Mark leases with even indexes as expired.
- if (evenLeaseIndex(i)) {
- // Mark lease as declined with 100 seconds of probation-period
- // (i.e. lease is supposed to be off limits for 100 seconds)
- decline(i, 100);
- // The higher the index, the more expired the lease.
- expire(i, 10 + i);
- }
- }
- EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
- "lease6_recover",
- skip ? lease6RecoverSkipCallout : lease6RecoverCallout));
- // Run leases reclamation routine on all leases.
- ASSERT_NO_THROW(reclaimExpiredLeases(0, 0, true));
- // Make sure that the callout really was called. It was supposed to modify
- // the callout_name_ and store the lease in callout_lease_
- EXPECT_EQ("lease6_recover", callout_name_);
- EXPECT_TRUE(callout_lease_);
- // Leases with even indexes should not exist in the DB
- if (skip) {
- // Skip status should have prevented removing the lease.
- EXPECT_TRUE(testLeases(&leaseExists, &evenLeaseIndex));
- } else {
- // The hook hasn't modified next step status. The lease should be gone.
- EXPECT_TRUE(testLeases(&leaseDoesntExist, &evenLeaseIndex));
- }
- };
- // This test verifies that the leases can be reclaimed without being removed
- // from the database. In such case, the leases' state is set to
- // "expired-reclaimed".
- TEST_F(ExpirationAllocEngine6Test, reclaimExpiredLeases6UpdateState) {
- testReclaimExpiredLeasesUpdateState();
- }
- // This test verifies that the reclaimed leases are deleted when requested.
- TEST_F(ExpirationAllocEngine6Test, reclaimExpiredLeasesDelete) {
- testReclaimExpiredLeasesDelete();
- }
- // This test verifies that it is possible to specify the limit for the
- // number of reclaimed leases.
- TEST_F(ExpirationAllocEngine6Test, reclaimExpiredLeasesLimit) {
- testReclaimExpiredLeasesLimit();
- }
- // This test verifies that DNS updates are generated for the leases
- // for which the DNS records exist.
- TEST_F(ExpirationAllocEngine6Test, reclaimExpiredLeasesWithDDNS) {
- testReclaimExpiredLeasesWithDDNS();
- }
- // This test verifies that it is DNS updates are generated only for the
- // reclaimed expired leases. In this case we limit the number of leases
- // reclaimed during a single call to reclamation routine.
- TEST_F(ExpirationAllocEngine6Test, reclaimExpiredLeasesWithDDNSAndLimit) {
- testReclaimExpiredLeasesWithDDNSAndLimit();
- }
- // This test verifies that if some leases have invalid hostnames, the
- // lease reclamation routine continues with reclamation of leases anyway.
- TEST_F(ExpirationAllocEngine6Test, reclaimExpiredLeasesInvalidHostname) {
- testReclaimExpiredLeasesInvalidHostname();
- }
- // This test verifies that statistics is correctly updated when the leases
- // are reclaimed.
- TEST_F(ExpirationAllocEngine6Test, reclaimExpiredLeasesStats) {
- testReclaimExpiredLeasesStats();
- }
- // This test verifies that callouts are executed for each expired lease.
- TEST_F(ExpirationAllocEngine6Test, reclaimExpiredLeasesHooks) {
- testReclaimExpiredLeasesHooks();
- }
- // This test verifies that callouts are executed for each expired lease
- // and that the lease is not reclaimed when the skip flag is set.
- TEST_F(ExpirationAllocEngine6Test, reclaimExpiredLeasesHooksWithSkip) {
- testReclaimExpiredLeasesHooksWithSkip();
- }
- // This test verifies that it is possible to set the timeout for the
- // execution of the lease reclamation routine.
- TEST_F(ExpirationAllocEngine6Test, reclaimExpiredLeasesTimeout) {
- // This test needs at least 40 leases to make sense.
- BOOST_STATIC_ASSERT(TEST_LEASES_NUM >= 40);
- // Run with timeout of 1.2s.
- testReclaimExpiredLeasesTimeout(1200);
- }
- // This test verifies that at least one lease is reclaimed if the timeout
- // for the lease reclamation routine is shorter than the time needed for
- // the reclamation of a single lease. This prevents the situation when
- // very short timeout (perhaps misconfigured) effectively precludes leases
- // reclamation.
- TEST_F(ExpirationAllocEngine6Test, reclaimExpiredLeasesShortTimeout) {
- // We will most likely reclaim just one lease, so 5 is more than enough.
- BOOST_STATIC_ASSERT(TEST_LEASES_NUM >= 5);
- // Reclaim leases with the 1ms timeout.
- testReclaimExpiredLeasesTimeout(1);
- }
- // This test verifies that expired-reclaimed leases are removed from the
- // lease database.
- TEST_F(ExpirationAllocEngine6Test, deleteExpiredReclaimedLeases) {
- BOOST_STATIC_ASSERT(TEST_LEASES_NUM >= 10);
- testDeleteExpiredReclaimedLeases();
- }
- /// This test verifies that @ref AllocEngine::reclaimExpiredLeases6 properly
- /// handles declined leases that have expired in case when it is told to
- /// remove leases.}
- TEST_F(ExpirationAllocEngine6Test, reclaimDeclined1) {
- testReclaimDeclined(true);
- }
- /// This test verifies that @ref AllocEngine::reclaimExpiredLeases6 properly
- /// handles declined leases that have expired in case when it is told to
- /// not remove leases. This flag should not matter and declined expired
- /// leases should always be removed.
- TEST_F(ExpirationAllocEngine6Test, reclaimDeclined2) {
- testReclaimDeclined(false);
- }
- /// This test verifies that statistics are modified correctly after
- /// reclaim expired leases is called.
- TEST_F(ExpirationAllocEngine6Test, reclaimDeclinedStats) {
- testReclaimDeclinedStats("assigned-nas");
- }
- // This test verifies that expired leases are reclaimed before they are
- // allocated to another client sending a Request message.
- TEST_F(ExpirationAllocEngine6Test, reclaimReusedLeases) {
- testReclaimReusedLeases(DHCPV6_REQUEST, false);
- }
- // This test verifies that allocation engine detects that the expired
- // lease has been reclaimed already when it reuses this lease.
- TEST_F(ExpirationAllocEngine6Test, reclaimReusedLeasesAlreadyReclaimed) {
- testReclaimReusedLeases(DHCPV6_REQUEST, true);
- }
- // This test verifies that expired leases are reclaimed before they
- // are renewed.
- TEST_F(ExpirationAllocEngine6Test, reclaimRenewedLeases) {
- testReclaimReusedLeases(DHCPV6_RENEW, false);
- }
- // This test verifies that allocation engine detects that the expired
- // lease has been reclaimed already when it renews the lease.
- TEST_F(ExpirationAllocEngine6Test, reclaimRenewedLeasesAlreadyReclaimed) {
- testReclaimReusedLeases(DHCPV6_RENEW, true);
- }
- // This test verifies that the expired leases are not reclaimed when the
- // Solicit message is being processed.
- TEST_F(ExpirationAllocEngine6Test, reclaimReusedLeasesSolicit) {
- testReclaimReusedLeases(DHCPV6_SOLICIT, false);
- }
- // This test verifies that the 'expired-reclaimed' leases are not reclaimed
- // again when the Solicit message is being processed.
- TEST_F(ExpirationAllocEngine6Test, reclaimReusedLeasesSolicitAlreadyReclaimed) {
- testReclaimReusedLeases(DHCPV6_SOLICIT, true);
- }
- // This test verifies if the hooks installed on lease6_recover are called
- // when the lease expires.
- TEST_F(ExpirationAllocEngine6Test, reclaimDeclinedHook1) {
- testReclaimDeclinedHook(false); // false = don't use skip callout
- }
- // This test verifies if the hooks installed on lease6_recover are called
- // when the lease expires and that the next step status set to SKIP
- // causes the recovery to not be conducted.
- TEST_F(ExpirationAllocEngine6Test, reclaimDeclinedHook2) {
- testReclaimDeclinedHook(true); // true = use skip callout
- }
- // *******************************************************
- //
- // DHCPv4 lease reclamation routine tests start here!
- //
- // *******************************************************
- /// @brief Specialization of the @c ExpirationAllocEngineTest class to test
- /// reclamation of the IPv4 leases.
- class ExpirationAllocEngine4Test : public ExpirationAllocEngineTest<Lease4Ptr> {
- public:
- /// @brief Class constructor.
- ///
- /// This constructor initializes @c TEST_LEASES_NUM leases and
- /// stores them in the lease manager.
- ExpirationAllocEngine4Test();
- /// @brief Virtual destructor.
- ///
- /// Clears up static fields that may be modified by hooks.
- virtual ~ExpirationAllocEngine4Test() {
- callout_lease_.reset();
- callout_name_ = string("");
- }
- /// @brief Creates collection of leases for a test.
- ///
- /// It is called internally at the construction time.
- void createLeases();
- /// @brief Generates unique client identifier from lease index.
- ///
- /// @param index lease index.
- void setUniqueClientId(const uint16_t index);
- /// @brief Updates lease in the lease database.
- ///
- /// @param lease_index Index of the lease.
- virtual void updateLease(const unsigned int lease_index) {
- LeaseMgrFactory::instance().updateLease4(leases_[lease_index]);
- }
- /// @brief Changes the owner of a lease.
- ///
- /// This method changes the owner of the lease by updating the client
- /// identifier (if present) or HW address.
- ///
- /// @param lease_index Lease index. Must be between 0 and
- /// @c TEST_LEASES_NUM.
- virtual void transferOwnership(const uint16_t lease_index);
- /// @brief Retrieves lease from the database.
- ///
- /// @param lease_index Index of the lease.
- virtual Lease4Ptr getLease(const unsigned int lease_index) const {
- return (LeaseMgrFactory::instance().getLease4(leases_[lease_index]->addr_));
- }
- /// @brief Sets subnet id for a lease.
- ///
- /// It also updates statistics of assigned leases in the stats manager.
- ///
- /// @param lease_index Lease index.
- /// @param id New subnet id.
- virtual void setSubnetId(const uint16_t lease_index, const SubnetID& id);
- /// @brief Wrapper method running lease reclamation routine.
- ///
- /// @param max_leases Maximum number of leases to be reclaimed.
- /// @param timeout Maximum amount of time that the reclaimation routine
- /// may be processing expired leases, expressed in seconds.
- /// @param remove_lease A boolean value indicating if the lease should
- /// be removed when it is reclaimed (if true) or it should be left in the
- /// database in the "expired-reclaimed" state (if false).
- virtual void reclaimExpiredLeases(const size_t max_leases,
- const uint16_t timeout,
- const bool remove_lease) {
- engine_->reclaimExpiredLeases4(max_leases, timeout, remove_lease);
- }
- /// @brief Wrapper method for removing expired-reclaimed leases.
- ///
- /// @param secs The minimum amount of time, expressed in seconds,
- /// for the lease to be left in the "expired-reclaimed" state
- /// before it can be removed.
- virtual void deleteExpiredReclaimedLeases(const uint32_t secs) {
- engine_->deleteExpiredReclaimedLeases4(secs);
- }
- /// @brief Lease algorithm checking if NCR has been generated from client
- /// identifier.
- ///
- /// @param lease Pointer to the lease for which the NCR needs to be checked.
- static bool dnsUpdateGeneratedFromClientId(const Lease4Ptr& lease);
- /// @brief Lease algorithm checking if NCR has been generated from
- /// HW address.
- static bool dnsUpdateGeneratedFromHWAddress(const Lease4Ptr& lease);
- /// @brief Test that DNS updates are properly generated when the
- /// reclaimed leases contain client identifier.
- void testReclaimExpiredLeasesWithDDNSAndClientId();
- /// @brief Test that statistics is updated when leases are reclaimed..
- void testReclaimExpiredLeasesStats();
- /// @brief Test that the lease is reclaimed before it is renewed or
- /// reused.
- ///
- /// @param msg_type DHCPv4 message type, i.e. DHCPDISCOVER or DHCPREQUEST.
- /// @param client_renews A boolean value which indicates if the test should
- /// simulate renewals of leases (if true) or reusing expired leases which
- /// belong to different clients (if false).
- /// @param use_reclaimed Boolean parameter indicating if the leases being
- /// reused should initially be reclaimed.
- void testReclaimReusedLeases(const uint8_t msg_type, const bool client_renews,
- const bool use_reclaimed);
- /// @brief Callout for lease4_recover
- ///
- /// This callout stores passed parameter into static fields.
- ///
- /// @param callout_handle will be provided by hooks framework
- /// @return always 0
- static int lease4RecoverCallout(CalloutHandle& callout_handle) {
- callout_name_ = "lease4_recover";
- callout_handle.getArgument("lease4", callout_lease_);
- return (0);
- }
- /// @brief Callout for lease4_recover that sets status to SKIP
- ///
- /// This callout stores passed parameter into static fields.
- ///
- /// @param callout_handle will be provided by hooks framework
- /// @return always 0
- static int lease4RecoverSkipCallout(CalloutHandle& callout_handle) {
- // Set the next step status to SKIP
- callout_handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
- return (lease4RecoverCallout(callout_handle));
- }
- /// @brief Test install a hook callout, recovers declined leases
- ///
- /// This test: declines, then expires half of the leases, then
- /// installs a callout on lease4_recover hook, then reclaims
- /// expired leases and checks that:
- /// - the callout was indeed called
- /// - the parameter (lease4) was indeed passed as expected
- /// - checks that the leases are removed (skip=false) or
- /// - checks that the leases are still there (skip=true)
- /// @param skip should the callout set the next step status to skip?
- void
- testReclaimDeclinedHook(bool skip);
- /// The following parameters will be written by a callout
- static std::string callout_name_; ///< Stores callout name
- static Lease4Ptr callout_lease_; ///< Stores callout parameter
- };
- std::string ExpirationAllocEngine4Test::callout_name_;
- Lease4Ptr ExpirationAllocEngine4Test::callout_lease_;
- ExpirationAllocEngine4Test::ExpirationAllocEngine4Test()
- : ExpirationAllocEngineTest<Lease4Ptr>("type=memfile universe=4 persist=false") {
- createLeases();
- callout_argument_name = "lease4";
- // Let's clear any garbage previous test may have left in static fields.
- callout_name_ = string("");
- callout_lease_.reset();
- }
- void
- ExpirationAllocEngine4Test::createLeases() {
- // Create TEST_LEASES_NUM leases.
- for (uint16_t i = 0; i < TEST_LEASES_NUM; ++i) {
- // HW address
- std::ostringstream hwaddr_s;
- hwaddr_s << "01:02:03:04:" << std::setw(2) << std::setfill('0')
- << (i >> 8) << ":" << std::setw(2) << std::setfill('0')
- << (i & 0x00FF);
- HWAddrPtr hwaddr(new HWAddr(HWAddr::fromText(hwaddr_s.str(),
- HTYPE_ETHER)));
- // Address.
- std::ostringstream address_s;
- address_s << "10.0." << (i >> 8) << "." << (i & 0x00FF);
- IOAddress address(address_s.str());
- // Create lease.
- Lease4Ptr lease(new Lease4(address, hwaddr, ClientIdPtr(), 60, 10, 20,
- time(NULL), SubnetID(1), true, true,
- generateHostnameForLeaseIndex(i)));
- leases_.push_back(lease);
- // Copy the lease before adding it to the lease manager. We want to
- // make sure that modifications to the leases held in the leases_
- // container doesn't affect the leases in the lease manager.
- LeaseMgrFactory::instance().addLease(Lease4Ptr(new Lease4(*lease)));
- // Note in the statistics that this lease has been added.
- StatsMgr& stats_mgr = StatsMgr::instance();
- std::string stat_name = "assigned-addresses";
- stats_mgr.addValue(stats_mgr.generateName("subnet", lease->subnet_id_, stat_name),
- int64_t(1));
- }
- }
- void
- ExpirationAllocEngine4Test::setUniqueClientId(const uint16_t index) {
- std::ostringstream clientid_s;
- clientid_s << "AA:BB:" << std::setw(2) << std::setfill('0')
- << (index >> 8) << ":" << std::setw(2) << std::setfill('0')
- << (index & 0x00FF);
- ClientIdPtr client_id(ClientId::fromText(clientid_s.str()));
- leases_[index]->client_id_ = client_id;
- LeaseMgrFactory::instance().updateLease4(leases_[index]);
- }
- void
- ExpirationAllocEngine4Test::setSubnetId(const uint16_t lease_index, const SubnetID& id) {
- ASSERT_GT(leases_.size(), lease_index);
- if (leases_[lease_index]->subnet_id_ != id) {
- StatsMgr& stats_mgr = StatsMgr::instance();
- stats_mgr.addValue(stats_mgr.generateName("subnet", id, "assigned-addresses"),
- int64_t(1));
- stats_mgr.addValue(stats_mgr.generateName("subnet",
- leases_[lease_index]->subnet_id_,
- "assigned-addresses"),
- int64_t(-1));
- leases_[lease_index]->subnet_id_ = id;
- ASSERT_NO_THROW(updateLease(lease_index));
- }
- }
- void
- ExpirationAllocEngine4Test::transferOwnership(const uint16_t lease_index) {
- ASSERT_GT(leases_.size(), lease_index);
- std::vector<uint8_t> bytes;
- if (leases_[lease_index]->client_id_) {
- bytes = leases_[lease_index]->client_id_->getClientId();
- } else {
- bytes = leases_[lease_index]->hwaddr_->hwaddr_;
- }
- if (!bytes.empty()) {
- if (++bytes[0] == 0) {
- ++bytes[1];
- }
- }
- if (leases_[lease_index]->client_id_) {
- leases_[lease_index]->client_id_.reset(new ClientId(bytes));
- } else {
- leases_[lease_index]->hwaddr_.reset(new HWAddr(bytes, HTYPE_ETHER));
- }
- }
- bool
- ExpirationAllocEngine4Test::dnsUpdateGeneratedFromClientId(const Lease4Ptr& lease) {
- try {
- NameChangeRequestPtr ncr = getNCRForLease(lease);
- if (ncr) {
- if (lease->client_id_) {
- // Generate hostname for this lease. Note that the lease
- // in the database doesn't have the hostname because it
- // has been removed by the lease reclamation routine.
- std::string hostname = generateHostnameForLeaseIndex(
- getLeaseIndexFromAddress(lease->addr_));
- // Get DHCID from NCR.
- const D2Dhcid& dhcid = ncr->getDhcid();
- // Generate reference DHCID to compare with the one from
- // the NCR.
- std::vector<uint8_t> fqdn_wire;
- OptionDataTypeUtil::writeFqdn(hostname, fqdn_wire, true);
- D2Dhcid clientid_dhcid(lease->client_id_->getClientId(),
- fqdn_wire);
- // Return true if they match.
- return (dhcid == clientid_dhcid);
- }
- }
- } catch (...) {
- // If error occurred, treat it as no match.
- return (false);
- }
- // All leases checked - no match.
- return (false);
- }
- bool
- ExpirationAllocEngine4Test::dnsUpdateGeneratedFromHWAddress(const Lease4Ptr& lease) {
- try {
- NameChangeRequestPtr ncr = getNCRForLease(lease);
- if (ncr) {
- if (lease->hwaddr_) {
- // Generate hostname for this lease. Note that the lease
- // in the database doesn't have the hostname because it
- // has been removed by the lease reclamation routine.
- std::string hostname = generateHostnameForLeaseIndex(
- getLeaseIndexFromAddress(lease->addr_));
- // Get DHCID from NCR.
- const D2Dhcid& dhcid = ncr->getDhcid();
- // Generate reference DHCID to compare with the one from
- // the NCR.
- std::vector<uint8_t> fqdn_wire;
- OptionDataTypeUtil::writeFqdn(hostname, fqdn_wire, true);
- D2Dhcid hwaddr_dhcid(lease->hwaddr_, fqdn_wire);
- // Return true if they match.
- return (dhcid == hwaddr_dhcid);
- }
- }
- } catch (...) {
- // If error occurred, treat it as no match.
- return (false);
- }
- // All leases checked - no match.
- return (false);
- }
- void
- ExpirationAllocEngine4Test::testReclaimExpiredLeasesWithDDNSAndClientId() {
- // DNS must be started for the D2 client to accept NCRs.
- ASSERT_NO_THROW(enableDDNS());
- for (unsigned int i = 0; i < TEST_LEASES_NUM; ++i) {
- // Set client identifiers for leases with even indexes only.
- if (evenLeaseIndex(i)) {
- setUniqueClientId(i);
- }
- // Expire all leases. The higher the index, the more expired the lease.
- expire(i, 10 + i);
- }
- // Reclaim all expired leases.
- ASSERT_NO_THROW(reclaimExpiredLeases(0, 0, false));
- // Leases with even indexes should be reclaimed.
- EXPECT_TRUE(testLeases(&leaseReclaimed, &evenLeaseIndex));
- // DNS updates (removal NCRs) should be generated for all leases.
- EXPECT_TRUE(testLeases(&dnsUpdateGeneratedForLease, &allLeaseIndexes));
- // Leases with even indexes include client identifiers so the DHCID should
- // be generated from the client identifiers.
- EXPECT_TRUE(testLeases(&dnsUpdateGeneratedFromClientId, &evenLeaseIndex));
- // Leases with odd indexes do not include client identifiers so their
- // DHCID should be generated from the HW address.
- EXPECT_TRUE(testLeases(&dnsUpdateGeneratedFromHWAddress, &oddLeaseIndex));
- }
- void
- ExpirationAllocEngine4Test::testReclaimExpiredLeasesStats() {
- // This test requires that the number of leases is an even number.
- BOOST_STATIC_ASSERT(TEST_LEASES_NUM % 2 == 0);
- for (unsigned int i = 0; i < TEST_LEASES_NUM; ++i) {
- // Mark all leaes as expired. The higher the index the less
- // expired the lease.
- expire(i, 1000 - i);
- // Modify subnet ids of some leases.
- if (evenLeaseIndex(i)) {
- setSubnetId(i, 2);
- }
- }
- // Leases will be reclaimed in groups of 8.
- const size_t reclamation_group_size = 8;
- for (unsigned int i = reclamation_group_size; i < TEST_LEASES_NUM;
- i += reclamation_group_size) {
- // Reclaim 8 most expired leases out of TEST_LEASES_NUM.
- ASSERT_NO_THROW(reclaimExpiredLeases(reclamation_group_size,
- 0, false));
- // Number of reclaimed leases should increase as we loop.
- EXPECT_TRUE(testStatistics("reclaimed-leases", i));
- // Make sure that the number of reclaimed leases is also distributed
- // across two subnets.
- EXPECT_TRUE(testStatistics("subnet[1].reclaimed-leases", i / 2));
- EXPECT_TRUE(testStatistics("subnet[2].reclaimed-leases", i / 2));
- // Number of assigned leases should decrease as we reclaim them.
- EXPECT_TRUE(testStatistics("subnet[1].assigned-addresses",
- (TEST_LEASES_NUM - i) / 2));
- EXPECT_TRUE(testStatistics("subnet[2].assigned-addresses",
- (TEST_LEASES_NUM - i) / 2));
- }
- }
- void
- ExpirationAllocEngine4Test::testReclaimReusedLeases(const uint8_t msg_type,
- const bool client_renews,
- const bool use_reclaimed) {
- // Let's restrict the number of leases.
- BOOST_STATIC_ASSERT(TEST_LEASES_NUM < 1000);
- for (unsigned int i = 0; i < TEST_LEASES_NUM; ++i) {
- // Depending on the parameter, mark leases 'expired-reclaimed' or
- // simply 'expired'.
- if (use_reclaimed) {
- reclaim(i, 1000 - i);
- } else {
- // Mark all leases as expired.
- expire(i, 1000 - i);
- }
- // Check if we're simulating renewals or reusing leases. If this is
- // about reusing leases, we should be using different MAC addresses
- // or client identifiers for the leases than those stored presently
- // in the database.
- if (!client_renews) {
- // This function modifies the MAC address or the client identifier
- // of the test lease to make sure it doesn't match the one we
- // have in the database.
- transferOwnership(i);
- }
- }
- // The call to AllocEngine::allocateLease4 requires the subnet selection.
- // The pool must be present within a subnet for the allocation engine to
- // hand out address from.
- Subnet4Ptr subnet(new Subnet4(IOAddress("10.0.0.0"), 16, 10, 20, 60, SubnetID(1)));
- ASSERT_NO_THROW(subnet->addPool(Pool4Ptr(new Pool4(IOAddress("10.0.0.0"),
- IOAddress("10.0.255.255")))));
- // Re-allocate leases (reuse or renew).
- for (unsigned int i = 0; i < TEST_LEASES_NUM; ++i) {
- // Build the context.
- AllocEngine::ClientContext4 ctx(subnet, leases_[i]->client_id_,
- leases_[i]->hwaddr_,
- leases_[i]->addr_, false, false,
- leases_[i]->hostname_,
- msg_type == DHCPDISCOVER);
- // Query is needed for logging purposes.
- ctx.query_.reset(new Pkt4(msg_type, 0x1234));
- // Re-allocate a lease. Note that the iterative will pick addresses
- // starting from the beginning of the pool. This matches exactly
- // the set of addresses we have allocated and stored in the database.
- // Since all leases are marked expired the allocation engine will
- // reuse them or renew them as appropriate.
- ASSERT_NO_THROW(engine_->allocateLease4(ctx));
- }
- // If DHCPDISCOVER is being processed, the leases should not be reclaimed.
- // Also, the leases should not be reclaimed if they are already in the
- // 'expired-reclaimed' state.
- if (use_reclaimed || (msg_type == DHCPDISCOVER)) {
- EXPECT_TRUE(testStatistics("reclaimed-leases", 0));
- } else if (msg_type == DHCPREQUEST) {
- // Re-allocation of expired leases should result in reclamations.
- EXPECT_TRUE(testStatistics("reclaimed-leases", TEST_LEASES_NUM));
- // Leases should have been updated in the lease database and their
- // state should not be 'expired-reclaimed' anymore.
- EXPECT_TRUE(testLeases(&leaseNotReclaimed, &allLeaseIndexes));
- }
- }
- void
- ExpirationAllocEngine4Test::testReclaimDeclinedHook(bool skip) {
- for (unsigned int i = 0; i < TEST_LEASES_NUM; ++i) {
- // Mark leases with even indexes as expired.
- if (evenLeaseIndex(i)) {
- // Mark lease as declined with 100 seconds of probation-period
- // (i.e. lease is supposed to be off limits for 100 seconds)
- decline(i, 100);
- // The higher the index, the more expired the lease.
- expire(i, 10 + i);
- }
- }
- EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
- "lease4_recover",
- skip ? lease4RecoverSkipCallout : lease4RecoverCallout));
- // Run leases reclamation routine on all leases.
- ASSERT_NO_THROW(reclaimExpiredLeases(0, 0, true));
- // Make sure that the callout really was called. It was supposed to modify
- // the callout_name_ and store the lease in callout_lease_
- EXPECT_EQ("lease4_recover", callout_name_);
- EXPECT_TRUE(callout_lease_);
- // Leases with even indexes should not exist in the DB
- if (skip) {
- // Skip status should have prevented removing the lease.
- EXPECT_TRUE(testLeases(&leaseExists, &evenLeaseIndex));
- } else {
- // The hook hasn't modified next step status. The lease should be gone.
- EXPECT_TRUE(testLeases(&leaseDoesntExist, &evenLeaseIndex));
- }
- };
- // This test verifies that the leases can be reclaimed without being removed
- // from the database. In such case, the leases' state is set to
- // "expired-reclaimed".
- TEST_F(ExpirationAllocEngine4Test, reclaimExpiredLeasesUpdateState) {
- testReclaimExpiredLeasesUpdateState();
- }
- // This test verifies that the reclaimed leases are deleted when requested.
- TEST_F(ExpirationAllocEngine4Test, reclaimExpiredLeasesDelete) {
- testReclaimExpiredLeasesDelete();
- }
- // This test verifies that it is possible to specify the limit for the
- // number of reclaimed leases.
- TEST_F(ExpirationAllocEngine4Test, reclaimExpiredLeasesLimit) {
- testReclaimExpiredLeasesLimit();
- }
- // This test verifies that DNS updates are generated for the leases
- // for which the DNS records exist.
- TEST_F(ExpirationAllocEngine4Test, reclaimExpiredLeasesWithDDNS) {
- testReclaimExpiredLeasesWithDDNS();
- }
- // This test verifies that it is DNS updates are generated only for the
- // reclaimed expired leases. In this case we limit the number of leases
- // reclaimed during a single call to reclamation routine.
- TEST_F(ExpirationAllocEngine4Test, reclaimExpiredLeasesWithDDNSAndLimit) {
- testReclaimExpiredLeasesWithDDNSAndLimit();
- }
- // This test verifies that if some leases have invalid hostnames, the
- // lease reclamation routine continues with reclamation of leases anyway.
- TEST_F(ExpirationAllocEngine4Test, reclaimExpiredLeasesInvalidHostname) {
- testReclaimExpiredLeasesInvalidHostname();
- }
- // This test verifies that DNS updates are properly generated when the
- // client id is used as a primary identifier in the lease.
- TEST_F(ExpirationAllocEngine4Test, reclaimExpiredLeasesWithDDNSAndClientId) {
- testReclaimExpiredLeasesWithDDNSAndClientId();
- }
- // This test verifies that statistics is correctly updated when the leases
- // are reclaimed.
- TEST_F(ExpirationAllocEngine4Test, reclaimExpiredLeasesStats) {
- testReclaimExpiredLeasesStats();
- }
- // This test verifies that callouts are executed for each expired lease.
- TEST_F(ExpirationAllocEngine4Test, reclaimExpiredLeasesHooks) {
- testReclaimExpiredLeasesHooks();
- }
- // This test verifies that callouts are executed for each expired lease
- // and that the lease is not reclaimed when the skip flag is set.
- TEST_F(ExpirationAllocEngine4Test, reclaimExpiredLeasesHooksWithSkip) {
- testReclaimExpiredLeasesHooksWithSkip();
- }
- // This test verifies that it is possible to set the timeout for the
- // execution of the lease reclamation routine.
- TEST_F(ExpirationAllocEngine4Test, reclaimExpiredLeasesTimeout) {
- // This test needs at least 40 leases to make sense.
- BOOST_STATIC_ASSERT(TEST_LEASES_NUM >= 40);
- // Run with timeout of 1.2s.
- testReclaimExpiredLeasesTimeout(1200);
- }
- // This test verifies that at least one lease is reclaimed if the timeout
- // for the lease reclamation routine is shorter than the time needed for
- // the reclamation of a single lease. This prevents the situation when
- // very short timeout (perhaps misconfigured) effectively precludes leases
- // reclamation.
- TEST_F(ExpirationAllocEngine4Test, reclaimExpiredLeasesShortTimeout) {
- // We will most likely reclaim just one lease, so 5 is more than enough.
- BOOST_STATIC_ASSERT(TEST_LEASES_NUM >= 5);
- // Reclaim leases with the 1ms timeout.
- testReclaimExpiredLeasesTimeout(1);
- }
- // This test verifies that expired-reclaimed leases are removed from the
- // lease database.
- TEST_F(ExpirationAllocEngine4Test, deleteExpiredReclaimedLeases) {
- BOOST_STATIC_ASSERT(TEST_LEASES_NUM >= 10);
- testDeleteExpiredReclaimedLeases();
- }
- /// This test verifies that @ref AllocEngine::reclaimExpiredLeases4 properly
- /// handles declined leases that have expired in case when it is told to
- /// remove leases.
- TEST_F(ExpirationAllocEngine4Test, reclaimDeclined1) {
- testReclaimDeclined(true);
- }
- /// This test verifies that @ref AllocEngine::reclaimExpiredLeases4 properly
- /// handles declined leases that have expired in case when it is told to
- /// not remove leases. This flag should not matter and declined expired
- /// leases should always be removed.
- TEST_F(ExpirationAllocEngine4Test, reclaimDeclined2) {
- testReclaimDeclined(false);
- }
- /// This test verifies that statistics are modified correctly after
- /// reclaim expired leases is called.
- TEST_F(ExpirationAllocEngine4Test, reclaimDeclinedStats) {
- testReclaimDeclinedStats("assigned-addresses");
- }
- // This test verifies that the lease is reclaimed before it is reused.
- TEST_F(ExpirationAllocEngine4Test, reclaimReusedLeases) {
- // First false value indicates that the leases will be reused.
- // Second false value indicates that the lease will not be
- // initially reclaimed.
- testReclaimReusedLeases(DHCPREQUEST, false, false);
- }
- // This test verifies that the lease is not reclaimed when it is
- // reused and if its state indicates that it has been already reclaimed.
- TEST_F(ExpirationAllocEngine4Test, reclaimReusedLeasesAlreadyReclaimed) {
- // false value indicates that the leases will be reused
- // true value indicates that the lease will be initially reclaimed.
- testReclaimReusedLeases(DHCPREQUEST, false, true);
- }
- // This test verifies that the expired lease is reclaimed before it
- // is renewed.
- TEST_F(ExpirationAllocEngine4Test, reclaimRenewedLeases) {
- // true value indicates that the leases will be renewed.
- // false value indicates that the lease will not be initially
- // reclaimed.
- testReclaimReusedLeases(DHCPREQUEST, true, false);
- }
- // This test verifies that the lease is not reclaimed upon renewal
- // if its state indicates that it has been already reclaimed.
- TEST_F(ExpirationAllocEngine4Test, reclaimRenewedLeasesAlreadyReclaimed) {
- // First true value indicates that the leases will be renewed.
- // Second true value indicates that the lease will be initially
- // reclaimed.
- testReclaimReusedLeases(DHCPREQUEST, true, true);
- }
- // This test verifies that the reused lease is not reclaimed when the
- // processed message is a DHCPDISCOVER.
- TEST_F(ExpirationAllocEngine4Test, reclaimReusedLeasesDiscover) {
- testReclaimReusedLeases(DHCPDISCOVER, false, false);
- }
- // This test verifies that the lease being in the 'expired-reclaimed'
- // state is not reclaimed again when processing the DHCPDISCOVER
- // message.
- TEST_F(ExpirationAllocEngine4Test, reclaimRenewedLeasesDiscoverAlreadyReclaimed) {
- testReclaimReusedLeases(DHCPDISCOVER, false, true);
- }
- // This test verifies if the hooks installed on lease4_recover are called
- // when the lease expires.
- TEST_F(ExpirationAllocEngine4Test, reclaimDeclinedHook1) {
- testReclaimDeclinedHook(false); // false = don't use skip callout
- }
- // This test verifies if the hooks installed on lease4_recover are called
- // when the lease expires and that the next step status set to SKIP
- // causes the recovery to not be conducted.
- TEST_F(ExpirationAllocEngine4Test, reclaimDeclinedHook2) {
- testReclaimDeclinedHook(true); // true = use skip callout
- }
- }; // end of anonymous namespace
|