Parcourir la source

[3965] Extended lease object to hold state.

Marcin Siodelski il y a 9 ans
Parent
commit
86bf7774ed
3 fichiers modifiés avec 221 ajouts et 25 suppressions
  1. 74 12
      src/lib/dhcpsrv/lease.cc
  2. 73 0
      src/lib/dhcpsrv/lease.h
  3. 74 13
      src/lib/dhcpsrv/tests/lease_unittest.cc

+ 74 - 12
src/lib/dhcpsrv/lease.cc

@@ -15,6 +15,7 @@
 #include <dhcpsrv/lease.h>
 #include <util/pointer_util.h>
 #include <sstream>
+#include <iostream>
 
 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<uint32_t>(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<int>(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<int64_t>(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&

+ 73 - 0
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<uint8_t>& 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

+ 74 - 13
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<int>(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