diff --git a/src/bin/lfc/tests/lfc_controller_unittests.cc b/src/bin/lfc/tests/lfc_controller_unittests.cc index cd7e12ce55..6c734a5412 100644 --- a/src/bin/lfc/tests/lfc_controller_unittests.cc +++ b/src/bin/lfc/tests/lfc_controller_unittests.cc @@ -90,11 +90,11 @@ protected: cstr_ = base_dir + "/" + "config_file"; // config v4_hdr_ = "address,hwaddr,client_id,valid_lifetime,expire,subnet_id," - "fqdn_fwd,fqdn_rev,hostname\n"; + "fqdn_fwd,fqdn_rev,hostname,state\n"; v6_hdr_ = "address,duid,valid_lifetime,expire,subnet_id," "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd," - "fqdn_rev,hostname,hwaddr\n"; + "fqdn_rev,hostname,hwaddr,state\n"; // and remove any outstanding test files removeTestFile(); @@ -402,26 +402,26 @@ TEST_F(LFCControllerTest, launch4) { // We have several entries for different leases, the naming is: // _ string a_1 = "192.0.2.1,06:07:08:09:0a:bc,," - "200,200,8,1,1,host.example.com\n"; + "200,200,8,1,1,host.example.com,1\n"; string a_2 = "192.0.2.1,06:07:08:09:0a:bc,," - "200,500,8,1,1,host.example.com\n"; + "200,500,8,1,1,host.example.com,1\n"; string a_3 = "192.0.2.1,06:07:08:09:0a:bc,," - "200,800,8,1,1,host.example.com\n"; + "200,800,8,1,1,host.example.com,1\n"; string b_1 = "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04," - "100,100,7,0,0,\n"; + "100,100,7,0,0,,1\n"; string b_2 = "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04," - "100,135,7,0,0,\n"; + "100,135,7,0,0,,1\n"; string b_3 = "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04," - "100,150,7,0,0,\n"; + "100,150,7,0,0,,1\n"; string c_1 = "192.0.2.3,,a:11:01:04," - "200,200,8,1,1,host.example.com\n"; + "200,200,8,1,1,host.example.com,1\n"; string d_1 = "192.0.2.5,16:17:18:19:1a:bc,," - "200,200,8,1,1,host.example.com\n"; + "200,200,8,1,1,host.example.com,1\n"; string d_2 = "192.0.2.5,16:17:18:19:1a:bc,," - "0,200,8,1,1,host.example.com\n"; + "0,200,8,1,1,host.example.com,1\n"; // Subtest 1: both previous and copy available. // Create the test previous file @@ -556,27 +556,27 @@ TEST_F(LFCControllerTest, launch6) { // We have several entries for different leases, the naming is: // _. string a_1 = "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f," - "200,200,8,100,0,7,0,1,1,host.example.com,\n"; - string a_2 = "2001:db8:1::1,,200,200,8,100,0,7,0,1,1,host.example.com,\n"; + "200,200,8,100,0,7,0,1,1,host.example.com,,1\n"; + string a_2 = "2001:db8:1::1,,200,200,8,100,0,7,0,1,1,host.example.com,,1\n"; string a_3 = "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f," - "200,400,8,100,0,7,0,1,1,host.example.com,\n"; + "200,400,8,100,0,7,0,1,1,host.example.com,,1\n"; string a_4 = "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f," - "0,200,8,100,0,7,0,1,1,host.example.com,\n"; + "0,200,8,100,0,7,0,1,1,host.example.com,,1\n"; string b_1 = "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05," - "300,300,6,150,0,8,0,0,0,,\n"; + "300,300,6,150,0,8,0,0,0,,,1\n"; string b_2 = "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05," - "300,800,6,150,0,8,0,0,0,,\n"; + "300,800,6,150,0,8,0,0,0,,,1\n"; string b_3 = "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05," - "300,1000,6,150,0,8,0,0,0,,\n"; + "300,1000,6,150,0,8,0,0,0,,,1\n"; string c_1 = "3000:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f," - "100,200,8,0,2,16,64,0,0,,\n"; + "100,200,8,0,2,16,64,0,0,,,1\n"; string c_2 = "3000:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f," - "100,400,8,0,2,16,64,0,0,,\n"; + "100,400,8,0,2,16,64,0,0,,,1\n"; string d_1 = "2001:db8:1::3,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f," - "200,600,8,100,0,7,0,1,1,host.example.com,\n"; + "200,600,8,100,0,7,0,1,1,host.example.com,,1\n"; // Subtest 1: bot previous and copy available // Create the test previous file diff --git a/src/lib/dhcpsrv/alloc_engine.cc b/src/lib/dhcpsrv/alloc_engine.cc index 4dc5320e6b..fa66d2e35a 100644 --- a/src/lib/dhcpsrv/alloc_engine.cc +++ b/src/lib/dhcpsrv/alloc_engine.cc @@ -931,7 +931,6 @@ AllocEngine::reuseExpiredLease(Lease6Ptr& expired, ClientContext6& ctx, expired->t2_ = ctx.subnet_->getT2(); expired->cltt_ = time(NULL); expired->subnet_id_ = ctx.subnet_->getID(); - expired->fixed_ = false; expired->hostname_ = ctx.hostname_; expired->fqdn_fwd_ = ctx.fwd_dns_update_; expired->fqdn_rev_ = ctx.rev_dns_update_; @@ -1912,7 +1911,6 @@ AllocEngine::reuseExpiredLease4(Lease4Ptr& expired, } updateLease4Information(expired, ctx); - expired->fixed_ = false; LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE_DETAIL_DATA, ALLOC_ENGINE_V4_REUSE_EXPIRED_LEASE_DATA) diff --git a/src/lib/dhcpsrv/csv_lease_file4.cc b/src/lib/dhcpsrv/csv_lease_file4.cc index 8ed0e82f31..844971e493 100644 --- a/src/lib/dhcpsrv/csv_lease_file4.cc +++ b/src/lib/dhcpsrv/csv_lease_file4.cc @@ -59,6 +59,7 @@ CSVLeaseFile4::append(const Lease4& lease) { row.writeAt(getColumnIndex("fqdn_fwd"), lease.fqdn_fwd_); row.writeAt(getColumnIndex("fqdn_rev"), lease.fqdn_rev_); row.writeAt(getColumnIndex("hostname"), lease.hostname_); + row.writeAt(getColumnIndex("state"), lease.state_); try { CSVFile::append(row); @@ -115,6 +116,7 @@ CSVLeaseFile4::next(Lease4Ptr& lease) { readFqdnFwd(row), readFqdnRev(row), readHostname(row))); + lease->state_ = readState(row); } catch (std::exception& ex) { // bump the read error count @@ -144,6 +146,7 @@ CSVLeaseFile4::initColumns() { addColumn("fqdn_fwd"); addColumn("fqdn_rev"); addColumn("hostname"); + addColumn("state"); } IOAddress @@ -212,5 +215,11 @@ CSVLeaseFile4::readHostname(const CSVRow& row) { return (hostname); } +uint32_t +CSVLeaseFile4::readState(const util::CSVRow& row) { + uint32_t state = row.readAndConvertAt(getColumnIndex("state")); + return (state); +} + } // end of namespace isc::dhcp } // end of namespace isc diff --git a/src/lib/dhcpsrv/csv_lease_file4.h b/src/lib/dhcpsrv/csv_lease_file4.h index 736316209d..7f302aba8b 100644 --- a/src/lib/dhcpsrv/csv_lease_file4.h +++ b/src/lib/dhcpsrv/csv_lease_file4.h @@ -102,6 +102,7 @@ private: /// - fqdn_fwd /// - fqdn_rev /// - hostname + /// - state void initColumns(); /// @@ -111,48 +112,53 @@ private: /// /// @brief Reads lease address from the CSV file row. /// - /// @param row CSV file holding lease values. + /// @param row CSV file row holding lease information. asiolink::IOAddress readAddress(const util::CSVRow& row); /// @brief Reads HW address from the CSV file row. /// - /// @param row CSV file holding lease values. + /// @param row CSV file row holding lease information. HWAddr readHWAddr(const util::CSVRow& row); /// @brief Reads client identifier from the CSV file row. /// - /// @param row CSV file holding lease values. + /// @param row CSV file row holding lease information. ClientIdPtr readClientId(const util::CSVRow& row); /// @brief Reads valid lifetime from the CSV file row. /// - /// @param row CSV file holding lease values. + /// @param row CSV file row holding lease information. uint32_t readValid(const util::CSVRow& row); /// @brief Reads cltt value from the CSV file row. /// - /// @param row CSV file holding lease values. + /// @param row CSV file row holding lease information. time_t readCltt(const util::CSVRow& row); /// @brief Reads subnet id from the CSV file row. /// - /// @param row CSV file holding lease values. + /// @param row CSV file row holding lease information. SubnetID readSubnetID(const util::CSVRow& row); /// @brief Reads the FQDN forward flag from the CSV file row. /// - /// @param row CSV file holding lease values. + /// @param row CSV file row holding lease information. bool readFqdnFwd(const util::CSVRow& row); /// @brief Reads the FQDN reverse flag from the CSV file row. /// - /// @param row CSV file holding lease values. + /// @param row CSV file row holding lease information. bool readFqdnRev(const util::CSVRow& row); /// @brief Reads hostname from the CSV file row. /// - /// @param row CSV file holding lease values. + /// @param row CSV file row holding lease information. std::string readHostname(const util::CSVRow& row); + + /// @brief Reads lease state from the CSV file row. + /// + /// @param row CSV file row holding lease information. + uint32_t readState(const util::CSVRow& row); //@} }; diff --git a/src/lib/dhcpsrv/csv_lease_file6.cc b/src/lib/dhcpsrv/csv_lease_file6.cc index a8c0d50c90..ce1dcb5fa3 100644 --- a/src/lib/dhcpsrv/csv_lease_file6.cc +++ b/src/lib/dhcpsrv/csv_lease_file6.cc @@ -59,6 +59,7 @@ CSVLeaseFile6::append(const Lease6& lease) { // We may not have hardware information row.writeAt(getColumnIndex("hwaddr"), lease.hwaddr_->toText(false)); } + row.writeAt(getColumnIndex("state"), lease.state_); try { CSVFile::append(row); } catch (const std::exception&) { @@ -100,6 +101,7 @@ CSVLeaseFile6::next(Lease6Ptr& lease) { lease->fqdn_fwd_ = readFqdnFwd(row); lease->fqdn_rev_ = readFqdnRev(row); lease->hostname_ = readHostname(row); + lease->state_ = readState(row); } catch (std::exception& ex) { // bump the read error count @@ -133,6 +135,7 @@ CSVLeaseFile6::initColumns() { addColumn("fqdn_rev"); addColumn("hostname"); addColumn("hwaddr"); + addColumn("state"); } Lease::Type @@ -226,10 +229,7 @@ CSVLeaseFile6::readHWAddr(const CSVRow& row) { // Let's return a pointer to new freshly created copy. return (HWAddrPtr(new HWAddr(hwaddr))); - } catch (const CSVFileError&) { - // That's ok, we may be reading old CSV file that didn't store hwaddr - return (HWAddrPtr()); - } catch (const BadValue& ex) { + } catch (const std::exception& ex) { // That's worse. There was something in the file, but its conversion // to HWAddr failed. Let's log it on warning and carry on. LOG_WARN(dhcpsrv_logger, DHCPSRV_MEMFILE_READ_HWADDR_FAIL) @@ -239,21 +239,10 @@ CSVLeaseFile6::readHWAddr(const CSVRow& row) { } } -bool -CSVLeaseFile6::validateHeader(const isc::util::CSVRow& header) { - - if (!CSVFile::validateHeader(header)) { - - // One possible validation failure is that we're reading Kea 0.9 - // lease file that didn't have hwaddr column. Let's add it and - // try to revalidate. - isc::util::CSVRow copy = header; - copy.append("hwaddr"); - return CSVFile::validateHeader(copy); - } else { - return (true); - } - +uint32_t +CSVLeaseFile6::readState(const util::CSVRow& row) { + uint32_t state = row.readAndConvertAt(getColumnIndex("state")); + return (state); } } // end of namespace isc::dhcp diff --git a/src/lib/dhcpsrv/csv_lease_file6.h b/src/lib/dhcpsrv/csv_lease_file6.h index 2c2a0f6f86..e4b221f72f 100644 --- a/src/lib/dhcpsrv/csv_lease_file6.h +++ b/src/lib/dhcpsrv/csv_lease_file6.h @@ -87,18 +87,6 @@ public: /// ticket http://kea.isc.org/ticket/2405 is implemented. bool next(Lease6Ptr& lease); -protected: - /// @brief This function validates the header of the Lease6 CSV file. - /// - /// It works similar to @c CSVFile::validateHeader, but if the validation - /// fails, it attempts to add hwaddr column and retry validation. - /// That's useful when attmepting to read CSV file generated in 0.9 - /// (did not have hwaddr field) in 0.9.1 or later code. - /// - /// @param header A row holding a header. - /// @return true if header matches the columns; false otherwise. - virtual bool validateHeader(const isc::util::CSVRow& header); - private: /// @brief Initializes columns of the CSV file holding leases. @@ -117,6 +105,7 @@ private: /// - fqdn_rev /// - hostname /// - hwaddr + /// - state void initColumns(); /// @@ -126,69 +115,74 @@ private: /// /// @brief Reads lease type from the CSV file row. /// - /// @param row CSV file holding lease values. + /// @param row CSV file row holding lease information. Lease::Type readType(const util::CSVRow& row); /// @brief Reads lease address from the CSV file row. /// - /// @param row CSV file holding lease values. + /// @param row CSV file row holding lease information. asiolink::IOAddress readAddress(const util::CSVRow& row); /// @brief Reads DUID from the CSV file row. /// - /// @param row CSV file holding lease values. + /// @param row CSV file row holding lease information. DuidPtr readDUID(const util::CSVRow& row); /// @brief Reads IAID from the CSV file row. /// - /// @param row CSV file holding lease values. + /// @param row CSV file row holding lease information. uint32_t readIAID(const util::CSVRow& row); /// @brief Reads preferred lifetime from the CSV file row. /// - /// @param row CSV file holding lease values. + /// @param row CSV file row holding lease information. uint32_t readPreferred(const util::CSVRow& row); /// @brief Reads valid lifetime from the CSV file row. /// - /// @param row CSV file holding lease values. + /// @param row CSV file row holding lease information. uint32_t readValid(const util::CSVRow& row); /// @brief Reads cltt value from the CSV file row. /// - /// @param row CSV file holding lease values. + /// @param row CSV file row holding lease information. uint32_t readCltt(const util::CSVRow& row); /// @brief Reads subnet id from the CSV file row. /// - /// @param row CSV file holding lease values. + /// @param row CSV file row holding lease information. SubnetID readSubnetID(const util::CSVRow& row); /// @brief Reads prefix length from the CSV file row. /// - /// @param row CSV file holding lease values. + /// @param row CSV file row holding lease information. uint8_t readPrefixLen(const util::CSVRow& row); /// @brief Reads the FQDN forward flag from the CSV file row. /// - /// @param row CSV file holding lease values. + /// @param row CSV file row holding lease information. bool readFqdnFwd(const util::CSVRow& row); /// @brief Reads the FQDN reverse flag from the CSV file row. /// - /// @param row CSV file holding lease values. + /// @param row CSV file row holding lease information. bool readFqdnRev(const util::CSVRow& row); /// @brief Reads hostname from the CSV file row. /// - /// @param row CSV file holding lease values. + /// @param row CSV file row holding lease information. std::string readHostname(const util::CSVRow& row); /// @brief Reads HW address from the CSV file row. /// - /// @param row CSV file holding lease values. + /// @param row CSV file row holding lease information. /// @return pointer to the HWAddr structure that was read HWAddrPtr readHWAddr(const util::CSVRow& row); + + /// @brief Reads lease state from the CSV file row. + /// + /// @param row CSV file row holding lease information. + uint32_t readState(const util::CSVRow& row); //@} }; diff --git a/src/lib/dhcpsrv/dhcpsrv_messages.mes b/src/lib/dhcpsrv/dhcpsrv_messages.mes index e3e887b7fd..8dd151e9df 100644 --- a/src/lib/dhcpsrv/dhcpsrv_messages.mes +++ b/src/lib/dhcpsrv/dhcpsrv_messages.mes @@ -231,6 +231,23 @@ A debug message issued when the server is attempting to delete a lease for the specified address from the memory file database for the specified address. +% DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED4 deleting reclaimed leases expired more than %1 s ago +A debug message issued when the server is removing reclaimed DHCPv4 +leases which have expired longer than a specified period of time. +The argument specified the number of seconds since leases' expiration +before they can be removed. + +% DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED6 deleting reclaimed leases expired more than %1 s ago +A debug message issued when the server is removing reclaimed DHCPv6 +leases which have expired longer than a specified period of time. +The argument specified the number of seconds since leases' expiration +before they can be removed. + +% DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED_START starting deletion of %1 expired-reclaimed leases +A debug message issued wheb the server has found expired-reclaimed +leases to be removed. The number of leases to be removed is logged +in the message. + % DHCPSRV_MEMFILE_GET_ADDR4 obtaining IPv4 lease for address %1 A debug message issued when the server is attempting to obtain an IPv4 lease from the memory file database for the specified address. diff --git a/src/lib/dhcpsrv/lease.cc b/src/lib/dhcpsrv/lease.cc index 616974852d..9e57408faf 100644 --- a/src/lib/dhcpsrv/lease.cc +++ b/src/lib/dhcpsrv/lease.cc @@ -15,6 +15,7 @@ #include #include #include +#include using namespace isc::util; using namespace std; @@ -22,13 +23,17 @@ using namespace std; namespace isc { namespace dhcp { +const uint32_t Lease::STATE_DEFAULT = 0x0; +const uint32_t Lease::STATE_DECLINED = 0x1; +const uint32_t Lease::STATE_EXPIRED_RECLAIMED = 0x2; + Lease::Lease(const isc::asiolink::IOAddress& addr, uint32_t t1, uint32_t t2, uint32_t valid_lft, SubnetID subnet_id, time_t cltt, const bool fqdn_fwd, const bool fqdn_rev, const std::string& hostname, const HWAddrPtr& hwaddr) :addr_(addr), t1_(t1), t2_(t2), valid_lft_(valid_lft), cltt_(cltt), - subnet_id_(subnet_id), fixed_(false), hostname_(hostname), - fqdn_fwd_(fqdn_fwd), fqdn_rev_(fqdn_rev), hwaddr_(hwaddr) { + subnet_id_(subnet_id), hostname_(hostname), fqdn_fwd_(fqdn_fwd), + fqdn_rev_(fqdn_rev), hwaddr_(hwaddr), state_(STATE_DEFAULT) { } @@ -52,11 +57,37 @@ Lease::typeToText(Lease::Type type) { } } -bool Lease::expired() const { +std::string +Lease::basicStatesToText(const uint32_t state) { + switch (state) { + case STATE_DEFAULT: + return ("default"); + case STATE_DECLINED: + return ("declined"); + case STATE_EXPIRED_RECLAIMED: + return ("expired-reclaimed"); + default: + // The default case will be handled further on + ; + } + std::ostringstream s; + s << "unknown (" << state << ")"; + return s.str(); +} - // Let's use int64 to avoid problems with negative/large uint32 values - int64_t expire_time = cltt_ + valid_lft_; - return (expire_time < time(NULL)); +bool +Lease::expired() const { + return (getExpirationTime() < time(NULL)); +} + +bool +Lease::stateExpiredReclaimed() const { + return (state_ == STATE_EXPIRED_RECLAIMED); +} + +int64_t +Lease::getExpirationTime() const { + return (static_cast(cltt_) + valid_lft_); } bool @@ -69,11 +100,10 @@ Lease::hasIdenticalFqdn(const Lease& other) const { Lease4::Lease4(const Lease4& other) : Lease(other.addr_, other.t1_, other.t2_, other.valid_lft_, other.subnet_id_, other.cltt_, other.fqdn_fwd_, - other.fqdn_rev_, other.hostname_, other.hwaddr_), - ext_(other.ext_) { + other.fqdn_rev_, other.hostname_, other.hwaddr_) { - fixed_ = other.fixed_; - comments_ = other.comments_; + // Copy over fields derived from Lease. + state_ = other.state_; // Copy the hardware address if it is defined. if (other.hwaddr_) { @@ -105,10 +135,13 @@ Lease4::Lease4(const isc::asiolink::IOAddress& address, : Lease(address, t1, t2, valid_lifetime, subnet_id, cltt, fqdn_fwd, fqdn_rev, hostname, hw_address), - ext_(0), client_id_(client_id) { + client_id_(client_id) { } - +std::string +Lease4::statesToText(const uint32_t state) { + return (Lease::basicStatesToText(state)); +} const std::vector& Lease4::getClientIdVector() const { @@ -155,12 +188,10 @@ Lease4::operator=(const Lease4& other) { valid_lft_ = other.valid_lft_; cltt_ = other.cltt_; subnet_id_ = other.subnet_id_; - fixed_ = other.fixed_; hostname_ = other.hostname_; fqdn_fwd_ = other.fqdn_fwd_; fqdn_rev_ = other.fqdn_rev_; - comments_ = other.comments_; - ext_ = other.ext_; + state_ = other.state_; // Copy the hardware address if it is defined. if (other.hwaddr_) { @@ -215,6 +246,11 @@ Lease6::Lease6() duid_(DuidPtr()), preferred_lft_(0) { } +std::string +Lease6::statesToText(const uint32_t state) { + return (Lease::basicStatesToText(state)); +} + const std::vector& Lease6::getDuidVector() const { if (!duid_) { @@ -239,7 +275,8 @@ Lease6::toText() const { << "Valid life: " << valid_lft_ << "\n" << "Cltt: " << cltt_ << "\n" << "Hardware addr: " << (hwaddr_?hwaddr_->toText(false):"(none)") << "\n" - << "Subnet ID: " << subnet_id_ << "\n"; + << "Subnet ID: " << subnet_id_ << "\n" + << "State: " << statesToText(state_) << "\n"; return (stream.str()); } @@ -255,7 +292,8 @@ Lease4::toText() const { << "Cltt: " << cltt_ << "\n" << "Hardware addr: " << (hwaddr_ ? hwaddr_->toText(false) : "(none)") << "\n" << "Client id: " << (client_id_ ? client_id_->toText() : "(none)") << "\n" - << "Subnet ID: " << subnet_id_ << "\n"; + << "Subnet ID: " << subnet_id_ << "\n" + << "State: " << statesToText(state_) << "\n"; return (stream.str()); } @@ -266,17 +304,15 @@ Lease4::operator==(const Lease4& other) const { return (nullOrEqualValues(hwaddr_, other.hwaddr_) && nullOrEqualValues(client_id_, other.client_id_) && addr_ == other.addr_ && - ext_ == other.ext_ && subnet_id_ == other.subnet_id_ && t1_ == other.t1_ && t2_ == other.t2_ && valid_lft_ == other.valid_lft_ && cltt_ == other.cltt_ && - fixed_ == other.fixed_ && hostname_ == other.hostname_ && fqdn_fwd_ == other.fqdn_fwd_ && fqdn_rev_ == other.fqdn_rev_ && - comments_ == other.comments_); + state_ == other.state_); } bool @@ -293,11 +329,10 @@ Lease6::operator==(const Lease6& other) const { t2_ == other.t2_ && cltt_ == other.cltt_ && subnet_id_ == other.subnet_id_ && - fixed_ == other.fixed_ && hostname_ == other.hostname_ && fqdn_fwd_ == other.fqdn_fwd_ && fqdn_rev_ == other.fqdn_rev_ && - comments_ == other.comments_); + state_ == other.state_); } std::ostream& diff --git a/src/lib/dhcpsrv/lease.h b/src/lib/dhcpsrv/lease.h index 082c8de438..1fc53847e2 100644 --- a/src/lib/dhcpsrv/lease.h +++ b/src/lib/dhcpsrv/lease.h @@ -48,6 +48,29 @@ struct Lease { /// @return text decription static std::string typeToText(Type type); + /// @name Common lease states constants. + //@{ + /// + /// @brief A lease in the default state. + static const uint32_t STATE_DEFAULT; + + /// @brief Declined lease. + static const uint32_t STATE_DECLINED; + + /// @brief Expired and reclaimed lease. + static const uint32_t STATE_EXPIRED_RECLAIMED; + + //@} + + /// @brief Returns name(s) of the basic lease state(s). + /// + /// @param state A numeric value holding a state information. + /// Some states may be composite, i.e. the single state value + /// maps to multiple logical states of the lease. + /// + /// @return Comma separated list of state names. + static std::string basicStatesToText(const uint32_t state); + /// @brief Constructor /// /// @param addr IP address @@ -108,11 +131,6 @@ struct Lease { /// Specifies the identification of the subnet to which the lease belongs. SubnetID subnet_id_; - /// @brief Fixed lease? - /// - /// Fixed leases are kept after they are released/expired. - bool fixed_; - /// @brief Client hostname /// /// This field may be empty @@ -133,11 +151,16 @@ struct Lease { /// This information may not be available in certain cases. HWAddrPtr hwaddr_; - /// @brief Lease comments + /// @brief Holds the lease state(s). /// - /// Currently not used. It may be used for keeping comments made by the - /// system administrator. - std::string comments_; + /// This is the field that holds the lease state(s). Typically, a + /// lease remains in a single states. However, it is posible to + /// define a value for state which indicates that the lease remains + /// in multiple logical states. + /// + /// The defined states are represented by the "STATE_*" constants + /// belonging to this class. + uint32_t state_; /// @brief Convert Lease to Printable Form /// @@ -148,6 +171,12 @@ struct Lease { /// @return true if the lease is expired bool expired() const; + /// @brief Indicates if the lease is in the "expired-reclaimed" state. + /// + /// @return true if the lease is in the "expired-reclaimed" state, false + /// otherwise. + bool stateExpiredReclaimed() const; + /// @brief Returns true if the other lease has equal FQDN data. /// /// @param other Lease which FQDN data is to be compared with our lease. @@ -165,6 +194,12 @@ struct Lease { /// /// @return const reference to the hardware address const std::vector& getHWAddrVector() const; + + /// @brief Returns lease expiration time. + /// + /// The lease expiration time is a sum of a client last transmission time + /// and valid lifetime. + int64_t getExpirationTime() const; }; /// @brief Structure that holds a lease for IPv4 address @@ -175,16 +210,6 @@ struct Lease { /// extensively, direct access is warranted. struct Lease4 : public Lease { - /// @brief Address extension - /// - /// It is envisaged that in some cases IPv4 address will be accompanied - /// with some additional data. One example of such use are Address + Port - /// solutions (or Port-restricted Addresses), where several clients may get - /// the same address, but different port ranges. This feature is not - /// expected to be widely used. Under normal circumstances, the value - /// should be 0. - uint32_t ext_; - /// @brief Client identifier /// /// @todo Should this be a pointer to a client ID or the ID itself? @@ -211,7 +236,7 @@ struct Lease4 : public Lease { const bool fqdn_fwd = false, const bool fqdn_rev = false, const std::string& hostname = "") : Lease(addr, t1, t2, valid_lft, subnet_id, cltt, fqdn_fwd, fqdn_rev, - hostname, hwaddr), ext_(0) { + hostname, hwaddr) { if (clientid_len) { client_id_.reset(new ClientId(clientid, clientid_len)); } @@ -246,7 +271,7 @@ struct Lease4 : public Lease { /// @brief Default constructor /// /// Initialize fields that don't have a default constructor. - Lease4() : Lease(0, 0, 0, 0, 0, 0, false, false, "", HWAddrPtr()), ext_(0) + Lease4() : Lease(0, 0, 0, 0, 0, 0, false, false, "", HWAddrPtr()) { } @@ -255,6 +280,16 @@ struct Lease4 : public Lease { /// @param other the @c Lease4 object to be copied. Lease4(const Lease4& other); + /// @brief Returns name of the lease states specific to DHCPv4. + /// + /// @todo Currently it simply returns common states for DHCPv4 and DHCPv6. + /// This method will have to be extended to handle DHCPv4 specific states + /// when they are defined. + /// + /// @param state Numeric value holding lease states. + /// @return Comma separated list of lease state names. + static std::string statesToText(const uint32_t state); + /// @brief Returns a client identifier. /// /// @warning Since the function returns the reference to a vector (not a @@ -440,6 +475,16 @@ struct Lease6 : public Lease { /// Initialize fields that don't have a default constructor. Lease6(); + /// @brief Returns name of the lease states specific to DHCPv6. + /// + /// @todo Currently it simply returns common states for DHCPv4 and DHCPv6. + /// This method will have to be extended to handle DHCPv6 specific states + /// when they are defined. + /// + /// @param state Numeric value holding lease states. + /// @return Comma separated list of lease state names. + static std::string statesToText(const uint32_t state); + /// @brief Returns a reference to a vector representing a DUID. /// /// @warning Since the function returns the reference to a vector (not a diff --git a/src/lib/dhcpsrv/lease_mgr.h b/src/lib/dhcpsrv/lease_mgr.h index edacec1c57..40edc9513d 100644 --- a/src/lib/dhcpsrv/lease_mgr.h +++ b/src/lib/dhcpsrv/lease_mgr.h @@ -291,7 +291,6 @@ public: virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid, uint32_t iaid, SubnetID subnet_id) const = 0; - /// @brief returns zero or one IPv6 lease for a given duid+iaid+subnet_id /// /// This function is mostly intended to be used in unit-tests during the @@ -318,6 +317,33 @@ public: Lease6Ptr getLease6(Lease::Type type, const DUID& duid, uint32_t iaid, SubnetID subnet_id) const; + /// @brief Returns a collection of expired DHCPv6 leases. + /// + /// This method returns at most @c max_leases expired leases. The leases + /// returned haven't been reclaimed, i.e. the database query must exclude + /// reclaimed leases from the results returned. + /// + /// @param [out] expired_leases A container to which expired leases returned + /// by the database backend are added. + /// @param max_leases A maximum number of leases to be returned. If this + /// value is set to 0, all expired (but not reclaimed) leases are returned. + virtual void getExpiredLeases6(Lease6Collection& expired_leases, + const size_t max_leases) const = 0; + + + /// @brief Returns a collection of expired DHCPv4 leases. + /// + /// This method returns at most @c max_leases expired leases. The leases + /// returned haven't been reclaimed, i.e. the database query must exclude + /// reclaimed leases from the results returned. + /// + /// @param [out] expired_leases A container to which expired leases returned + /// by the database backend are added. + /// @param max_leases A maximum number of leases to be returned. If this + /// value is set to 0, all expired (but not reclaimed) leases are returned. + virtual void getExpiredLeases4(Lease4Collection& expired_leases, + const size_t max_leases) const = 0; + /// @brief Updates IPv4 lease. /// /// @param lease4 The lease to be updated. @@ -338,6 +364,24 @@ public: /// @return true if deletion was successful, false if no such lease exists virtual bool deleteLease(const isc::asiolink::IOAddress& addr) = 0; + /// @brief Deletes all expired and reclaimed DHCPv4 leases. + /// + /// @param secs Number of seconds since expiration of leases before + /// they can be removed. Leases which have expired later than this + /// time will not be deleted. + /// + /// @return Number of leases deleted. + virtual uint64_t deleteExpiredReclaimedLeases4(const uint32_t secs) = 0; + + /// @brief Deletes all expired and reclaimed DHCPv6 leases. + /// + /// @param secs Number of seconds since expiration of leases before + /// they can be removed. Leases which have expired later than this + /// time will not be deleted. + /// + /// @return Number of leases deleted. + virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t secs) = 0; + /// @brief Return backend type /// /// Returns the type of the backend (e.g. "mysql", "memfile" etc.) diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.cc b/src/lib/dhcpsrv/memfile_lease_mgr.cc index 6bde95e93f..46e06d15c0 100644 --- a/src/lib/dhcpsrv/memfile_lease_mgr.cc +++ b/src/lib/dhcpsrv/memfile_lease_mgr.cc @@ -25,6 +25,7 @@ #include #include #include +#include #include namespace { @@ -320,10 +321,9 @@ Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_GET_ADDR4).arg(addr.toText()); - typedef Lease4Storage::nth_index<0>::type SearchIndex; - const SearchIndex& idx = storage4_.get<0>(); - Lease4Storage::iterator l = idx.find(addr); - if (l == storage4_.end()) { + const Lease4StorageAddressIndex& idx = storage4_.get(); + Lease4StorageAddressIndex::iterator l = idx.find(addr); + if (l == idx.end()) { return (Lease4Ptr()); } else { return (Lease4Ptr(new Lease4(**l))); @@ -334,10 +334,9 @@ Lease4Collection Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr) const { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_GET_HWADDR).arg(hwaddr.toText()); - typedef Lease4Storage::nth_index<0>::type SearchIndex; Lease4Collection collection; - const SearchIndex& idx = storage4_.get<0>(); - for(SearchIndex::const_iterator lease = idx.begin(); + const Lease4StorageAddressIndex& idx = storage4_.get(); + for(Lease4StorageAddressIndex::const_iterator lease = idx.begin(); lease != idx.end(); ++lease) { // Every Lease4 has a hardware address, so we can compare it @@ -355,14 +354,11 @@ Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr, SubnetID subnet_id) const { DHCPSRV_MEMFILE_GET_SUBID_HWADDR).arg(subnet_id) .arg(hwaddr.toText()); - // We are going to use index #1 of the multi index container. - // We define SearchIndex locally in this function because - // currently only this function uses this index. - typedef Lease4Storage::nth_index<1>::type SearchIndex; - // Get the index. - const SearchIndex& idx = storage4_.get<1>(); + // Get the index by HW Address and Subnet Identifier. + const Lease4StorageHWAddressSubnetIdIndex& idx = + storage4_.get(); // Try to find the lease using HWAddr and subnet id. - SearchIndex::const_iterator lease = + Lease4StorageHWAddressSubnetIdIndex::const_iterator lease = idx.find(boost::make_tuple(hwaddr.hwaddr_, subnet_id)); // Lease was not found. Return empty pointer to the caller. if (lease == idx.end()) { @@ -377,10 +373,9 @@ Lease4Collection Memfile_LeaseMgr::getLease4(const ClientId& client_id) const { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_GET_CLIENTID).arg(client_id.toText()); - typedef Lease4Storage::nth_index<0>::type SearchIndex; Lease4Collection collection; - const SearchIndex& idx = storage4_.get<0>(); - for(SearchIndex::const_iterator lease = idx.begin(); + const Lease4StorageAddressIndex& idx = storage4_.get(); + for(Lease4StorageAddressIndex::const_iterator lease = idx.begin(); lease != idx.end(); ++ lease) { // client-id is not mandatory in DHCPv4. There can be a lease that does @@ -402,14 +397,11 @@ Memfile_LeaseMgr::getLease4(const ClientId& client_id, .arg(hwaddr.toText()) .arg(subnet_id); - // We are going to use index #3 of the multi index container. - // We define SearchIndex locally in this function because - // currently only this function uses this index. - typedef Lease4Storage::nth_index<3>::type SearchIndex; - // Get the index. - const SearchIndex& idx = storage4_.get<3>(); + // Get the index by client id, HW address and subnet id. + const Lease4StorageClientIdHWAddressSubnetIdIndex& idx = + storage4_.get(); // Try to get the lease using client id, hardware address and subnet id. - SearchIndex::const_iterator lease = + Lease4StorageClientIdHWAddressSubnetIdIndex::const_iterator lease = idx.find(boost::make_tuple(client_id.getClientId(), hwaddr.hwaddr_, subnet_id)); @@ -429,14 +421,11 @@ Memfile_LeaseMgr::getLease4(const ClientId& client_id, DHCPSRV_MEMFILE_GET_SUBID_CLIENTID).arg(subnet_id) .arg(client_id.toText()); - // We are going to use index #2 of the multi index container. - // We define SearchIndex locally in this function because - // currently only this function uses this index. - typedef Lease4Storage::nth_index<2>::type SearchIndex; - // Get the index. - const SearchIndex& idx = storage4_.get<2>(); + // Get the index by client and subnet id. + const Lease4StorageClientIdSubnetIdIndex& idx = + storage4_.get(); // Try to get the lease using client id and subnet id. - SearchIndex::const_iterator lease = + Lease4StorageClientIdSubnetIdIndex::const_iterator lease = idx.find(boost::make_tuple(client_id.getClientId(), subnet_id)); // Lease was not found. Return empty pointer to the caller. if (lease == idx.end()) { @@ -470,15 +459,15 @@ Memfile_LeaseMgr::getLeases6(Lease::Type type, .arg(duid.toText()) .arg(Lease::typeToText(type)); - // We are going to use index #1 of the multi index container. - typedef Lease6Storage::nth_index<1>::type SearchIndex; - // Get the index. - const SearchIndex& idx = storage6_.get<1>(); + // Get the index by DUID, IAID, lease type. + const Lease6StorageDuidIaidTypeIndex& idx = storage6_.get(); // Try to get the lease using the DUID, IAID and lease type. - std::pair l = + std::pair l = idx.equal_range(boost::make_tuple(duid.getDuid(), iaid, type)); Lease6Collection collection; - for(SearchIndex::iterator lease = l.first; lease != l.second; ++lease) { + for(Lease6StorageDuidIaidTypeIndex::const_iterator lease = + l.first; lease != l.second; ++lease) { collection.push_back(Lease6Ptr(new Lease6(**lease))); } @@ -496,15 +485,15 @@ Memfile_LeaseMgr::getLeases6(Lease::Type type, .arg(duid.toText()) .arg(Lease::typeToText(type)); - // We are going to use index #1 of the multi index container. - typedef Lease6Storage::nth_index<1>::type SearchIndex; - // Get the index. - const SearchIndex& idx = storage6_.get<1>(); + // Get the index by DUID, IAID, lease type. + const Lease6StorageDuidIaidTypeIndex& idx = storage6_.get(); // Try to get the lease using the DUID, IAID and lease type. - std::pair l = + std::pair l = idx.equal_range(boost::make_tuple(duid.getDuid(), iaid, type)); Lease6Collection collection; - for(SearchIndex::iterator lease = l.first; lease != l.second; ++lease) { + for(Lease6StorageDuidIaidTypeIndex::const_iterator lease = + l.first; lease != l.second; ++lease) { // Filter out the leases which subnet id doesn't match. if((*lease)->subnet_id_ == subnet_id) { collection.push_back(Lease6Ptr(new Lease6(**lease))); @@ -514,12 +503,60 @@ Memfile_LeaseMgr::getLeases6(Lease::Type type, return (collection); } +void +Memfile_LeaseMgr::getExpiredLeases6(Lease6Collection& expired_leases, + const size_t max_leases) const { + // Obtain the index which segragates leases by state and time. + const Lease6StorageExpirationIndex& index = storage6_.get(); + + // Retrieve leases which are not reclaimed and which haven't expired. The + // 'less-than' operator will be used for both components of the index. So, + // for the 'state' 'false' is less than 'true'. Also the leases with + // expiration time lower than current time will be returned. + Lease6StorageExpirationIndex::const_iterator ub = + index.upper_bound(boost::make_tuple(false, time(NULL))); + + // Copy only the number of leases indicated by the max_leases parameter. + for (Lease6StorageExpirationIndex::const_iterator lease = index.begin(); + (lease != ub) && ((max_leases == 0) || (std::distance(index.begin(), lease) < + max_leases)); + ++lease) { + expired_leases.push_back(Lease6Ptr(new Lease6(**lease))); + } +} + +void +Memfile_LeaseMgr::getExpiredLeases4(Lease4Collection& expired_leases, + const size_t max_leases) const { + // Obtain the index which segragates leases by state and time. + const Lease4StorageExpirationIndex& index = storage4_.get(); + + // Retrieve leases which are not reclaimed and which haven't expired. The + // 'less-than' operator will be used for both components of the index. So, + // for the 'state' 'false' is less than 'true'. Also the leases with + // expiration time lower than current time will be returned. + Lease4StorageExpirationIndex::const_iterator ub = + index.upper_bound(boost::make_tuple(false, time(NULL))); + + // Copy only the number of leases indicated by the max_leases parameter. + for (Lease4StorageExpirationIndex::const_iterator lease = index.begin(); + (lease != ub) && ((max_leases == 0) || (std::distance(index.begin(), lease) < + max_leases)); + ++lease) { + expired_leases.push_back(Lease4Ptr(new Lease4(**lease))); + } +} + void Memfile_LeaseMgr::updateLease4(const Lease4Ptr& lease) { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_UPDATE_ADDR4).arg(lease->addr_.toText()); - Lease4Storage::iterator lease_it = storage4_.find(lease->addr_); + // Obtain 'by address' index. + Lease4StorageAddressIndex& index = storage4_.get(); + + // Lease must exist if it is to be updated. + Lease4StorageAddressIndex::const_iterator lease_it = index.find(lease->addr_); if (lease_it == storage4_.end()) { isc_throw(NoSuchLease, "failed to update the lease with address " << lease->addr_ << " - no such lease"); @@ -532,7 +569,8 @@ Memfile_LeaseMgr::updateLease4(const Lease4Ptr& lease) { lease_file4_->append(*lease); } - **lease_it = *lease; + // Use replace() to re-index leases. + index.replace(lease_it, Lease4Ptr(new Lease4(*lease))); } void @@ -540,8 +578,12 @@ Memfile_LeaseMgr::updateLease6(const Lease6Ptr& lease) { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_UPDATE_ADDR6).arg(lease->addr_.toText()); - Lease6Storage::iterator lease_it = storage6_.find(lease->addr_); - if (lease_it == storage6_.end()) { + // Obtain 'by address' index. + Lease6StorageAddressIndex& index = storage6_.get(); + + // Lease must exist if it is to be updated. + Lease6StorageAddressIndex::const_iterator lease_it = index.find(lease->addr_); + if (lease_it == index.end()) { isc_throw(NoSuchLease, "failed to update the lease with address " << lease->addr_ << " - no such lease"); } @@ -553,7 +595,8 @@ Memfile_LeaseMgr::updateLease6(const Lease6Ptr& lease) { lease_file6_->append(*lease); } - **lease_it = *lease; + // Use replace() to re-index leases. + index.replace(lease_it, Lease6Ptr(new Lease6(*lease))); } bool @@ -603,6 +646,89 @@ Memfile_LeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) { } } +uint64_t +Memfile_LeaseMgr::deleteExpiredReclaimedLeases4(const uint32_t secs) { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED4) + .arg(secs); + return (deleteExpiredReclaimedLeases< + Lease4StorageExpirationIndex, Lease4 + >(secs, V4, storage4_, lease_file4_)); +} + +uint64_t +Memfile_LeaseMgr::deleteExpiredReclaimedLeases6(const uint32_t secs) { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED6) + .arg(secs); + return (deleteExpiredReclaimedLeases< + Lease6StorageExpirationIndex, Lease6 + >(secs, V6, storage6_, lease_file6_)); +} + +template +uint64_t +Memfile_LeaseMgr::deleteExpiredReclaimedLeases(const uint32_t secs, + const Universe& universe, + StorageType& storage, + LeaseFileType& lease_file) const { + // Obtain the index which segragates leases by state and time. + IndexType& index = storage.template get(); + + // This returns the first element which is greater than the specified + // tuple (true, time(NULL) - secs). However, the range between the + // beginnng of the index and returned element also includes all the + // elements for which the first value is false (lease state is NOT + // reclaimed), because false < true. All elements between the + // beginning of the index and the element returned, for which the + // first value is true, represent the reclaimed leases which should + // be deleted, because their expiration time + secs has occured earlier + // than current time. + typename IndexType::const_iterator upper_limit = + index.upper_bound(boost::make_tuple(true, time(NULL) - secs)); + + // Now, we have to exclude all elements of the index which represent + // leases in the state other than reclaimed - with the first value + // in the index equal to false. Note that elements in the index are + // ordered from the lower to the higher ones. So, all elements with + // the first value of false are placed before the elements with the + // value of true. Hence, we have to find the first element which + // contains value of true. The time value is the lowest possible. + typename IndexType::const_iterator lower_limit = + index.upper_bound(boost::make_tuple(true, std::numeric_limits::min())); + + // If there are some elements in this range, delete them. + uint64_t num_leases = static_cast(std::distance(lower_limit, upper_limit)); + if (num_leases > 0) { + + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED_START) + .arg(num_leases); + + // If lease persistence is enabled, we also have to mark leases + // as deleted in the lease file. We do this by setting the + // lifetime to 0. + if (persistLeases(universe)) { + for (typename IndexType::const_iterator lease = lower_limit; + lease != upper_limit; ++lease) { + // Copy lease to not affect the lease in the container. + LeaseType lease_copy(**lease); + // Set the valid lifetime to 0 to indicate the removal + // of the lease. + lease_copy.valid_lft_ = 0; + lease_file->append(lease_copy); + } + } + + // Erase leases from memory. + index.erase(lower_limit, upper_limit); + } + // Return number of leases deleted. + return (num_leases); +} + + std::string Memfile_LeaseMgr::getDescription() const { return (std::string("This is a dummy memfile backend implementation.\n" @@ -871,6 +997,5 @@ void Memfile_LeaseMgr::lfcExecute(boost::shared_ptr& lease_file) } } - } // end of namespace isc::dhcp } // end of namespace isc diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.h b/src/lib/dhcpsrv/memfile_lease_mgr.h index 053b13ce66..b85b59194c 100644 --- a/src/lib/dhcpsrv/memfile_lease_mgr.h +++ b/src/lib/dhcpsrv/memfile_lease_mgr.h @@ -259,6 +259,33 @@ public: uint32_t iaid, SubnetID subnet_id) const; + /// @brief Returns a collection of expired DHCPv6 leases. + /// + /// This method returns at most @c max_leases expired leases. The leases + /// returned haven't been reclaimed, i.e. the database query must exclude + /// reclaimed leases from the results returned. + /// + /// @param [out] expired_leases A container to which expired leases returned + /// by the database backend are added. + /// @param max_leases A maximum number of leases to be returned. If this + /// value is set to 0, all expired (but not reclaimed) leases are returned. + virtual void getExpiredLeases6(Lease6Collection& expired_leases, + const size_t max_leases) const; + + + /// @brief Returns a collection of expired DHCPv4 leases. + /// + /// This method returns at most @c max_leases expired leases. The leases + /// returned haven't been reclaimed, i.e. the database query must exclude + /// reclaimed leases from the results returned. + /// + /// @param [out] expired_leases A container to which expired leases returned + /// by the database backend are added. + /// @param max_leases A maximum number of leases to be returned. If this + /// value is set to 0, all expired (but not reclaimed) leases are returned. + virtual void getExpiredLeases4(Lease4Collection& expired_leases, + const size_t max_leases) const; + /// @brief Updates IPv4 lease. /// /// @warning This function does not validate the pointer to the lease. @@ -287,6 +314,61 @@ public: /// @return true if deletion was successful, false if no such lease exists virtual bool deleteLease(const isc::asiolink::IOAddress& addr); + /// @brief Deletes all expired-reclaimed DHCPv4 leases. + /// + /// @param secs Number of seconds since expiration of leases before + /// they can be removed. Leases which have expired later than this + /// time will not be deleted. + /// + /// @return Number of leases deleted. + virtual uint64_t deleteExpiredReclaimedLeases4(const uint32_t secs); + + /// @brief Deletes all expired-reclaimed DHCPv6 leases. + /// + /// @param secs Number of seconds since expiration of leases before + /// they can be removed. Leases which have expired later than this + /// time will not be deleted. + /// + /// @return Number of leases deleted. + virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t secs); + +private: + + /// @brief Deletes all expired-reclaimed leases. + /// + /// This private method is called by both of the public methods: + /// @c deleteExpiredReclaimedLeases4 and + /// @c deleteExpiredReclaimedLeases6 to remove all expired + /// reclaimed DHCPv4 or DHCPv6 leases respectively. + /// + /// @param secs Number of seconds since expiration of leases before + /// they can be removed. Leases which have expired later than this + /// time will not be deleted. + /// @param universe V4 or V6. + /// @param storage Reference to the container where leases are held. + /// Some expired-reclaimed leases will be removed from this container. + /// @param lease_file Reference to a DHCPv4 or DHCPv6 lease file + /// instance where leases should be marked as deleted. + /// + /// @return Number of leases deleted. + /// + /// @tparam IndexType Index type to be used to search for the + /// expired-reclaimed leases, i.e. + /// @c Lease4StorageExpirationIndex or @c Lease6StorageExpirationIndex. + /// @tparam LeaseType Lease type, i.e. @c Lease4 or @c Lease6. + /// @tparam StorageType Type of storage where leases are held, i.e. + /// @c Lease4Storage or @c Lease6Storage. + /// @tparam LeaseFileType Type of the lease file, i.e. DHCPv4 or + /// DHCPv6 lease file type. + template + uint64_t deleteExpiredReclaimedLeases(const uint32_t secs, + const Universe& universe, + StorageType& storage, + LeaseFileType& lease_file) const; + +public: + /// @brief Return backend type /// /// Returns the type of the backend. diff --git a/src/lib/dhcpsrv/memfile_lease_storage.h b/src/lib/dhcpsrv/memfile_lease_storage.h index 11c77bb4c5..097e59df38 100644 --- a/src/lib/dhcpsrv/memfile_lease_storage.h +++ b/src/lib/dhcpsrv/memfile_lease_storage.h @@ -12,8 +12,8 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -#ifndef INMEMORY_LEASE_STORAGE_H -#define INMEMORY_LEASE_STORAGE_H +#ifndef MEMFILE_LEASE_STORAGE_H +#define MEMFILE_LEASE_STORAGE_H #include #include @@ -31,11 +31,40 @@ namespace isc { namespace dhcp { + +/// @brief Tag for indexes by address. +struct AddressIndexTag { }; + +/// @brief Tag for indexes by DUID, IAID, lease type tuple. +struct DuidIaidTypeIndexTag { }; + +/// @brief Tag for indexes by expiration time. +struct ExpirationIndexTag { }; + +/// @brief Tag for indexes by HW address, subnet identifier tuple. +struct HWAddressSubnetIdIndexTag { }; + +/// @brief Tag for indexes by client and subnet identifiers. +struct ClientIdSubnetIdIndexTag { }; + +/// @brief Tag for indexes by client id, HW address and subnet id. +struct ClientIdHWAddressSubnetIdIndexTag { }; + +/// @name Multi index containers holding DHCPv4 and DHCPv6 leases. +/// +//@{ + /// @brief A multi index container holding DHCPv6 leases. /// /// The leases in the container may be accessed using different indexes: /// - using an IPv6 address, /// - using a composite index: DUID, IAID and lease type. +/// - using a composite index: boolean flag indicating if the state is +/// "expired-reclaimed" and expiration time. +/// +/// Indexes can be accessed using the index number (from 0 to 2) or a +/// name tag. It is recommended to use the tags to access indexes as +/// they do not depend on the order of indexes in the container. typedef boost::multi_index_container< // It holds pointers to Lease6 objects. Lease6Ptr, @@ -44,11 +73,13 @@ typedef boost::multi_index_container< // This index sorts leases by IPv6 addresses represented as // IOAddress objects. boost::multi_index::ordered_unique< + boost::multi_index::tag, boost::multi_index::member >, // Specification of the second index starts here. boost::multi_index::ordered_non_unique< + boost::multi_index::tag, // This is a composite index that will be used to search for // the lease using three attributes: DUID, IAID and lease type. boost::multi_index::composite_key< @@ -62,6 +93,24 @@ typedef boost::multi_index_container< boost::multi_index::member, boost::multi_index::member > + >, + + // Specification of the third index starts here. + boost::multi_index::ordered_non_unique< + boost::multi_index::tag, + // This is a composite index that will be used to search for + // the expired leases. Depending on the value of the first component + // of the search key, the reclaimed or not reclaimed leases will can + // be searched. + boost::multi_index::composite_key< + Lease6, + // The boolean value specifying if lease is reclaimed or not. + boost::multi_index::const_mem_fun, + // Lease expiration time. + boost::multi_index::const_mem_fun + > > > > Lease6Storage; // Specify the type name of this container. @@ -73,6 +122,12 @@ typedef boost::multi_index_container< /// - composite index: HW address and subnet id, /// - composite index: client id and subnet id, /// - composite index: HW address, client id and subnet id +/// - using a composite index: boolean flag indicating if the state is +/// "expired-reclaimed" and expiration time. +/// +/// Indexes can be accessed using the index number (from 0 to 4) or a +/// name tag. It is recommended to use the tags to access indexes as +/// they do not depend on the order of indexes in the container. typedef boost::multi_index_container< // It holds pointers to Lease4 objects. Lease4Ptr, @@ -82,6 +137,7 @@ typedef boost::multi_index_container< // This index sorts leases by IPv4 addresses represented as // IOAddress objects. boost::multi_index::ordered_unique< + boost::multi_index::tag, // The IPv4 address are held in addr_ members that belong to // Lease class. boost::multi_index::member @@ -89,6 +145,7 @@ typedef boost::multi_index_container< // Specification of the second index starts here. boost::multi_index::ordered_non_unique< + boost::multi_index::tag, // This is a composite index that combines two attributes of the // Lease4 object: hardware address and subnet id. boost::multi_index::composite_key< @@ -109,6 +166,7 @@ typedef boost::multi_index_container< // Specification of the third index starts here. boost::multi_index::ordered_non_unique< + boost::multi_index::tag, // This is a composite index that uses two values to search for a // lease: client id and subnet id. boost::multi_index::composite_key< @@ -124,6 +182,7 @@ typedef boost::multi_index_container< // Specification of the fourth index starts here. boost::multi_index::ordered_non_unique< + boost::multi_index::tag, // This is a composite index that uses three values to search for a // lease: client id, HW address and subnet id. boost::multi_index::composite_key< @@ -141,11 +200,63 @@ typedef boost::multi_index_container< // The subnet id is accessed through the subnet_id_ member. boost::multi_index::member > + >, + + // Specification of the fifth index starts here. + boost::multi_index::ordered_non_unique< + boost::multi_index::tag, + // This is a composite index that will be used to search for + // the expired leases. Depending on the value of the first component + // of the search key, the reclaimed or not reclaimed leases will can + // be searched. + boost::multi_index::composite_key< + Lease4, + // The boolean value specifying if lease is reclaimed or not. + boost::multi_index::const_mem_fun, + // Lease expiration time. + boost::multi_index::const_mem_fun + > > > > Lease4Storage; // Specify the type name for this container. +//@} + +/// @name Indexes used by the multi index containers +/// +//@{ + +/// @brief DHCPv6 lease storage index by address. +typedef Lease6Storage::index::type Lease6StorageAddressIndex; + +/// @brief DHCPv6 lease storage index by DUID, IAID, lease type. +typedef Lease6Storage::index::type Lease6StorageDuidIaidTypeIndex; + +/// @brief DHCPv6 lease storage index by expiration time. +typedef Lease6Storage::index::type Lease6StorageExpirationIndex; + +/// @brief DHCPv4 lease storage index by address. +typedef Lease4Storage::index::type Lease4StorageAddressIndex; + +/// @brief DHCPv4 lease storage index by exiration time. +typedef Lease4Storage::index::type Lease4StorageExpirationIndex; + +/// @brief DHCPv4 lease storage index by HW address and subnet identifier. +typedef Lease4Storage::index::type +Lease4StorageHWAddressSubnetIdIndex; + +/// @brief DHCPv4 lease storage index by client and subnet identifier. +typedef Lease4Storage::index::type +Lease4StorageClientIdSubnetIdIndex; + +/// @brief DHCPv4 lease storage index by client id, HW address and subnet id. +typedef Lease4Storage::index::type +Lease4StorageClientIdHWAddressSubnetIdIndex; + +//@} } // end of isc::dhcp namespace } // end of isc namespace -#endif // INMEMORY_LEASE_STORAGE_H +#endif // MEMFILE_LEASE_STORAGE_H diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.cc b/src/lib/dhcpsrv/mysql_lease_mgr.cc index ccdb74db06..68f00755b3 100644 --- a/src/lib/dhcpsrv/mysql_lease_mgr.cc +++ b/src/lib/dhcpsrv/mysql_lease_mgr.cc @@ -1932,6 +1932,18 @@ MySqlLeaseMgr::getLeases6(Lease::Type lease_type, return (result); } +void +MySqlLeaseMgr::getExpiredLeases6(Lease6Collection&, const size_t) const { + isc_throw(NotImplemented, "MySqlLeaseMgr::getExpiredLeases6 is currently" + " not implemented"); +} + +void +MySqlLeaseMgr::getExpiredLeases4(Lease4Collection&, const size_t) const { + isc_throw(NotImplemented, "MySqlLeaseMgr::getExpiredLeases4 is currently" + " not implemented"); +} + // Update lease methods. These comprise common code that handles the actual // update, and type-specific methods that set up the parameters for the prepared // statement depending on the type of lease. @@ -2074,6 +2086,19 @@ MySqlLeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) { } } +uint64_t +MySqlLeaseMgr::deleteExpiredReclaimedLeases4(const uint32_t) { + isc_throw(NotImplemented, "MySqlLeaseMgr::deleteExpiredReclaimedLeases4" + " is not implemented"); +} + +uint64_t +MySqlLeaseMgr::deleteExpiredReclaimedLeases6(const uint32_t) { + isc_throw(NotImplemented, "MySqlLeaseMgr::deleteExpiredReclaimedLeases6" + " is not implemented"); +} + + // Miscellaneous database methods. std::string diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.h b/src/lib/dhcpsrv/mysql_lease_mgr.h index 66dbbe1f2c..e23fcc6e53 100644 --- a/src/lib/dhcpsrv/mysql_lease_mgr.h +++ b/src/lib/dhcpsrv/mysql_lease_mgr.h @@ -317,6 +317,33 @@ public: virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid, uint32_t iaid, SubnetID subnet_id) const; + /// @brief Returns a collection of expired DHCPv6 leases. + /// + /// This method returns at most @c max_leases expired leases. The leases + /// returned haven't been reclaimed, i.e. the database query must exclude + /// reclaimed leases from the results returned. + /// + /// @param [out] expired_leases A container to which expired leases returned + /// by the database backend are added. + /// @param max_leases A maximum number of leases to be returned. If this + /// value is set to 0, all expired (but not reclaimed) leases are returned. + virtual void getExpiredLeases6(Lease6Collection& expired_leases, + const size_t max_leases) const; + + + /// @brief Returns a collection of expired DHCPv4 leases. + /// + /// This method returns at most @c max_leases expired leases. The leases + /// returned haven't been reclaimed, i.e. the database query must exclude + /// reclaimed leases from the results returned. + /// + /// @param [out] expired_leases A container to which expired leases returned + /// by the database backend are added. + /// @param max_leases A maximum number of leases to be returned. If this + /// value is set to 0, all expired (but not reclaimed) leases are returned. + virtual void getExpiredLeases4(Lease4Collection& expired_leases, + const size_t max_leases) const; + /// @brief Updates IPv4 lease. /// /// Updates the record of the lease in the database (as identified by the @@ -354,6 +381,24 @@ public: /// failed. virtual bool deleteLease(const isc::asiolink::IOAddress& addr); + /// @brief Deletes all expired-reclaimed DHCPv4 leases. + /// + /// @param secs Number of seconds since expiration of leases before + /// they can be removed. Leases which have expired later than this + /// time will not be deleted. + /// + /// @return Number of leases deleted. + virtual uint64_t deleteExpiredReclaimedLeases4(const uint32_t secs); + + /// @brief Deletes all expired-reclaimed DHCPv6 leases. + /// + /// @param secs Number of seconds since expiration of leases before + /// they can be removed. Leases which have expired later than this + /// time will not be deleted. + /// + /// @return Number of leases deleted. + virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t secs); + /// @brief Return backend type /// /// Returns the type of the backend (e.g. "mysql", "memfile" etc.) diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.cc b/src/lib/dhcpsrv/pgsql_lease_mgr.cc index d991000aa8..4d30b4539b 100644 --- a/src/lib/dhcpsrv/pgsql_lease_mgr.cc +++ b/src/lib/dhcpsrv/pgsql_lease_mgr.cc @@ -1382,6 +1382,19 @@ PgSqlLeaseMgr::getLeases6(Lease::Type lease_type, const DUID& duid, return (result); } +void +PgSqlLeaseMgr::getExpiredLeases6(Lease6Collection&, const size_t) const { + isc_throw(NotImplemented, "PgSqlLeaseMgr::getExpiredLeases6 is currently" + " not implemented"); +} + +void +PgSqlLeaseMgr::getExpiredLeases4(Lease4Collection&, const size_t) const { + isc_throw(NotImplemented, "PgSqlLeaseMgr::getExpiredLeases4 is currently" + " not implemented"); +} + + template void PgSqlLeaseMgr::updateLeaseCommon(StatementIndex stindex, @@ -1494,6 +1507,18 @@ PgSqlLeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) { return (deleteLeaseCommon(DELETE_LEASE6, bind_array)); } +uint64_t +PgSqlLeaseMgr::deleteExpiredReclaimedLeases4(const uint32_t) { + isc_throw(NotImplemented, "PgSqlLeaseMgr::deleteExpiredReclaimedLeases4" + " is not implemented"); +} + +uint64_t +PgSqlLeaseMgr::deleteExpiredReclaimedLeases6(const uint32_t) { + isc_throw(NotImplemented, "PgSqlLeaseMgr::deleteExpiredReclaimedLeases6" + " is not implemented"); +} + string PgSqlLeaseMgr::getName() const { string name = ""; diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.h b/src/lib/dhcpsrv/pgsql_lease_mgr.h index 9df59a515e..afaaf9325e 100644 --- a/src/lib/dhcpsrv/pgsql_lease_mgr.h +++ b/src/lib/dhcpsrv/pgsql_lease_mgr.h @@ -322,6 +322,33 @@ public: virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid, uint32_t iaid, SubnetID subnet_id) const; + /// @brief Returns a collection of expired DHCPv6 leases. + /// + /// This method returns at most @c max_leases expired leases. The leases + /// returned haven't been reclaimed, i.e. the database query must exclude + /// reclaimed leases from the results returned. + /// + /// @param [out] expired_leases A container to which expired leases returned + /// by the database backend are added. + /// @param max_leases A maximum number of leases to be returned. If this + /// value is set to 0, all expired (but not reclaimed) leases are returned. + virtual void getExpiredLeases6(Lease6Collection& expired_leases, + const size_t max_leases) const; + + + /// @brief Returns a collection of expired DHCPv4 leases. + /// + /// This method returns at most @c max_leases expired leases. The leases + /// returned haven't been reclaimed, i.e. the database query must exclude + /// reclaimed leases from the results returned. + /// + /// @param [out] expired_leases A container to which expired leases returned + /// by the database backend are added. + /// @param max_leases A maximum number of leases to be returned. If this + /// value is set to 0, all expired (but not reclaimed) leases are returned. + virtual void getExpiredLeases4(Lease4Collection& expired_leases, + const size_t max_leases) const; + /// @brief Updates IPv4 lease. /// /// Updates the record of the lease in the database (as identified by the @@ -359,6 +386,24 @@ public: /// failed. virtual bool deleteLease(const isc::asiolink::IOAddress& addr); + /// @brief Deletes all expired-reclaimed DHCPv4 leases. + /// + /// @param secs Number of seconds since expiration of leases before + /// they can be removed. Leases which have expired later than this + /// time will not be deleted. + /// + /// @return Number of leases deleted. + virtual uint64_t deleteExpiredReclaimedLeases4(const uint32_t secs); + + /// @brief Deletes all expired-reclaimed DHCPv6 leases. + /// + /// @param secs Number of seconds since expiration of leases before + /// they can be removed. Leases which have expired later than this + /// time will not be deleted. + /// + /// @return Number of leases deleted. + virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t secs); + /// @brief Return backend type /// /// Returns the type of the backend (e.g. "mysql", "memfile" etc.) diff --git a/src/lib/dhcpsrv/tests/csv_lease_file4_unittest.cc b/src/lib/dhcpsrv/tests/csv_lease_file4_unittest.cc index d0d16604b9..da7451dbcc 100644 --- a/src/lib/dhcpsrv/tests/csv_lease_file4_unittest.cc +++ b/src/lib/dhcpsrv/tests/csv_lease_file4_unittest.cc @@ -111,12 +111,12 @@ CSVLeaseFile4Test::absolutePath(const std::string& filename) { void CSVLeaseFile4Test::writeSampleFile() const { io_.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id," - "fqdn_fwd,fqdn_rev,hostname\n" + "fqdn_fwd,fqdn_rev,hostname,state\n" "192.0.2.1,06:07:08:09:0a:bc,,200,200,8,1,1," - "host.example.com\n" - "192.0.2.1,,a:11:01:04,200,200,8,1,1,host.example.com\n" + "host.example.com,0\n" + "192.0.2.1,,a:11:01:04,200,200,8,1,1,host.example.com,1\n" "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,100,100,7," - "0,0,\n"); + "0,0,,1\n"); } // This test checks the capability to read and parse leases from the file. @@ -153,6 +153,7 @@ TEST_F(CSVLeaseFile4Test, parse) { EXPECT_TRUE(lease->fqdn_fwd_); EXPECT_TRUE(lease->fqdn_rev_); EXPECT_EQ("host.example.com", lease->hostname_); + EXPECT_EQ(Lease::STATE_DEFAULT, lease->state_); } // Second lease is malformed - HW address is empty. @@ -182,6 +183,7 @@ TEST_F(CSVLeaseFile4Test, parse) { EXPECT_FALSE(lease->fqdn_fwd_); EXPECT_FALSE(lease->fqdn_rev_); EXPECT_TRUE(lease->hostname_.empty()); + EXPECT_EQ(Lease::STATE_DECLINED, lease->state_); } // There are no more leases. Reading should cause no error, but the returned @@ -217,6 +219,7 @@ TEST_F(CSVLeaseFile4Test, recreate) { NULL, 0, 200, 50, 80, 0, 8, true, true, "host.example.com")); + lease->state_ = Lease::STATE_EXPIRED_RECLAIMED; { SCOPED_TRACE("First write"); ASSERT_NO_THROW(lf->append(*lease)); @@ -238,10 +241,10 @@ TEST_F(CSVLeaseFile4Test, recreate) { lf->close(); // Check that the contents of the csv file are correct. EXPECT_EQ("address,hwaddr,client_id,valid_lifetime,expire,subnet_id," - "fqdn_fwd,fqdn_rev,hostname\n" - "192.0.3.2,00:01:02:03:04:05,,200,200,8,1,1,host.example.com\n" + "fqdn_fwd,fqdn_rev,hostname,state\n" + "192.0.3.2,00:01:02:03:04:05,,200,200,8,1,1,host.example.com,2\n" "192.0.3.10,0d:0e:0a:0d:0b:0e:0e:0f,01:02:03:04,100,100,7,0," - "0,\n", + "0,,0\n", io_.readFile()); } diff --git a/src/lib/dhcpsrv/tests/csv_lease_file6_unittest.cc b/src/lib/dhcpsrv/tests/csv_lease_file6_unittest.cc index 7735f1a15f..e85276adb6 100644 --- a/src/lib/dhcpsrv/tests/csv_lease_file6_unittest.cc +++ b/src/lib/dhcpsrv/tests/csv_lease_file6_unittest.cc @@ -110,14 +110,14 @@ void CSVLeaseFile6Test::writeSampleFile() const { io_.writeFile("address,duid,valid_lifetime,expire,subnet_id," "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd," - "fqdn_rev,hostname,hwaddr\n" + "fqdn_rev,hostname,hwaddr,state\n" "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f," - "200,200,8,100,0,7,0,1,1,host.example.com\n" - "2001:db8:1::1,,200,200,8,100,0,7,0,1,1,host.example.com\n" + "200,200,8,100,0,7,0,1,1,host.example.com,,1\n" + "2001:db8:1::1,,200,200,8,100,0,7,0,1,1,host.example.com,,1\n" "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,300,300,6,150," - "0,8,0,0,0,\n" + "0,8,0,0,0,,,1\n" "3000:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,0,200,8,0,2," - "16,64,0,0,,\n"); + "16,64,0,0,,,1\n"); } // This test checks the capability to read and parse leases from the file. @@ -277,13 +277,14 @@ TEST_F(CSVLeaseFile6Test, recreate) { } EXPECT_EQ("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime," - "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr\n" + "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr," + "state\n" "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f," - "200,200,8,100,0,7,0,1,1,host.example.com,\n" + "200,200,8,100,0,7,0,1,1,host.example.com,,0\n" "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05" - ",300,300,6,150,0,8,128,0,0,,\n" + ",300,300,6,150,0,8,128,0,0,,,0\n" "3000:1:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f," - "300,300,10,150,2,7,64,0,0,,\n", + "300,300,10,150,2,7,64,0,0,,,0\n", io_.readFile()); } diff --git a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc index 76b8a08430..43d2b7c98b 100644 --- a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc @@ -79,11 +79,8 @@ GenericLeaseMgrTest::initializeLease4(std::string address) { lease->addr_ = IOAddress(address); // Initialize unused fields. - lease->ext_ = 0; // Not saved lease->t1_ = 0; // Not saved lease->t2_ = 0; // Not saved - lease->fixed_ = false; // Unused - lease->comments_ = std::string(""); // Unused // Set other parameters. For historical reasons, address 0 is not used. if (address == straddress4_[0]) { @@ -199,8 +196,6 @@ GenericLeaseMgrTest::initializeLease6(std::string address) { // Initialize unused fields. lease->t1_ = 0; // Not saved lease->t2_ = 0; // Not saved - lease->fixed_ = false; // Unused - lease->comments_ = std::string(""); // Unused // Set other parameters. For historical reasons, address 0 is not used. if (address == straddress6_[0]) { @@ -1304,8 +1299,6 @@ GenericLeaseMgrTest::testLease6LeaseTypeCheck() { // Initialize unused fields. empty_lease->t1_ = 0; // Not saved empty_lease->t2_ = 0; // Not saved - empty_lease->fixed_ = false; // Unused - empty_lease->comments_ = std::string(""); // Unused empty_lease->iaid_ = 142; empty_lease->duid_ = DuidPtr(new DUID(*duid)); empty_lease->subnet_id_ = 23; @@ -1657,6 +1650,439 @@ GenericLeaseMgrTest::testVersion(int major, int minor) { EXPECT_EQ(minor, lmptr_->getVersion().second); } +void +GenericLeaseMgrTest::testGetExpiredLeases4() { + // Get the leases to be used for the test. + vector leases = createLeases4(); + // Make sure we have at least 6 leases there. + ASSERT_GE(leases.size(), 6); + + // Use the same current time for all leases. + time_t current_time = time(NULL); + + // Add them to the database + for (size_t i = 0; i < leases.size(); ++i) { + // Mark every other lease as expired. + if (i % 2 == 0) { + // Set client last transmission time to the value older than the + // valid lifetime to make it expired. The expiration time also + // depends on the lease index, so as we can later check that the + // leases are ordered by the expiration time. + leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 10 - i; + + } else { + // Set current time as cltt for remaining leases. These leases are + // not expired. + leases[i]->cltt_ = current_time; + } + ASSERT_TRUE(lmptr_->addLease(leases[i])); + } + + // Retrieve at most 1000 expired leases. + Lease4Collection expired_leases; + ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 1000)); + // Leases with even indexes should be returned as expired. + ASSERT_EQ(static_cast(leases.size() / 2), expired_leases.size()); + + // The expired leases should be returned from the most to least expired. + // This matches the reverse order to which they have been added. + for (Lease4Collection::reverse_iterator lease = expired_leases.rbegin(); + lease != expired_leases.rend(); ++lease) { + int index = static_cast(std::distance(expired_leases.rbegin(), lease)); + // Multiple current index by two, because only leases with even indexes + // should have been returned. + EXPECT_EQ(leases[2 * index]->addr_, (*lease)->addr_); + } + + // Update current time for the next test. + current_time = time(NULL); + // Also, remove expired leases collected during the previous test. + expired_leases.clear(); + + // This time let's reverse the expiration time and see if they will be returned + // in the correct order. + for (int i = 0; i < leases.size(); ++i) { + // Update the time of expired leases with even indexes. + if (i % 2 == 0) { + leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 1000 + i; + + } else { + // Make sure remaining leases remain unexpired. + leases[i]->cltt_ = current_time + 100; + } + ASSERT_NO_THROW(lmptr_->updateLease4(leases[i])); + } + + // Retrieve expired leases again. The limit of 0 means return all expired + // leases. + ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 0)); + // The same leases should be returned. + ASSERT_EQ(static_cast(leases.size() / 2), expired_leases.size()); + + // This time leases should be returned in the non-reverse order. + for (Lease4Collection::iterator lease = expired_leases.begin(); + lease != expired_leases.end(); ++lease) { + int index = static_cast(std::distance(expired_leases.begin(), lease)); + EXPECT_EQ(leases[2 * index]->addr_, (*lease)->addr_); + } + + // Remember expired leases returned. + std::vector saved_expired_leases = expired_leases; + + // Remove expired leases again. + expired_leases.clear(); + + // Limit the number of leases to be returned to 2. + ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 2)); + + // Make sure we have exactly 2 leases returned. + ASSERT_EQ(2, expired_leases.size()); + + // Test that most expired leases have been returned. + for (Lease4Collection::iterator lease = expired_leases.begin(); + lease != expired_leases.end(); ++lease) { + int index = static_cast(std::distance(expired_leases.begin(), lease)); + EXPECT_EQ(leases[2 * index]->addr_, (*lease)->addr_); + } + + // Mark every other expired lease as reclaimed. + for (int i = 0; i < saved_expired_leases.size(); ++i) { + if (i % 2 != 0) { + saved_expired_leases[i]->state_ = Lease::STATE_EXPIRED_RECLAIMED; + } + ASSERT_NO_THROW(lmptr_->updateLease4(saved_expired_leases[i])); + } + + expired_leases.clear(); + + // This the returned leases should exclude reclaimed ones. So the number + // of returned leases should be roughly half of the expired leases. + ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 0)); + ASSERT_EQ(static_cast(saved_expired_leases.size() / 2), + expired_leases.size()); + + // Make sure that returned leases are those that are not reclaimed, i.e. + // those that have even index. + for (Lease4Collection::iterator lease = expired_leases.begin(); + lease != expired_leases.end(); ++lease) { + int index = static_cast(std::distance(expired_leases.begin(), lease)); + EXPECT_EQ(saved_expired_leases[2 * index]->addr_, (*lease)->addr_); + } +} + +void +GenericLeaseMgrTest::testGetExpiredLeases6() { + // Get the leases to be used for the test. + vector leases = createLeases6(); + // Make sure we have at least 6 leases there. + ASSERT_GE(leases.size(), 6); + + // Use the same current time for all leases. + time_t current_time = time(NULL); + + // Add them to the database + for (size_t i = 0; i < leases.size(); ++i) { + // Mark every other lease as expired. + if (i % 2 == 0) { + // Set client last transmission time to the value older than the + // valid lifetime to make it expired. The expiration time also + // depends on the lease index, so as we can later check that the + // leases are ordered by the expiration time. + leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 10 - i; + + } else { + // Set current time as cltt for remaining leases. These leases are + // not expired. + leases[i]->cltt_ = current_time; + } + ASSERT_TRUE(lmptr_->addLease(leases[i])); + } + + // Retrieve at most 1000 expired leases. + Lease6Collection expired_leases; + ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 1000)); + // Leases with even indexes should be returned as expired. + ASSERT_EQ(static_cast(leases.size() / 2), expired_leases.size()); + + // The expired leases should be returned from the most to least expired. + // This matches the reverse order to which they have been added. + for (Lease6Collection::reverse_iterator lease = expired_leases.rbegin(); + lease != expired_leases.rend(); ++lease) { + int index = static_cast(std::distance(expired_leases.rbegin(), lease)); + // Multiple current index by two, because only leases with even indexes + // should have been returned. + EXPECT_EQ(leases[2 * index]->addr_, (*lease)->addr_); + } + + // Update current time for the next test. + current_time = time(NULL); + // Also, remove expired leases collected during the previous test. + expired_leases.clear(); + + // This time let's reverse the expiration time and see if they will be returned + // in the correct order. + for (int i = 0; i < leases.size(); ++i) { + // Update the time of expired leases with even indexes. + if (i % 2 == 0) { + leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 1000 + i; + + } else { + // Make sure remaining leases remain unexpired. + leases[i]->cltt_ = current_time + 100; + } + ASSERT_NO_THROW(lmptr_->updateLease6(leases[i])); + } + + // Retrieve expired leases again. The limit of 0 means return all expired + // leases. + ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 0)); + // The same leases should be returned. + ASSERT_EQ(static_cast(leases.size() / 2), expired_leases.size()); + + // This time leases should be returned in the non-reverse order. + for (Lease6Collection::iterator lease = expired_leases.begin(); + lease != expired_leases.end(); ++lease) { + int index = static_cast(std::distance(expired_leases.begin(), lease)); + EXPECT_EQ(leases[2 * index]->addr_, (*lease)->addr_); + } + + // Remember expired leases returned. + std::vector saved_expired_leases = expired_leases; + + // Remove expired leases again. + expired_leases.clear(); + + // Limit the number of leases to be returned to 2. + ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 2)); + + // Make sure we have exactly 2 leases returned. + ASSERT_EQ(2, expired_leases.size()); + + // Test that most expired leases have been returned. + for (Lease6Collection::iterator lease = expired_leases.begin(); + lease != expired_leases.end(); ++lease) { + int index = static_cast(std::distance(expired_leases.begin(), lease)); + EXPECT_EQ(leases[2 * index]->addr_, (*lease)->addr_); + } + + // Mark every other expired lease as reclaimed. + for (int i = 0; i < saved_expired_leases.size(); ++i) { + if (i % 2 != 0) { + saved_expired_leases[i]->state_ = Lease::STATE_EXPIRED_RECLAIMED; + } + ASSERT_NO_THROW(lmptr_->updateLease6(saved_expired_leases[i])); + } + + expired_leases.clear(); + + // This the returned leases should exclude reclaimed ones. So the number + // of returned leases should be roughly half of the expired leases. + ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 0)); + ASSERT_EQ(static_cast(saved_expired_leases.size() / 2), + expired_leases.size()); + + // Make sure that returned leases are those that are not reclaimed, i.e. + // those that have even index. + for (Lease6Collection::iterator lease = expired_leases.begin(); + lease != expired_leases.end(); ++lease) { + int index = static_cast(std::distance(expired_leases.begin(), lease)); + EXPECT_EQ(saved_expired_leases[2 * index]->addr_, (*lease)->addr_); + } +} + +void +GenericLeaseMgrTest::testDeleteExpiredReclaimedLeases4() { + // Get the leases to be used for the test. + vector leases = createLeases4(); + // Make sure we have at least 6 leases there. + ASSERT_GE(leases.size(), 6); + + time_t current_time = time(NULL); + + // Add them to the database + for (size_t i = 0; i < leases.size(); ++i) { + // Mark every other lease as expired. + if (i % 2 == 0) { + // Set client last transmission time to the value older than the + // valid lifetime to make it expired. We also substract the value + // of 10, 20, 30, 40 etc, depending on the lease index. This + // simulates different expiration times for various leases. + leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - i * 10; + // Set reclaimed state. + leases[i]->state_ = Lease::STATE_EXPIRED_RECLAIMED; + + } else { + // Other leases are left as not expired - client last transmission + // time set to current time. + leases[i]->cltt_ = current_time; + } + ASSERT_TRUE(lmptr_->addLease(leases[i])); + } + + // Keep reclaimed lease for 15 seconds after expiration. + const uint32_t lease_affinity_time = 15; + + // Delete expired and reclaimed leases which have expired earlier than + // 15 seconds ago. This should affect leases with index 2, 3, 4 etc. + uint64_t deleted_num; + uint64_t should_delete_num = 0; + ASSERT_NO_THROW( + deleted_num = lmptr_->deleteExpiredReclaimedLeases4(lease_affinity_time) + ); + + for (size_t i = 0; i < leases.size(); ++i) { + // Obtain lease from the server. + Lease4Ptr lease = lmptr_->getLease4(leases[i]->addr_); + + // If the lease is reclaimed and the expiration time passed more than + // 15 seconds ago, the lease should have been deleted. + if (leases[i]->stateExpiredReclaimed() && + ((leases[i]->getExpirationTime() + lease_affinity_time) < current_time)) { + EXPECT_FALSE(lease) << "The following lease should have been" + " deleted: " << leases[i]->toText(); + ++should_delete_num; + + } else { + // If the lease is not reclaimed or it has expired less than + // 15 seconds ago, the lease should still be there. + EXPECT_TRUE(lease) << "The following lease shouldn't have been" + " deleted: " << leases[i]->toText(); + } + } + // Check that the number of leases deleted is correct. + EXPECT_EQ(deleted_num, should_delete_num); + + // Make sure we can make another attempt, when there are no more leases + // to be deleted. + ASSERT_NO_THROW( + deleted_num = lmptr_->deleteExpiredReclaimedLeases4(lease_affinity_time) + ); + // No lease should have been deleted. + EXPECT_EQ(0, deleted_num); + + // Reopen the database. This to ensure that the leases have been deleted + // from the persistent storage. + reopen(V4); + + for (size_t i = 0; i < leases.size(); ++i) { + /// @todo Leases with empty HW address are dropped by the memfile + /// backend. We will have to reevaluate if this is right behavior + /// of the backend when client identifier is present. + if (leases[i]->hwaddr_ && leases[i]->hwaddr_->hwaddr_.empty()) { + continue; + } + // Obtain lease from the server. + Lease4Ptr lease = lmptr_->getLease4(leases[i]->addr_); + + // If the lease is reclaimed and the expiration time passed more than + // 15 seconds ago, the lease should have been deleted. + if (leases[i]->stateExpiredReclaimed() && + ((leases[i]->getExpirationTime() + lease_affinity_time) < current_time)) { + EXPECT_FALSE(lease) << "The following lease should have been" + " deleted: " << leases[i]->toText(); + + } else { + // If the lease is not reclaimed or it has expired less than + // 15 seconds ago, the lease should still be there. + EXPECT_TRUE(lease) << "The following lease shouldn't have been" + " deleted: " << leases[i]->toText(); + } + } +} + +void +GenericLeaseMgrTest::testDeleteExpiredReclaimedLeases6() { + // Get the leases to be used for the test. + vector leases = createLeases6(); + // Make sure we have at least 6 leases there. + ASSERT_GE(leases.size(), 6); + + time_t current_time = time(NULL); + + // Add them to the database + for (size_t i = 0; i < leases.size(); ++i) { + // Mark every other lease as expired. + if (i % 2 == 0) { + // Set client last transmission time to the value older than the + // valid lifetime to make it expired. We also substract the value + // of 10, 20, 30, 40 etc, depending on the lease index. This + // simulates different expiration times for various leases. + leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - i * 10; + // Set reclaimed state. + leases[i]->state_ = Lease::STATE_EXPIRED_RECLAIMED; + + } else { + // Other leases are left as not expired - client last transmission + // time set to current time. + leases[i]->cltt_ = current_time; + } + ASSERT_TRUE(lmptr_->addLease(leases[i])); + } + + // Keep reclaimed lease for 15 seconds after expiration. + const uint32_t lease_affinity_time = 15; + + // Delete expired and reclaimed leases which have expired earlier than + // 15 seconds ago. This should affect leases with index 2, 3, 4 etc. + uint64_t deleted_num; + uint64_t should_delete_num = 0; + ASSERT_NO_THROW( + deleted_num = lmptr_->deleteExpiredReclaimedLeases6(lease_affinity_time) + ); + + for (size_t i = 0; i < leases.size(); ++i) { + // Obtain lease from the server. + Lease6Ptr lease = lmptr_->getLease6(leases[i]->type_, leases[i]->addr_); + + // If the lease is reclaimed and the expiration time passed more than + // 15 seconds ago, the lease should have been deleted. + if (leases[i]->stateExpiredReclaimed() && + ((leases[i]->getExpirationTime() + lease_affinity_time) < current_time)) { + EXPECT_FALSE(lease) << "The following lease should have been" + " deleted: " << leases[i]->toText(); + ++should_delete_num; + + } else { + // If the lease is not reclaimed or it has expired less than + // 15 seconds ago, the lease should still be there. + EXPECT_TRUE(lease) << "The following lease shouldn't have been" + " deleted: " << leases[i]->toText(); + } + } + // Check that the number of deleted leases is correct. + EXPECT_EQ(should_delete_num, deleted_num); + + // Make sure we can make another attempt, when there are no more leases + // to be deleted. + ASSERT_NO_THROW( + deleted_num = lmptr_->deleteExpiredReclaimedLeases6(lease_affinity_time) + ); + // No lease should have been deleted. + EXPECT_EQ(0, deleted_num); + + // Reopen the database. This to ensure that the leases have been deleted + // from the persistent storage. + reopen(V6); + + for (size_t i = 0; i < leases.size(); ++i) { + // Obtain lease from the server. + Lease6Ptr lease = lmptr_->getLease6(leases[i]->type_, leases[i]->addr_); + + // If the lease is reclaimed and the expiration time passed more than + // 15 seconds ago, the lease should have been deleted. + if (leases[i]->stateExpiredReclaimed() && + ((leases[i]->getExpirationTime() + lease_affinity_time) < current_time)) { + EXPECT_FALSE(lease) << "The following lease should have been" + " deleted: " << leases[i]->toText(); + + } else { + // If the lease is not reclaimed or it has expired less than + // 15 seconds ago, the lease should still be there. + EXPECT_TRUE(lease) << "The following lease shouldn't have been" + " deleted: " << leases[i]->toText(); + } + } +} }; // namespace test }; // namespace dhcp diff --git a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h index 7d88fa4c07..320c347b72 100644 --- a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h +++ b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h @@ -1,4 +1,4 @@ -// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -265,6 +265,42 @@ public: /// @param minor Expected minor version to be reported. void testVersion(int major, int minor); + /// @brief Checks that the expired DHCPv4 leases can be retrieved. + /// + /// This test checks the following: + /// - all expired and not reclaimed leases are retured + /// - number of leases returned can be limited + /// - leases are returned in the order from the most expired to the + /// least expired + /// - reclaimed leases are not returned. + void testGetExpiredLeases4(); + + /// @brief Checks that the expired DHCPv6 leases can be retrieved. + /// + /// This test checks the following: + /// - all expired and not reclaimed leases are retured + /// - number of leases returned can be limited + /// - leases are returned in the order from the most expired to the + /// least expired + /// - reclaimed leases are not returned. + void testGetExpiredLeases6(); + + /// @brief Checks that selected expired-reclaimed DHCPv6 leases + /// are removed. + /// + /// This creates a number of DHCPv6 leases and marks some of them + /// as expired-reclaimed. It later verifies that the expired-reclaimed + /// leases can be removed. + void testDeleteExpiredReclaimedLeases6(); + + /// @brief Checks that selected expired-reclaimed DHCPv4 leases + /// are removed. + /// + /// This creates a number of DHCPv4 leases and marks some of them + /// as expired-reclaimed. It later verifies that the expired-reclaimed + /// leases can be removed. + void testDeleteExpiredReclaimedLeases4(); + /// @brief String forms of IPv4 addresses std::vector straddress4_; diff --git a/src/lib/dhcpsrv/tests/lease_file_loader_unittest.cc b/src/lib/dhcpsrv/tests/lease_file_loader_unittest.cc index 76c3b1dea2..e51822a5c5 100644 --- a/src/lib/dhcpsrv/tests/lease_file_loader_unittest.cc +++ b/src/lib/dhcpsrv/tests/lease_file_loader_unittest.cc @@ -151,11 +151,11 @@ protected: /// @brief Sets up the header strings virtual void SetUp() { v4_hdr_ = "address,hwaddr,client_id,valid_lifetime,expire,subnet_id," - "fqdn_fwd,fqdn_rev,hostname\n"; + "fqdn_fwd,fqdn_rev,hostname,state\n"; v6_hdr_ = "address,duid,valid_lifetime,expire,subnet_id," "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd," - "fqdn_rev,hostname,hwaddr\n"; + "fqdn_rev,hostname,hwaddr,state\n"; } }; @@ -179,17 +179,17 @@ LeaseFileLoaderTest::absolutePath(const std::string& filename) { TEST_F(LeaseFileLoaderTest, loadWrite4) { std::string test_str; std::string a_1 = "192.0.2.1,06:07:08:09:0a:bc,," - "200,200,8,1,1,host.example.com\n"; + "200,200,8,1,1,host.example.com,1\n"; std::string a_2 = "192.0.2.1,06:07:08:09:0a:bc,," - "200,500,8,1,1,host.example.com\n"; + "200,500,8,1,1,host.example.com,1\n"; std::string b_1 = "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04," - "100,100,7,0,0,\n"; + "100,100,7,0,0,,1\n"; std::string b_2 = "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04," - "100,135,7,0,0,\n"; + "100,135,7,0,0,,1\n"; std::string c_1 = "192.0.2.3,,a:11:01:04," - "200,200,8,1,1,host.example.com\n"; + "200,200,8,1,1,host.example.com,1\n"; // Create lease file with leases for 192.0.2.1, 192.0.3.15. The lease // entry for the 192.0.2.3 is invalid (lacks HW address) and should @@ -250,14 +250,14 @@ TEST_F(LeaseFileLoaderTest, loadWrite4) { TEST_F(LeaseFileLoaderTest, loadWrite4LeaseRemove) { std::string test_str; std::string a_1 = "192.0.2.1,06:07:08:09:0a:bc,," - "200,200,8,1,1,host.example.com\n"; + "200,200,8,1,1,host.example.com,1\n"; std::string a_2 = "192.0.2.1,06:07:08:09:0a:bc,," - "0,500,8,1,1,host.example.com\n"; + "0,500,8,1,1,host.example.com,1\n"; std::string b_1 = "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04," - "100,100,7,0,0,\n"; + "100,100,7,0,0,,1\n"; std::string b_2 = "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04," - "100,135,7,0,0,\n"; + "100,135,7,0,0,,1\n"; // Create lease file in which one of the entries for 192.0.2.1 @@ -305,19 +305,19 @@ TEST_F(LeaseFileLoaderTest, loadWrite4LeaseRemove) { TEST_F(LeaseFileLoaderTest, loadWrite6) { std::string test_str; std::string a_1 = "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f," - "200,200,8,100,0,7,0,1,1,host.example.com,\n"; + "200,200,8,100,0,7,0,1,1,host.example.com,,1\n"; std::string a_2 = "2001:db8:1::1,," - "200,200,8,100,0,7,0,1,1,host.example.com,\n"; + "200,200,8,100,0,7,0,1,1,host.example.com,,1\n"; std::string a_3 = "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f," - "200,400,8,100,0,7,0,1,1,host.example.com,\n"; + "200,400,8,100,0,7,0,1,1,host.example.com,,1\n"; std::string b_1 = "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05," - "300,300,6,150,0,8,0,0,0,,\n"; + "300,300,6,150,0,8,0,0,0,,,1\n"; std::string b_2 = "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05," - "300,800,6,150,0,8,0,0,0,,\n"; + "300,800,6,150,0,8,0,0,0,,,1\n"; std::string c_1 = "3000:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f," - "100,200,8,0,2,16,64,0,0,,\n"; + "100,200,8,0,2,16,64,0,0,,,1\n"; @@ -379,14 +379,14 @@ TEST_F(LeaseFileLoaderTest, loadWrite6) { TEST_F(LeaseFileLoaderTest, loadWrite6LeaseRemove) { std::string test_str; std::string a_1 = "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f," - "200,200,8,100,0,7,0,1,1,host.example.com,\n"; + "200,200,8,100,0,7,0,1,1,host.example.com,,1\n"; std::string a_2 = "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f," - "0,400,8,100,0,7,0,1,1,host.example.com,\n"; + "0,400,8,100,0,7,0,1,1,host.example.com,,1\n"; std::string b_1 = "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05," - "300,300,6,150,0,8,0,0,0,,\n"; + "300,300,6,150,0,8,0,0,0,,,1\n"; std::string b_2 = "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05," - "300,800,6,150,0,8,0,0,0,,\n"; + "300,800,6,150,0,8,0,0,0,,,1\n"; // Create lease file in which one of the entries for the 2001:db8:1::1 // has valid lifetime set to 0, in which case the lease should be @@ -431,13 +431,13 @@ TEST_F(LeaseFileLoaderTest, loadWrite6LeaseRemove) { TEST_F(LeaseFileLoaderTest, loadMaxErrors) { std::string test_str; std::string a_1 = "192.0.2.1,06:07:08:09:0a:bc,," - "200,200,8,1,1,host.example.com\n"; + "200,200,8,1,1,host.example.com,1\n"; std::string a_2 = "192.0.2.1,06:07:08:09:0a:bc,," - "200,500,8,1,1,host.example.com\n"; + "200,500,8,1,1,host.example.com,1\n"; - std::string b_1 = "192.0.2.3,,a:11:01:04,200,200,8,1,1,host.example.com\n"; + std::string b_1 = "192.0.2.3,,a:11:01:04,200,200,8,1,1,host.example.com,1\n"; - std::string c_1 = "192.0.2.10,01:02:03:04:05:06,,200,300,8,1,1,\n"; + std::string c_1 = "192.0.2.10,01:02:03:04:05:06,,200,300,8,1,1,,1\n"; // Create a lease file for which there is a number of invalid // entries. b_1 is invalid and gets used multiple times. @@ -501,8 +501,8 @@ TEST_F(LeaseFileLoaderTest, loadMaxErrors) { // and comparing that with the expected value. TEST_F(LeaseFileLoaderTest, loadWriteLeaseWithZeroLifetime) { std::string test_str; - std::string a_1 = "192.0.2.1,06:07:08:09:0a:bc,,200,200,8,1,1,\n"; - std::string b_2 = "192.0.2.3,06:07:08:09:0a:bd,,0,200,8,1,1,\n"; + std::string a_1 = "192.0.2.1,06:07:08:09:0a:bc,,200,200,8,1,1,,1\n"; + std::string b_2 = "192.0.2.3,06:07:08:09:0a:bd,,0,200,8,1,1,,1\n"; // Create lease file. The second lease has a valid lifetime of 0. test_str = v4_hdr_ + a_1 + b_2; diff --git a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc index 55b41140bd..6d6777c7ac 100644 --- a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -173,6 +173,23 @@ public: return (leases6_); } + + /// @brief Returns expired DHCPv6 leases. + /// + /// This method is not implemented. + virtual void getExpiredLeases6(Lease6Collection&, const size_t) const { + isc_throw(NotImplemented, "ConcreteLeaseMgr::getExpiredLeases6 is not" + " implemented"); + } + + /// @brief Returns expired DHCPv4 leases. + /// + /// This method is not implemented. + virtual void getExpiredLeases4(Lease4Collection&, const size_t) const { + isc_throw(NotImplemented, "ConcreteLeaseMgr::getExpiredLeases4 is not" + " implemented"); + } + /// @brief Updates IPv4 lease. /// /// @param lease4 The lease to be updated. @@ -197,6 +214,26 @@ public: return (false); } + /// @brief Deletes all expired and reclaimed DHCPv4 leases. + /// + /// @param secs Number of seconds since expiration of leases before + /// they can be removed. Leases which have expired later than this + /// time will not be deleted. + virtual uint64_t deleteExpiredReclaimedLeases4(const uint32_t) { + isc_throw(NotImplemented, "ConcreteLeaseMgr::deleteExpiredReclaimedLeases4" + " is not implemented"); + } + + /// @brief Deletes all expired and reclaimed DHCPv6 leases. + /// + /// @param secs Number of seconds since expiration of leases before + /// they can be removed. Leases which have expired later than this + /// time will not be deleted. + virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t) { + isc_throw(NotImplemented, "ConcreteLeaseMgr::deleteExpiredReclaimedLeases6" + " is not implemented"); + } + /// @brief Returns backend type. /// /// Returns the type of the backend (e.g. "mysql", "memfile" etc.) diff --git a/src/lib/dhcpsrv/tests/lease_unittest.cc b/src/lib/dhcpsrv/tests/lease_unittest.cc index 9292801fd9..6b95a616ad 100644 --- a/src/lib/dhcpsrv/tests/lease_unittest.cc +++ b/src/lib/dhcpsrv/tests/lease_unittest.cc @@ -96,7 +96,6 @@ TEST_F(Lease4Test, constructor) { "hostname.example.com."); EXPECT_EQ(ADDRESS[i], static_cast(lease.addr_)); - EXPECT_EQ(0, lease.ext_); EXPECT_TRUE(util::equalValues(hwaddr_, lease.hwaddr_)); EXPECT_TRUE(util::equalValues(clientid_, lease.client_id_)); EXPECT_EQ(0, lease.t1_); @@ -104,11 +103,10 @@ TEST_F(Lease4Test, constructor) { EXPECT_EQ(VALID_LIFETIME, lease.valid_lft_); EXPECT_EQ(current_time, lease.cltt_); EXPECT_EQ(SUBNET_ID, lease.subnet_id_); - EXPECT_FALSE(lease.fixed_); EXPECT_EQ("hostname.example.com.", lease.hostname_); EXPECT_TRUE(lease.fqdn_fwd_); EXPECT_TRUE(lease.fqdn_rev_); - EXPECT_TRUE(lease.comments_.empty()); + EXPECT_EQ(Lease::STATE_DEFAULT, lease.state_); } } @@ -126,6 +124,10 @@ TEST_F(Lease4Test, copyConstructor) { Lease4 lease(0xffffffff, hwaddr_, clientid_, VALID_LIFETIME, 0, 0, current_time, SUBNET_ID); + // Declined is a non-default state. We'll see if the state will be copied + // or the default state will be set for the copied lease. + lease.state_ = Lease::STATE_DECLINED; + // Use copy constructor to copy the lease. Lease4 copied_lease(lease); @@ -163,8 +165,14 @@ TEST_F(Lease4Test, operatorAssign) { Lease4 lease(0xffffffff, hwaddr_, clientid_, VALID_LIFETIME, 0, 0, current_time, SUBNET_ID); - // Use assignment operator to assign the lease. - Lease4 copied_lease = lease; + // Declined is a non-default state. We'll see if the state will be copied + // or the default state will be set for the copied lease. + lease.state_ = Lease::STATE_DECLINED; + + // Create a default lease. + Lease4 copied_lease; + // Use assignment operator to assign new lease. + copied_lease = lease; // Both leases should be now equal. When doing this check we assume that // the equality operator works correctly. @@ -284,13 +292,6 @@ TEST_F(Lease4Test, operatorEquals) { EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the EXPECT_FALSE(lease1 != lease2); // ... leases equal - ++lease1.ext_; - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.ext_ = lease2.ext_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - ++lease1.hwaddr_->hwaddr_[0]; EXPECT_FALSE(lease1 == lease2); EXPECT_TRUE(lease1 != lease2); @@ -343,13 +344,6 @@ TEST_F(Lease4Test, operatorEquals) { EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the EXPECT_FALSE(lease1 != lease2); // ... leases equal - lease1.fixed_ = !lease1.fixed_; - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.fixed_ = lease2.fixed_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - lease1.hostname_ += std::string("Something random"); EXPECT_FALSE(lease1 == lease2); EXPECT_TRUE(lease1 != lease2); @@ -371,10 +365,10 @@ TEST_F(Lease4Test, operatorEquals) { EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the EXPECT_FALSE(lease1 != lease2); // ... leases equal - lease1.comments_ += std::string("Something random"); + lease1.state_ += 1; EXPECT_FALSE(lease1 == lease2); EXPECT_TRUE(lease1 != lease2); - lease1.comments_ = lease2.comments_; + lease2.state_ += 1; EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the EXPECT_FALSE(lease1 != lease2); // ... leases equal } @@ -430,7 +424,8 @@ TEST_F(Lease4Test, toText) { << "Cltt: 12345678\n" << "Hardware addr: " << hwaddr_->toText(false) << "\n" << "Client id: " << clientid_->toText() << "\n" - << "Subnet ID: 789\n"; + << "Subnet ID: 789\n" + << "State: default\n"; EXPECT_EQ(expected.str(), lease.toText()); @@ -445,10 +440,18 @@ TEST_F(Lease4Test, toText) { << "Cltt: 12345678\n" << "Hardware addr: (none)\n" << "Client id: (none)\n" - << "Subnet ID: 789\n"; + << "Subnet ID: 789\n" + << "State: default\n"; EXPECT_EQ(expected.str(), lease.toText()); } +// Verify that the lease states are correctly returned in the textual format. +TEST_F(Lease4Test, stateToText) { + EXPECT_EQ("default", Lease4::statesToText(Lease::STATE_DEFAULT)); + EXPECT_EQ("declined", Lease4::statesToText(Lease::STATE_DECLINED)); + EXPECT_EQ("expired-reclaimed", Lease4::statesToText(Lease::STATE_EXPIRED_RECLAIMED)); +} + /// @brief Creates an instance of the lease with certain FQDN data. /// /// @param hostname Hostname. @@ -467,7 +470,7 @@ Lease6 createLease6(const std::string& hostname, const bool fqdn_fwd, // Lease6 is also defined in lease_mgr.h, so is tested in this file as well. // This test checks if the Lease6 structure can be instantiated correctly -TEST(Lease6, Lease6ConstructorDefault) { +TEST(Lease6Test, Lease6ConstructorDefault) { // check a variety of addresses with different bits set. const char* ADDRESS[] = { @@ -514,7 +517,7 @@ TEST(Lease6, Lease6ConstructorDefault) { // This test verifies that the Lease6 constructor which accepts FQDN data, // sets the data correctly for the lease. -TEST(Lease6, Lease6ConstructorWithFQDN) { +TEST(Lease6Test, Lease6ConstructorWithFQDN) { // check a variety of addresses with different bits set. const char* ADDRESS[] = { @@ -563,7 +566,7 @@ TEST(Lease6, Lease6ConstructorWithFQDN) { /// Checks that the operator==() correctly compares two leases for equality. /// As operator!=() is also defined for this class, every check on operator==() /// is followed by the reverse check on operator!=(). -TEST(Lease6, OperatorEquals) { +TEST(Lease6Test, operatorEquals) { // check a variety of addresses with different bits set. const IOAddress addr("2001:db8:1::456"); @@ -665,13 +668,6 @@ TEST(Lease6, OperatorEquals) { EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the EXPECT_FALSE(lease1 != lease2); // ... leases equal - lease1.fixed_ = !lease1.fixed_; - EXPECT_FALSE(lease1 == lease2); - EXPECT_TRUE(lease1 != lease2); - lease1.fixed_ = lease2.fixed_; - EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the - EXPECT_FALSE(lease1 != lease2); // ... leases equal - lease1.hostname_ += std::string("Something random"); EXPECT_FALSE(lease1 == lease2); EXPECT_TRUE(lease1 != lease2); @@ -693,16 +689,16 @@ TEST(Lease6, OperatorEquals) { EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the EXPECT_FALSE(lease1 != lease2); // ... leases equal - lease1.comments_ += std::string("Something random"); + lease1.state_ += 1; EXPECT_FALSE(lease1 == lease2); EXPECT_TRUE(lease1 != lease2); - lease1.comments_ = lease2.comments_; + lease2.state_ += 1; EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the EXPECT_FALSE(lease1 != lease2); // ... leases equal } // Checks if lease expiration is calculated properly -TEST(Lease6, Lease6Expired) { +TEST(Lease6Test, Lease6Expired) { const IOAddress addr("2001:db8:1::456"); const uint8_t duid_array[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; const DuidPtr duid(new DUID(duid_array, sizeof(duid_array))); @@ -727,7 +723,7 @@ TEST(Lease6, Lease6Expired) { // Verify that the DUID can be returned as a vector object and if DUID is NULL // the empty vector is returned. -TEST(Lease6, getDuidVector) { +TEST(Lease6Test, getDuidVector) { // Create a lease. Lease6 lease; // By default, the lease should have client id set to NULL. If it doesn't, @@ -746,7 +742,7 @@ TEST(Lease6, getDuidVector) { } // Verify the behavior of the function which checks FQDN data for equality. -TEST(Lease6, hasIdenticalFqdn) { +TEST(Lease6Test, hasIdenticalFqdn) { Lease6 lease = createLease6("myhost.example.com.", true, true); EXPECT_TRUE(lease.hasIdenticalFqdn(createLease6("myhost.example.com.", true, true))); @@ -763,7 +759,7 @@ TEST(Lease6, hasIdenticalFqdn) { } // Verify that toText() method reports Lease4 structure properly. -TEST(Lease6, toText) { +TEST(Lease6Test, toText) { HWAddrPtr hwaddr(new HWAddr(HWADDR, sizeof(HWADDR), HTYPE_ETHER)); @@ -773,6 +769,7 @@ TEST(Lease6, toText) { Lease6 lease(Lease::TYPE_NA, IOAddress("2001:db8::1"), duid, 123456, 400, 800, 100, 200, 5678, hwaddr, 128); lease.cltt_ = 12345678; + lease.state_ = Lease::STATE_DECLINED; std::stringstream expected; expected << "Type: IA_NA(" << static_cast(Lease::TYPE_NA) << ")\n" @@ -783,7 +780,8 @@ TEST(Lease6, toText) { << "Valid life: 800\n" << "Cltt: 12345678\n" << "Hardware addr: " << hwaddr->toText(false) << "\n" - << "Subnet ID: 5678\n"; + << "Subnet ID: 5678\n" + << "State: declined\n"; EXPECT_EQ(expected.str(), lease.toText()); @@ -798,8 +796,17 @@ TEST(Lease6, toText) { << "Valid life: 800\n" << "Cltt: 12345678\n" << "Hardware addr: (none)\n" - << "Subnet ID: 5678\n"; + << "Subnet ID: 5678\n" + << "State: declined\n"; EXPECT_EQ(expected.str(), lease.toText()); } +// Verify that the lease states are correctly returned in the textual format. +TEST(Lease6Test, stateToText) { + EXPECT_EQ("default", Lease6::statesToText(Lease::STATE_DEFAULT)); + EXPECT_EQ("declined", Lease6::statesToText(Lease::STATE_DECLINED)); + EXPECT_EQ("expired-reclaimed", Lease6::statesToText(Lease::STATE_EXPIRED_RECLAIMED)); +} + + }; // end of anonymous namespace diff --git a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc index c7b5c23d3d..1f3f023ae7 100644 --- a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc @@ -404,20 +404,20 @@ TEST_F(MemfileLeaseMgrTest, leaseFileCleanup4) { // stored. std::string new_file_contents = "address,hwaddr,client_id,valid_lifetime,expire," - "subnet_id,fqdn_fwd,fqdn_rev,hostname\n"; + "subnet_id,fqdn_fwd,fqdn_rev,hostname,state\n"; // This string contains the contents of the lease file with exactly // one lease, but two entries. One of the entries should be removed // as a result of lease file cleanup. std::string current_file_contents = new_file_contents + - "192.0.2.2,02:02:02:02:02:02,,200,200,8,1,1,,\n" - "192.0.2.2,02:02:02:02:02:02,,200,800,8,1,1,,\n"; + "192.0.2.2,02:02:02:02:02:02,,200,200,8,1,1,,1\n" + "192.0.2.2,02:02:02:02:02:02,,200,800,8,1,1,,1\n"; LeaseFileIO current_file(getLeaseFilePath("leasefile4_0.csv")); current_file.writeFile(current_file_contents); std::string previous_file_contents = new_file_contents + - "192.0.2.3,03:03:03:03:03:03,,200,200,8,1,1,,\n" - "192.0.2.3,03:03:03:03:03:03,,200,800,8,1,1,,\n"; + "192.0.2.3,03:03:03:03:03:03,,200,200,8,1,1,,1\n" + "192.0.2.3,03:03:03:03:03:03,,200,800,8,1,1,,1\n"; LeaseFileIO previous_file(getLeaseFilePath("leasefile4_0.csv.2")); previous_file.writeFile(previous_file_contents); @@ -453,15 +453,15 @@ TEST_F(MemfileLeaseMgrTest, leaseFileCleanup4) { ASSERT_NO_THROW(lease_mgr->addLease(new_lease)); std::string updated_file_contents = new_file_contents + - "192.0.2.45,00:00:00:00:00:00,,100,100,1,0,0,\n"; + "192.0.2.45,00:00:00:00:00:00,,100,100,1,0,0,,0\n"; EXPECT_EQ(updated_file_contents, current_file.readFile()); // This string contains the contents of the lease file we // expect after the LFC run. It has two leases with one // entry each. std::string result_file_contents = new_file_contents + - "192.0.2.2,02:02:02:02:02:02,,200,800,8,1,1,\n" - "192.0.2.3,03:03:03:03:03:03,,200,800,8,1,1,\n"; + "192.0.2.2,02:02:02:02:02:02,,200,800,8,1,1,,1\n" + "192.0.2.3,03:03:03:03:03:03,,200,800,8,1,1,,1\n"; // The LFC should have created a file with the two leases and moved it // to leasefile4_0.csv.2 @@ -480,24 +480,24 @@ TEST_F(MemfileLeaseMgrTest, leaseFileCleanup6) { std::string new_file_contents = "address,duid,valid_lifetime,expire,subnet_id," "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd," - "fqdn_rev,hostname,hwaddr\n"; + "fqdn_rev,hostname,hwaddr,state\n"; // This string contains the contents of the lease file with exactly // one lease, but two entries. One of the entries should be removed // as a result of lease file cleanup. std::string current_file_contents = new_file_contents + "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,200,200," - "8,100,0,7,0,1,1,,\n" + "8,100,0,7,0,1,1,,,1\n" "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,200,800," - "8,100,0,7,0,1,1,,\n"; + "8,100,0,7,0,1,1,,,1\n"; LeaseFileIO current_file(getLeaseFilePath("leasefile6_0.csv")); current_file.writeFile(current_file_contents); std::string previous_file_contents = new_file_contents + "2001:db8:1::2,01:01:01:01:01:01:01:01:01:01:01:01:01,200,200," - "8,100,0,7,0,1,1,,\n" + "8,100,0,7,0,1,1,,,1\n" "2001:db8:1::2,01:01:01:01:01:01:01:01:01:01:01:01:01,200,800," - "8,100,0,7,0,1,1,,\n"; + "8,100,0,7,0,1,1,,,1\n"; LeaseFileIO previous_file(getLeaseFilePath("leasefile6_0.csv.2")); previous_file.writeFile(previous_file_contents); @@ -536,7 +536,7 @@ TEST_F(MemfileLeaseMgrTest, leaseFileCleanup6) { std::string update_file_contents = new_file_contents + "3000::1,00:00:00:00:00:00:00:00:00:00:00:00:00,400," - "400,2,300,0,123,128,0,0,,\n"; + "400,2,300,0,123,128,0,0,,,0\n"; EXPECT_EQ(update_file_contents, current_file.readFile()); // This string contains the contents of the lease file we @@ -544,9 +544,9 @@ TEST_F(MemfileLeaseMgrTest, leaseFileCleanup6) { // entry each. std::string result_file_contents = new_file_contents + "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,200,800," - "8,100,0,7,0,1,1,,\n" + "8,100,0,7,0,1,1,,,1\n" "2001:db8:1::2,01:01:01:01:01:01:01:01:01:01:01:01:01,200,800," - "8,100,0,7,0,1,1,,\n"; + "8,100,0,7,0,1,1,,,1\n"; // The LFC should have created a file with the two leases and moved it // to leasefile6_0.csv.2 @@ -564,11 +564,11 @@ TEST_F(MemfileLeaseMgrTest, leaseFileCleanupStartFail) { // stored. std::string new_file_contents = "address,hwaddr,client_id,valid_lifetime,expire," - "subnet_id,fqdn_fwd,fqdn_rev,hostname\n"; + "subnet_id,fqdn_fwd,fqdn_rev,hostname,state\n"; // Create the lease file to be used by the backend. std::string current_file_contents = new_file_contents + - "192.0.2.2,02:02:02:02:02:02,,200,200,8,1,1,,\n"; + "192.0.2.2,02:02:02:02:02:02,,200,200,8,1,1,,1\n"; LeaseFileIO current_file(getLeaseFilePath("leasefile4_0.csv")); current_file.writeFile(current_file_contents); @@ -605,15 +605,15 @@ TEST_F(MemfileLeaseMgrTest, leaseFileFinish) { std::string new_file_contents = "address,duid,valid_lifetime,expire,subnet_id," "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd," - "fqdn_rev,hostname,hwaddr\n"; + "fqdn_rev,hostname,hwaddr,state\n"; // This string contains the contents of the current lease file. // It should not be moved. std::string current_file_contents = new_file_contents + "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,200,200," - "8,100,0,7,0,1,1,,\n" + "8,100,0,7,0,1,1,,,1\n" "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,200,800," - "8,100,0,7,0,1,1,,\n"; + "8,100,0,7,0,1,1,,,1\n"; LeaseFileIO current_file(getLeaseFilePath("leasefile6_0.csv")); current_file.writeFile(current_file_contents); @@ -621,7 +621,7 @@ TEST_F(MemfileLeaseMgrTest, leaseFileFinish) { // be moved to the previous file. std::string finish_file_contents = new_file_contents + "2001:db8:1::2,01:01:01:01:01:01:01:01:01:01:01:01:01,200,800," - "8,100,0,7,0,1,1,,\n"; + "8,100,0,7,0,1,1,,,1\n"; LeaseFileIO finish_file(getLeaseFilePath("leasefile6_0.csv.completed")); finish_file.writeFile(finish_file_contents); @@ -668,15 +668,15 @@ TEST_F(MemfileLeaseMgrTest, leaseFileCopy) { std::string new_file_contents = "address,duid,valid_lifetime,expire,subnet_id," "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd," - "fqdn_rev,hostname,hwaddr\n"; + "fqdn_rev,hostname,hwaddr,state\n"; // This string contains the contents of the current lease file. // It should not be moved. std::string current_file_contents = new_file_contents + "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,200,200," - "8,100,0,7,0,1,1,,\n" + "8,100,0,7,0,1,1,,,1\n" "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,200,800," - "8,100,0,7,0,1,1,,\n"; + "8,100,0,7,0,1,1,,,1\n"; LeaseFileIO current_file(getLeaseFilePath("leasefile6_0.csv")); current_file.writeFile(current_file_contents); @@ -686,7 +686,7 @@ TEST_F(MemfileLeaseMgrTest, leaseFileCopy) { // the same. std::string input_file_contents = new_file_contents + "2001:db8:1::2,01:01:01:01:01:01:01:01:01:01:01:01:01,200,800," - "8,100,0,7,0,1,1,,\n"; + "8,100,0,7,0,1,1,,,1\n"; LeaseFileIO input_file(getLeaseFilePath("leasefile6_0.csv.1")); input_file.writeFile(input_file_contents); @@ -872,15 +872,48 @@ TEST_F(MemfileLeaseMgrTest, getLeases6DuidIaid) { testGetLeases6DuidIaid(); } -// Check that the system can cope with a DUID of allowed size. - -/// @todo: test disabled, because Memfile_LeaseMgr::getLeases6(Lease::Type, -/// const DUID& duid, uint32_t iaid) const is not implemented yet. +/// @brief Check that the system can cope with a DUID of allowed size. TEST_F(MemfileLeaseMgrTest, getLeases6DuidSize) { startBackend(V6); testGetLeases6DuidSize(); } +/// @brief Check that the expired DHCPv4 leases can be retrieved. +/// +/// This test adds a number of leases to the lease database and marks +/// some of them as expired. Then it queries for expired leases and checks +/// whether only expired leases are returned, and that they are returned in +/// the order from most to least expired. It also checks that the lease +/// which is marked as 'reclaimed' is not returned. +TEST_F(MemfileLeaseMgrTest, getExpiredLeases4) { + startBackend(V4); + testGetExpiredLeases4(); +} + +/// @brief Check that the expired DHCPv6 leases can be retrieved. +/// +/// This test adds a number of leases to the lease database and marks +/// some of them as expired. Then it queries for expired leases and checks +/// whether only expired leases are returned, and that they are returned in +/// the order from most to least expired. It also checks that the lease +/// which is marked as 'reclaimed' is not returned. +TEST_F(MemfileLeaseMgrTest, getExpiredLeases6) { + startBackend(V6); + testGetExpiredLeases6(); +} + +/// @brief Check that expired reclaimed DHCPv6 leases are removed. +TEST_F(MemfileLeaseMgrTest, deleteExpiredReclaimedLeases6) { + startBackend(V6); + testDeleteExpiredReclaimedLeases6(); +} + +/// @brief Check that expired reclaimed DHCPv4 leases are removed. +TEST_F(MemfileLeaseMgrTest, deleteExpiredReclaimedLeases4) { + startBackend(V4); + testDeleteExpiredReclaimedLeases4(); +} + /// @brief Check that getLease6 methods discriminate by lease type. /// /// Adds six leases, two per lease type all with the same duid and iad but @@ -975,43 +1008,6 @@ TEST_F(MemfileLeaseMgrTest, testLease6Mac) { testLease6MAC(); } -/// @brief Tests whether memfile is able to work with old CSV file (without mac) -/// -/// Ticket #3555 introduced MAC address support in Lease6. Instead of developing -/// an upgrade script, the code is written in a way that allows reading old CSV -/// (i.e. format that was used in Kea 0.9), hence no upgrade is necessary. -TEST_F(MemfileLeaseMgrTest, testUpgrade0_9_0_to_0_9_1) { - - // Let's write a CSV file without hwaddr column. Sorry about the long - // lines, but nobody was around to whine about 80 columns limit when CSV - // format was invented :). - string csv_nohwaddr = - "address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname\n" - "2001:db8::1,42:42:42:42:42:42:42:42,3677,127133,73,3600,1,42,0,0,1,myhost.example.com.\n" - "2001:db8::2,3a:3a:3a:3a:3a:3a:3a:3a,5412,239979,73,1800,2,89,7,0,0,myhost.example.com.\n" - "2001:db8::3,1f:20:21:22:23:24:25:26,7000,241567,37,7200,0,4294967294,28,1,0,myhost.example.com.\n"; - - ofstream csv(getLeaseFilePath("leasefile6_0.csv").c_str(), ios::out | ios::trunc); - ASSERT_TRUE(csv.is_open()); - csv << csv_nohwaddr; - csv.close(); - - startBackend(V6); - - // None of the leases should have any hardware addresses assigned. - Lease6Ptr stored1 = lmptr_->getLease6(leasetype6_[1], ioaddress6_[1]); - ASSERT_TRUE(stored1); - EXPECT_FALSE(stored1->hwaddr_); - - Lease6Ptr stored2 = lmptr_->getLease6(leasetype6_[2], ioaddress6_[2]); - ASSERT_TRUE(stored2); - EXPECT_FALSE(stored2->hwaddr_); - - Lease6Ptr stored3 = lmptr_->getLease6(leasetype6_[3], ioaddress6_[3]); - ASSERT_TRUE(stored3); - EXPECT_FALSE(stored3->hwaddr_); -} - // Check that memfile reports version correctly. TEST_F(MemfileLeaseMgrTest, versionCheck) { @@ -1033,22 +1029,22 @@ TEST_F(MemfileLeaseMgrTest, versionCheck) { TEST_F(MemfileLeaseMgrTest, load4MultipleLeaseFiles) { LeaseFileIO io2(getLeaseFilePath("leasefile4_0.csv.2")); io2.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id," - "fqdn_fwd,fqdn_rev,hostname\n" - "192.0.2.2,02:02:02:02:02:02,,200,200,8,1,1,,\n" - "192.0.2.11,bb:bb:bb:bb:bb:bb,,200,200,8,1,1,,\n"); + "fqdn_fwd,fqdn_rev,hostname,state\n" + "192.0.2.2,02:02:02:02:02:02,,200,200,8,1,1,,1\n" + "192.0.2.11,bb:bb:bb:bb:bb:bb,,200,200,8,1,1,,1\n"); LeaseFileIO io1(getLeaseFilePath("leasefile4_0.csv.1")); io1.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id," - "fqdn_fwd,fqdn_rev,hostname\n" - "192.0.2.1,01:01:01:01:01:01,,200,200,8,1,1,,\n" - "192.0.2.11,bb:bb:bb:bb:bb:bb,,200,400,8,1,1,,\n" - "192.0.2.12,cc:cc:cc:cc:cc:cc,,200,200,8,1,1,,\n"); + "fqdn_fwd,fqdn_rev,hostname,state\n" + "192.0.2.1,01:01:01:01:01:01,,200,200,8,1,1,,1\n" + "192.0.2.11,bb:bb:bb:bb:bb:bb,,200,400,8,1,1,,1\n" + "192.0.2.12,cc:cc:cc:cc:cc:cc,,200,200,8,1,1,,1\n"); LeaseFileIO io(getLeaseFilePath("leasefile4_0.csv")); io.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id," - "fqdn_fwd,fqdn_rev,hostname\n" - "192.0.2.10,0a:0a:0a:0a:0a:0a,,200,200,8,1,1,,\n" - "192.0.2.12,cc:cc:cc:cc:cc:cc,,200,400,8,1,1,,\n"); + "fqdn_fwd,fqdn_rev,hostname,state\n" + "192.0.2.10,0a:0a:0a:0a:0a:0a,,200,200,8,1,1,,1\n" + "192.0.2.12,cc:cc:cc:cc:cc:cc,,200,400,8,1,1,,1\n"); startBackend(V4); @@ -1091,27 +1087,27 @@ TEST_F(MemfileLeaseMgrTest, load4MultipleLeaseFiles) { TEST_F(MemfileLeaseMgrTest, load4CompletedFile) { LeaseFileIO io2(getLeaseFilePath("leasefile4_0.csv.2")); io2.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id," - "fqdn_fwd,fqdn_rev,hostname\n" - "192.0.2.2,02:02:02:02:02:02,,200,200,8,1,1,,\n" - "192.0.2.11,bb:bb:bb:bb:bb:bb,,200,200,8,1,1,,\n"); + "fqdn_fwd,fqdn_rev,hostname,state\n" + "192.0.2.2,02:02:02:02:02:02,,200,200,8,1,1,,1\n" + "192.0.2.11,bb:bb:bb:bb:bb:bb,,200,200,8,1,1,,1\n"); LeaseFileIO io1(getLeaseFilePath("leasefile4_0.csv.1")); io1.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id," - "fqdn_fwd,fqdn_rev,hostname\n" - "192.0.2.1,01:01:01:01:01:01,,200,200,8,1,1,,\n" - "192.0.2.11,bb:bb:bb:bb:bb:bb,,200,400,8,1,1,,\n" - "192.0.2.12,cc:cc:cc:cc:cc:cc,,200,200,8,1,1,,\n"); + "fqdn_fwd,fqdn_rev,hostname,state\n" + "192.0.2.1,01:01:01:01:01:01,,200,200,8,1,1,,1\n" + "192.0.2.11,bb:bb:bb:bb:bb:bb,,200,400,8,1,1,,1\n" + "192.0.2.12,cc:cc:cc:cc:cc:cc,,200,200,8,1,1,,1\n"); LeaseFileIO io(getLeaseFilePath("leasefile4_0.csv")); io.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id," - "fqdn_fwd,fqdn_rev,hostname\n" - "192.0.2.10,0a:0a:0a:0a:0a:0a,,200,200,8,1,1,,\n" - "192.0.2.12,cc:cc:cc:cc:cc:cc,,200,400,8,1,1,,\n"); + "fqdn_fwd,fqdn_rev,hostname,state\n" + "192.0.2.10,0a:0a:0a:0a:0a:0a,,200,200,8,1,1,,1\n" + "192.0.2.12,cc:cc:cc:cc:cc:cc,,200,400,8,1,1,,1\n"); LeaseFileIO ioc(getLeaseFilePath("leasefile4_0.csv.completed")); ioc.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id," - "fqdn_fwd,fqdn_rev,hostname\n" - "192.0.2.13,ff:ff:ff:ff:ff:ff,,200,200,8,1,1,,\n"); + "fqdn_fwd,fqdn_rev,hostname,state\n" + "192.0.2.13,ff:ff:ff:ff:ff:ff,,200,200,8,1,1,,1\n"); startBackend(V4); @@ -1167,29 +1163,32 @@ TEST_F(MemfileLeaseMgrTest, load4LFCInProgress) { TEST_F(MemfileLeaseMgrTest, load6MultipleLeaseFiles) { LeaseFileIO io2(getLeaseFilePath("leasefile6_0.csv.2")); io2.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime," - "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr\n" + "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr," + "state\n" "2001:db8:1::1,01:01:01:01:01:01:01:01:01:01:01:01:01," - "200,200,8,100,0,7,0,1,1,,\n" + "200,200,8,100,0,7,0,1,1,,,1\n" "2001:db8:1::2,02:02:02:02:02:02:02:02:02:02:02:02:02," - "200,200,8,100,0,7,0,1,1,,\n"); + "200,200,8,100,0,7,0,1,1,,,1\n"); LeaseFileIO io1(getLeaseFilePath("leasefile6_0.csv.1")); io1.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime," - "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr\n" + "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr," + "state\n" "2001:db8:1::3,03:03:03:03:03:03:03:03:03:03:03:03:03," - "200,200,8,100,0,7,0,1,1,,\n" + "200,200,8,100,0,7,0,1,1,,,1\n" "2001:db8:1::2,02:02:02:02:02:02:02:02:02:02:02:02:02," - "300,800,8,100,0,7,0,1,1,,\n" + "300,800,8,100,0,7,0,1,1,,,1\n" "2001:db8:1::4,04:04:04:04:04:04:04:04:04:04:04:04:04," - "200,200,8,100,0,7,0,1,1,,\n"); + "200,200,8,100,0,7,0,1,1,,,1\n"); LeaseFileIO io(getLeaseFilePath("leasefile6_0.csv")); io.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime," - "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr\n" + "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr," + "state\n" "2001:db8:1::4,04:04:04:04:04:04:04:04:04:04:04:04:04," - "400,1000,8,100,0,7,0,1,1,,\n" + "400,1000,8,100,0,7,0,1,1,,,1\n" "2001:db8:1::5,05:05:05:05:05:05:05:05:05:05:05:05:05," - "200,200,8,100,0,7,0,1,1,,\n"); + "200,200,8,100,0,7,0,1,1,,,1\n"); startBackend(V6); @@ -1230,21 +1229,23 @@ TEST_F(MemfileLeaseMgrTest, load6MultipleLeaseFiles) { TEST_F(MemfileLeaseMgrTest, load6MultipleNoSecondFile) { LeaseFileIO io1(getLeaseFilePath("leasefile6_0.csv.1")); io1.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime," - "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr\n" + "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr," + "state\n" "2001:db8:1::3,03:03:03:03:03:03:03:03:03:03:03:03:03," - "200,200,8,100,0,7,0,1,1,,\n" + "200,200,8,100,0,7,0,1,1,,,1\n" "2001:db8:1::2,02:02:02:02:02:02:02:02:02:02:02:02:02," - "300,800,8,100,0,7,0,1,1,,\n" + "300,800,8,100,0,7,0,1,1,,,1\n" "2001:db8:1::4,04:04:04:04:04:04:04:04:04:04:04:04:04," - "200,200,8,100,0,7,0,1,1,,\n"); + "200,200,8,100,0,7,0,1,1,,,1\n"); LeaseFileIO io(getLeaseFilePath("leasefile6_0.csv")); io.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime," - "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr\n" + "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr," + "state\n" "2001:db8:1::4,04:04:04:04:04:04:04:04:04:04:04:04:04," - "400,1000,8,100,0,7,0,1,1,,\n" + "400,1000,8,100,0,7,0,1,1,,,1\n" "2001:db8:1::5,05:05:05:05:05:05:05:05:05:05:05:05:05," - "200,200,8,100,0,7,0,1,1,,\n"); + "200,200,8,100,0,7,0,1,1,,,1\n"); startBackend(V6); @@ -1276,19 +1277,21 @@ TEST_F(MemfileLeaseMgrTest, load6MultipleNoSecondFile) { TEST_F(MemfileLeaseMgrTest, load6MultipleNoFirstFile) { LeaseFileIO io2(getLeaseFilePath("leasefile6_0.csv.2")); io2.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime," - "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr\n" + "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr," + "state\n" "2001:db8:1::1,01:01:01:01:01:01:01:01:01:01:01:01:01," - "200,200,8,100,0,7,0,1,1,,\n" + "200,200,8,100,0,7,0,1,1,,,1\n" "2001:db8:1::2,02:02:02:02:02:02:02:02:02:02:02:02:02," - "200,200,8,100,0,7,0,1,1,,\n"); + "200,200,8,100,0,7,0,1,1,,,1\n"); LeaseFileIO io(getLeaseFilePath("leasefile6_0.csv")); io.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime," - "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr\n" + "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr," + "state\n" "2001:db8:1::4,04:04:04:04:04:04:04:04:04:04:04:04:04," - "400,1000,8,100,0,7,0,1,1,,\n" + "400,1000,8,100,0,7,0,1,1,,,1\n" "2001:db8:1::5,05:05:05:05:05:05:05:05:05:05:05:05:05," - "200,200,8,100,0,7,0,1,1,,\n"); + "200,200,8,100,0,7,0,1,1,,,1\n"); startBackend(V6); @@ -1322,35 +1325,39 @@ TEST_F(MemfileLeaseMgrTest, load6MultipleNoFirstFile) { TEST_F(MemfileLeaseMgrTest, load6CompletedFile) { LeaseFileIO io2(getLeaseFilePath("leasefile6_0.csv.2")); io2.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime," - "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr\n" + "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr," + "state\n" "2001:db8:1::1,01:01:01:01:01:01:01:01:01:01:01:01:01," - "200,200,8,100,0,7,0,1,1,,\n" + "200,200,8,100,0,7,0,1,1,,,1\n" "2001:db8:1::2,02:02:02:02:02:02:02:02:02:02:02:02:02," - "200,200,8,100,0,7,0,1,1,,\n"); + "200,200,8,100,0,7,0,1,1,,,1\n"); LeaseFileIO io1(getLeaseFilePath("leasefile6_0.csv.1")); io1.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime," - "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr\n" + "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr," + "state\n" "2001:db8:1::3,03:03:03:03:03:03:03:03:03:03:03:03:03," - "200,200,8,100,0,7,0,1,1,,\n" + "200,200,8,100,0,7,0,1,1,,,1\n" "2001:db8:1::2,02:02:02:02:02:02:02:02:02:02:02:02:02," - "300,800,8,100,0,7,0,1,1,,\n" + "300,800,8,100,0,7,0,1,1,,,1\n" "2001:db8:1::4,04:04:04:04:04:04:04:04:04:04:04:04:04," - "200,200,8,100,0,7,0,1,1,,\n"); + "200,200,8,100,0,7,0,1,1,,,1\n"); LeaseFileIO io(getLeaseFilePath("leasefile6_0.csv")); io.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime," - "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr\n" + "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr," + "state\n" "2001:db8:1::4,04:04:04:04:04:04:04:04:04:04:04:04:04," - "400,1000,8,100,0,7,0,1,1,,\n" + "400,1000,8,100,0,7,0,1,1,,,1\n" "2001:db8:1::5,05:05:05:05:05:05:05:05:05:05:05:05:05," - "200,200,8,100,0,7,0,1,1,,\n"); + "200,200,8,100,0,7,0,1,1,,,1\n"); LeaseFileIO ioc(getLeaseFilePath("leasefile6_0.csv.completed")); ioc.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime," - "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr\n" + "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr," + "state\n" "2001:db8:1::125,ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff," - "400,1000,8,100,0,7,0,1,1,,\n"); + "400,1000,8,100,0,7,0,1,1,,,1\n"); startBackend(V6);