From: Marcin Siodelski Date: Thu, 13 Aug 2015 10:18:20 +0000 (+0200) Subject: [3965] Extended lease object to hold state. X-Git-Tag: trac3238_base~1^2~15 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=86bf7774edd0df4858f205373ef0afd0bad74dd0;p=thirdparty%2Fkea.git [3965] Extended lease object to hold state. --- diff --git a/src/lib/dhcpsrv/lease.cc b/src/lib/dhcpsrv/lease.cc index 616974852d..9605b6b39a 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,21 @@ using namespace std; namespace isc { namespace dhcp { +const uint32_t Lease::STATE_DEFAULT = 0x1; +const uint32_t Lease::STATE_DECLINED = 0x2; +const uint32_t Lease::STATE_EXPIRED_RECLAIMED = 0x4; + +const unsigned int Lease::BASIC_STATES_NUM = 3; + + 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) { + fqdn_fwd_(fqdn_fwd), fqdn_rev_(fqdn_rev), hwaddr_(hwaddr), + state_(STATE_DEFAULT) { } @@ -52,11 +61,57 @@ Lease::typeToText(Lease::Type type) { } } -bool Lease::expired() const { +std::string +Lease::basicStatesToText(const uint32_t state) { + // Stream will hold comma separated list of states. + std::ostringstream s; + // Iterate over all defined states. + for (int i = 0; i < BASIC_STATES_NUM; ++i) { + // Test each bit of the lease state to see if it is set. + uint32_t single_state = (state & (static_cast(1) << i)); + // Only proceed if the bit is set. + if (single_state > 0) { + // Comma sign is applied only if any state has been found. + if (static_cast(s.tellp() > 0)) { + s << ","; + } + // Check which bit is set and append state name. + switch (single_state) { + case STATE_DEFAULT: + s << "default"; + break; + + case STATE_DECLINED: + s << "declined"; + break; + + case STATE_EXPIRED_RECLAIMED: + s << "expired-reclaimed"; + break; + + default: + // This shouldn't really happen. + s << "unknown"; + } + } + } + + return (s.tellp() > 0 ? s.str() : "(not set)"); +} + +bool +Lease::expired() const { + return (getExpirationTime() < time(NULL)); +} + +bool +Lease::stateExpiredReclaimed() const { + return ((state_ & STATE_EXPIRED_RECLAIMED) != 0); +} - // Let's use int64 to avoid problems with negative/large uint32 values - int64_t expire_time = cltt_ + valid_lft_; - return (expire_time < time(NULL)); +int64_t +Lease::getExpirationTime() const { + return (static_cast(cltt_) + valid_lft_); } bool @@ -69,11 +124,13 @@ 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_; + // Copy over fields derived from Lease. + ext_ = other.ext_; comments_ = other.comments_; + fixed_ = other.fixed_; + state_ = other.state_; // Copy the hardware address if it is defined. if (other.hwaddr_) { @@ -161,6 +218,7 @@ Lease4::operator=(const Lease4& other) { 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_) { @@ -239,7 +297,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 +314,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()); } @@ -276,7 +336,8 @@ Lease4::operator==(const Lease4& other) const { hostname_ == other.hostname_ && fqdn_fwd_ == other.fqdn_fwd_ && fqdn_rev_ == other.fqdn_rev_ && - comments_ == other.comments_); + comments_ == other.comments_ && + state_ == other.state_); } bool @@ -297,7 +358,8 @@ Lease6::operator==(const Lease6& other) const { hostname_ == other.hostname_ && fqdn_fwd_ == other.fqdn_fwd_ && fqdn_rev_ == other.fqdn_rev_ && - comments_ == other.comments_); + comments_ == other.comments_ && + state_ == other.state_); } std::ostream& diff --git a/src/lib/dhcpsrv/lease.h b/src/lib/dhcpsrv/lease.h index 082c8de438..60f90c76f2 100644 --- a/src/lib/dhcpsrv/lease.h +++ b/src/lib/dhcpsrv/lease.h @@ -48,6 +48,34 @@ 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 Number of common states for DHCPv4 and DHCPv6. + /// + /// This constants holds the number of states used for both DHCPv4 and + /// DHCPv6 leases. If new states are defined, this value must be adjusted + /// accordingly. + static const unsigned int BASIC_STATES_NUM; + //@} + + /// @brief Returns name(s) of the basic lease state(s). + /// + /// @param state A numeric value holding a state information. Multiple bits + /// may be set to indicate that the lease is in multiple states. + /// + /// @return Comma separated list of state names. + static std::string basicStatesToText(const uint32_t state); + /// @brief Constructor /// /// @param addr IP address @@ -139,6 +167,14 @@ struct Lease { /// system administrator. std::string comments_; + /// @brief Bitfield holding lease state(s). + /// + /// This is a bitfield which holds the lease state. Typically, a lease + /// remains in a single state, but it is possible set multiple states + /// for a single lease. In this case, multiple bits of this bitfield + /// will be set. + uint32_t state_; + /// @brief Convert Lease to Printable Form /// /// @return String form of the lease @@ -148,6 +184,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 +207,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 @@ -255,6 +303,19 @@ 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) { + return (Lease::basicStatesToText(state)); + } + + /// @brief Returns a client identifier. /// /// @warning Since the function returns the reference to a vector (not a @@ -440,6 +501,18 @@ 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) { + return (Lease::basicStatesToText(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/tests/lease_unittest.cc b/src/lib/dhcpsrv/tests/lease_unittest.cc index 9292801fd9..d6e9d1ebfa 100644 --- a/src/lib/dhcpsrv/tests/lease_unittest.cc +++ b/src/lib/dhcpsrv/tests/lease_unittest.cc @@ -109,6 +109,7 @@ TEST_F(Lease4Test, constructor) { EXPECT_TRUE(lease.fqdn_fwd_); EXPECT_TRUE(lease.fqdn_rev_); EXPECT_TRUE(lease.comments_.empty()); + EXPECT_EQ(Lease::STATE_DEFAULT, lease.state_); } } @@ -126,6 +127,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 +168,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. @@ -377,6 +388,13 @@ TEST_F(Lease4Test, operatorEquals) { lease1.comments_ = lease2.comments_; EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the EXPECT_FALSE(lease1 != lease2); // ... leases equal + + lease1.state_ += 1; + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + lease2.state_ += 1; + EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the + EXPECT_FALSE(lease1 != lease2); // ... leases equal } // Verify that the client id can be returned as a vector object and if client @@ -430,7 +448,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 +464,26 @@ 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)); + + // Try multiple states. + EXPECT_EQ("default,declined,expired-reclaimed", + Lease4::statesToText(Lease::STATE_DEFAULT | Lease::STATE_DECLINED | + Lease::STATE_EXPIRED_RECLAIMED)); + + // Try undefined state. + EXPECT_EQ("(not set)", Lease4::statesToText(0)); +} + /// @brief Creates an instance of the lease with certain FQDN data. /// /// @param hostname Hostname. @@ -467,7 +502,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 +549,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 +598,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"); @@ -699,10 +734,17 @@ TEST(Lease6, OperatorEquals) { lease1.comments_ = lease2.comments_; EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the EXPECT_FALSE(lease1 != lease2); // ... leases equal + + lease1.state_ += 1; + EXPECT_FALSE(lease1 == lease2); + EXPECT_TRUE(lease1 != lease2); + 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 +769,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 +788,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 +805,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 +815,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 +826,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 +842,25 @@ 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)); + + // Try multiple states. + EXPECT_EQ("default,declined,expired-reclaimed", + Lease6::statesToText(Lease::STATE_DEFAULT | Lease::STATE_DECLINED | + Lease::STATE_EXPIRED_RECLAIMED)); + + // Try undefined state. + EXPECT_EQ("(not set)", Lease6::statesToText(0)); +} + + }; // end of anonymous namespace