Browse Source

[2342] Added updateLease6 capability

... where the elase is selected by address only.
Stephen Morris 12 years ago
parent
commit
4342869430

+ 9 - 1
src/lib/dhcp/lease_mgr.h

@@ -84,6 +84,13 @@ public:
         isc::Exception(file, line, what) {}
 };
 
+/// @brief Attempt to update lease that was not there
+class NoSuchLease : public Exception {
+public:
+    NoSuchLease(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
 /// @brief specifies unique subnet identifier
 /// @todo: Move this to subnet.h once ticket #2237 is merged
 typedef uint32_t SubnetID;
@@ -478,7 +485,8 @@ public:
     ///
     /// @param lease4 The lease to be updated.
     ///
-    /// If no such lease is present, an exception will be thrown.
+    /// @exception NoSuchLease Attempt to update lease that did not exist.
+    /// @exception DbOperationError Update operation updated multiple leases.
     virtual void updateLease6(const Lease6Ptr& lease6) = 0;
 
     /// @brief Deletes a lease.

+ 91 - 37
src/lib/dhcp/mysql_lease_mgr.cc

@@ -50,20 +50,16 @@ namespace dhcp {
 ///
 /// On any MySQL operation, arrays of MYSQL_BIND structures must be built to
 /// describe the parameters in the prepared statements.  Where information is
-/// inserted or retrieved - INSERT, UPDATE, SELECT - one array describes the
-/// data being exchanged with the database.  The other array describes the
-/// WHERE clause of the statement.
+/// inserted or retrieved - INSERT, UPDATE, SELECT - a large amount of that
+/// structure is identical - it defines data values in the Lease6 structure.
 ///
-/// The array describing the information exchanged is common between the
-/// INSERT, UPDATE and SELECT statements, and this class handles the creation
-/// of that array and the insertion/extraction of data into/from it.
+/// This class handles the creation of that array.  For maximum flexibility,
+/// the data is appended to an array of MYSQL_BIND elemements, so allowing
+/// additional elements to be prepended/appended to it.
 ///
 /// Owing to the MySQL API, the process requires some intermediate variables
 /// to hold things like length etc.  This object holds the intermediate
-/// variables and can:
-/// 1. Build the MYSQL_BIND structures for a Lease6 object ready for passing
-///    in to the MYSQL code.
-/// 1. Copy information from the MYSQL_BIND structures into a Lease6 object.
+/// variables as well.
 
 class MySqlLease6Exchange {
 public:
@@ -76,21 +72,19 @@ public:
     /// Fills in the MYSQL_BIND objects for the Lease6 passed to it.
     ///
     /// @param lease Lease object to be added to the database
-    ///
-    /// @return Pointer to MYSQL_BIND array holding the bind information.
-    ///         This is a pointer to data internal to this object, and remains
-    ///         valid only for as long as (1) this object is in existence and
-    ///         (2) the lease object passed to it is in existence.  The
-    ///         caller should NOT delete it.
-    MYSQL_BIND* createBindForSend(const Lease6Ptr& lease) {
+    /// @param bindvec Vector of MySQL BIND objects: the elements describing the
+    ///        lease are appended to this vector.  The data added to the vector
+    ///        only remain valid while both the lease and this object are valid.
+    void
+    createBindForSend(const Lease6Ptr& lease, std::vector<MYSQL_BIND>& bindvec) {
         // Store lease object to ensure it remains valid.
         lease_ = lease;
 
-        // Ensure bind array clear.
+        // Ensure bind_ array clear for constructing the MYSQL_BIND structures
+        // for this lease.
         memset(bind_, 0, sizeof(bind_));
 
         // address: varchar(40)
-
         addr6_ = lease_->addr_.toText();
         addr6_length_ = addr6_.size();
 
@@ -108,7 +102,7 @@ public:
         bind_[1].buffer_length = duid_length_;
         bind_[1].length = &duid_length_;
 
-        // lease_time: unsigned int
+        // valid lifetime: unsigned int
         bind_[2].buffer_type = MYSQL_TYPE_LONG;
         bind_[2].buffer = reinterpret_cast<char*>(&lease->valid_lft_);
         bind_[2].is_unsigned = true_;
@@ -119,6 +113,8 @@ public:
         /// expiry time (expire).  The relationship is given by:
         //
         // expire = cltt_ + valid_lft_
+        //
+        // @TODO Handle overflows
         MySqlLeaseMgr::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_,
                                              expire_);
         bind_[3].buffer_type = MYSQL_TYPE_TIMESTAMP;
@@ -156,7 +152,9 @@ public:
         bind_[8].buffer = reinterpret_cast<char*>(&lease_->prefixlen_);
         bind_[8].is_unsigned = true_;
 
-        return(bind_);
+        // Add the data to the vector.  Note the end element is one after the
+        // end of the array.
+        bindvec.insert(bindvec.end(), &bind_[0], &bind_[9]);
     }
 
     /// @brief Create BIND array to receive data
@@ -165,12 +163,14 @@ public:
     /// After data is successfully received, getLeaseData() is used to copy
     /// it to a Lease6 object.
     ///
-    /// @return Pointer to MYSQL_BIND array for data reception.  This array is
-    ///         valid only for as long as this MySqlLease6Exchange object is
-    ///         in existence.
-    MYSQL_BIND* createBindForReceive() {
+    /// @param bindvec Vector of MySQL BIND objects: the elements describing the
+    ///        lease are appended to this vector.  The data added to the vector
+    ///        only remain valid while both the lease and this object are valid.
+
+    void createBindForReceive(std::vector<MYSQL_BIND>& bindvec) {
 
-        // Ensure bind array clear.
+        // Ensure both the array of MYSQL_BIND structures and the error array
+        // are clear.
         memset(bind_, 0, sizeof(bind_));
         memset(error_, 0, sizeof(error_));
 
@@ -235,7 +235,9 @@ public:
         bind_[8].is_unsigned = true_;
         bind_[8].error = &error_[8];
 
-        return (bind_);
+        // Add the data to the vector.  Note the end element is one after the
+        // end of the array.
+        bindvec.insert(bindvec.end(), &bind_[0], &bind_[9]);
     }
 
     /// @brief Copy Received Data into Lease6 Object
@@ -253,7 +255,7 @@ public:
         // Create the object to be returned.
         Lease6Ptr result(new Lease6());
 
-        // Success - put the data in the lease object
+        // Put the data in the lease object
 
         // The address buffer is declared larger than the buffer size passed
         // to the access function so that we can always append a null byte.
@@ -465,9 +467,18 @@ MySqlLeaseMgr::openDatabase() {
                   mysql_error(mysql_));
     }
 
-    // Open the database.  Use defaults for non-specified options.
+    // Open the database.
+    //
+    // The option CLIENT_FOUND_ROWS is specified so that in an UPDATE,
+    // the affected rows are the number of rows found that match the
+    // WHERE clause of the SQL statement, not the rows changed.  The reason
+    // here is that MySQL apparently does not update a row if data has not
+    // changed and so the "affected rows" (retrievable from MySQL) is zero.
+    // This makes it hard to distinguish whether the UPDATE changed no rows
+    // because no row matching the WHERE clause was found, or because a
+    // row was found by no data was altered.
     MYSQL* status = mysql_real_connect(mysql_, host, user, password, name,
-                                       0, NULL, 0);
+                                       0, NULL, CLIENT_FOUND_ROWS);
     if (status != mysql_) {
         isc_throw(DbOpenError, mysql_error(mysql_));
     }
@@ -523,6 +534,11 @@ MySqlLeaseMgr::prepareStatements() {
                          "expire, subnet_id, pref_lifetime, "
                          "lease_type, iaid, prefix_len) "
                          "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
+    prepareStatement(UPDATE_LEASE6,
+                     "UPDATE lease6 SET address = ?, duid = ?, "
+                         "valid_lifetime = ?, expire = ?, subnet_id = ?, "
+                         "pref_lifetime = ?, lease_type = ?, iaid = ?, "
+                         "prefix_len = ? WHERE address = ?");
 }
 
 bool
@@ -537,10 +553,11 @@ MySqlLeaseMgr::addLease(const Lease6Ptr& lease) {
 
     // Create the MYSQL_BIND array for the lease
     MySqlLease6Exchange exchange;
-    MYSQL_BIND* bind = exchange.createBindForSend(lease);
+    std::vector<MYSQL_BIND> bind;
+    exchange.createBindForSend(lease, bind);
 
     // Bind the parameters to the statement
-    int status = mysql_stmt_bind_param(statements_[INSERT_LEASE6], bind);
+    int status = mysql_stmt_bind_param(statements_[INSERT_LEASE6], &bind[0]);
     checkError(status, INSERT_LEASE6, "unable to bind parameters");
 
     // Execute the statement
@@ -621,14 +638,15 @@ MySqlLeaseMgr::getLease6(const isc::asiolink::IOAddress& addr) const {
 
     // Set up the SELECT clause
     MySqlLease6Exchange exchange;
-    MYSQL_BIND* outbind = exchange.createBindForReceive();
+    std::vector<MYSQL_BIND> outbind;
+    exchange.createBindForReceive(outbind);
 
     // Bind the input parameters to the statement
     int status = mysql_stmt_bind_param(statements_[GET_LEASE6], inbind);
     checkError(status, GET_LEASE6, "unable to bind WHERE clause parameter");
 
     // Bind the output parameters to the statement
-    status = mysql_stmt_bind_result(statements_[GET_LEASE6], outbind);
+    status = mysql_stmt_bind_result(statements_[GET_LEASE6], &outbind[0]);
     checkError(status, GET_LEASE6, "unable to bind SELECT caluse parameters");
 
     // Execute the statement
@@ -691,9 +709,45 @@ MySqlLeaseMgr::updateLease4(const Lease4Ptr& /* lease4 */) {
 }
 
 void
-MySqlLeaseMgr::updateLease6(const Lease6Ptr& /* lease6 */) {
-    isc_throw(NotImplemented, "MySqlLeaseMgr::updateLease6(const Lease6Ptr&) "
-              "not implemented yet");
+MySqlLeaseMgr::updateLease6(const Lease6Ptr& lease) {
+    // Create the MYSQL_BIND array for the data being updated
+    MySqlLease6Exchange exchange;
+    std::vector<MYSQL_BIND> bind;
+    exchange.createBindForSend(lease, bind);
+
+    // Set up the WHERE clause value
+    MYSQL_BIND where;
+    memset(&where, 0, sizeof(where));
+
+    std::string addr6 = lease->addr_.toText();
+    unsigned long addr6_length = addr6.size();
+
+    where.buffer_type = MYSQL_TYPE_STRING;
+    where.buffer = const_cast<char*>(addr6.c_str());
+    where.buffer_length = addr6_length;
+    where.length = &addr6_length;
+    bind.push_back(where);
+
+    // Bind the parameters to the statement
+    int status = mysql_stmt_bind_param(statements_[UPDATE_LEASE6], &bind[0]);
+    checkError(status, UPDATE_LEASE6, "unable to bind parameters");
+
+    // Execute
+    status = mysql_stmt_execute(statements_[UPDATE_LEASE6]);
+    checkError(status, UPDATE_LEASE6, "unable to execute");
+
+    // See how many rows were affected.  The statement should only delete a
+    // single row.
+    int affected_rows = mysql_stmt_affected_rows(statements_[UPDATE_LEASE6]);
+    if (affected_rows == 0) {
+        isc_throw(NoSuchLease, "unable to update lease for address " <<
+                  addr6 << " as it does not exist");
+    } else if (affected_rows > 1) {
+        // Should not happen - primary key constraint should only have selected
+        // one row.
+        isc_throw(DbOperationError, "apparently updated more than one lease "
+                  "that had the address " << addr6);
+    }
 }
 
 bool

+ 3 - 1
src/lib/dhcp/mysql_lease_mgr.h

@@ -208,7 +208,8 @@ public:
     ///
     /// @param lease4 The lease to be updated.
     ///
-    /// If no such lease is present, an exception will be thrown.
+    /// @exception NoSuchLease Attempt to update lease that did not exist.
+    /// @exception DbOperationError Update operation updated multiple leases.
     virtual void updateLease6(const Lease6Ptr& lease6);
 
     /// @brief Deletes a lease.
@@ -334,6 +335,7 @@ private:
         GET_LEASE6,         // Get lease 6 by address
         GET_VERSION,        // Obtain version number
         INSERT_LEASE6,      // Add entry to lease6 table
+        UPDATE_LEASE6,      // Update a Lease6 entry
         NUM_STATEMENTS      // Number of statements
     };
 

+ 222 - 123
src/lib/dhcp/tests/mysql_lease_mgr_unittest.cc

@@ -30,6 +30,11 @@ using namespace std;
 
 namespace {
 
+// IPv6 addresseses
+const char* ADDRESS_1 = "2001:db8::1";
+const char* ADDRESS_2 = "2001:db8::2";
+const char* ADDRESS_3 = "2001:db8::3";
+
 // Connection strings.  Assume:
 // Database: keatest
 // Username: keatest
@@ -93,41 +98,36 @@ validConnectionString() {
                              VALID_USER, VALID_PASSWORD));
 }
 
-// Clear everything from the database tables
-void
-clearAll() {
-        // Initialise
-        MYSQL handle;
-        (void) mysql_init(&handle);
-
-        // Open database
-        (void) mysql_real_connect(&handle, "localhost", "keatest", "keatest",
-                                  "keatest", 0, NULL, 0);
-
-        // Clear the database
-        (void) mysql_query(&handle, "DELETE FROM lease4");
-        (void) mysql_query(&handle, "DELETE FROM lease6");
-
-        // ... and close
-        (void) mysql_close(&handle);
-}
-
-/// @brief Test Fixture Class
-///
-/// Opens the database prior to each test and closes it afterwards.
-/// All pending transactions are deleted prior to closure.
+// @brief Test Fixture Class
+//
+// Opens the database prior to each test and closes it afterwards.
+// All pending transactions are deleted prior to closure.
 
 class MySqlLeaseMgrTest : public ::testing::Test {
 public:
     /// @brief Constructor
     ///
     /// Deletes everything from the database and opens it.
-    MySqlLeaseMgrTest() {
+    MySqlLeaseMgrTest() : L1_ADDRESS(ADDRESS_1), L2_ADDRESS(ADDRESS_2),
+        L3_ADDRESS(ADDRESS_3), L1_IOADDRESS(L1_ADDRESS),
+        L2_IOADDRESS(L2_ADDRESS), L3_IOADDRESS(L3_ADDRESS)
+    {
         clearAll();
         LeaseMgrFactory::create(validConnectionString());
         lmptr_ = &(LeaseMgrFactory::instance());
     }
 
+    /// @brief Destructor
+    ///
+    /// Rolls back all pending transactions.  The deletion of the
+    /// lmptr_ member variable will close the database.  Then
+    /// reopen it and delete everything created by the test.
+    virtual ~MySqlLeaseMgrTest() {
+        lmptr_->rollback();
+        LeaseMgrFactory::destroy();
+        clearAll();
+    }
+
     /// @brief Reopen the database
     ///
     /// Closes the database and re-open it.  Anything committed should be
@@ -138,27 +138,126 @@ public:
         lmptr_ = &(LeaseMgrFactory::instance());
     }
 
-    /// @brief Destructor
+    /// @brief Clear everything from the database tables
     ///
-    /// Rolls back all pending transactions.  The deletion of the
-    /// lmptr_ member variable will close the database.  Then
-    /// reopen it and delete everything created by the test.
-    virtual ~MySqlLeaseMgrTest() {
-        lmptr_->rollback();
-        LeaseMgrFactory::destroy();
-        clearAll();
+    /// There is no error checking in this code, as this is just
+    /// extra checking that the database is clear before the text.
+    void
+    clearAll() {
+            // Initialise
+            MYSQL handle;
+            (void) mysql_init(&handle);
+
+            // Open database
+            (void) mysql_real_connect(&handle, "localhost", "keatest",
+                                      "keatest", "keatest", 0, NULL,
+                                      0);
+
+            // Clear the database
+            (void) mysql_query(&handle, "DELETE FROM lease4");
+            (void) mysql_query(&handle, "DELETE FROM lease6");
+
+            // ... and close
+            (void) mysql_close(&handle);
     }
 
+    // @brief Initialize Lease6 Fields
+    //
+    // Returns a pointer to a Lease6 structure.  Different values are put
+    // in the lease according to the address passed.
+    //
+    // This is just a convenience function for the test methods.
+    //
+    // @param address Address to use for the initialization
+    //
+    // @return Lease6Ptr.  This will not point to anything if the initialization
+    //         failed (e.g. unknown address).
+
+    Lease6Ptr initializeLease6(std::string address) {
+        Lease6Ptr lease(new Lease6());
+
+        // Set the address of the lease
+        lease->addr_ = IOAddress(address);
+
+        // Initialize unused fields.
+        lease->t1_ = 0;                             // Not saved
+        lease->t2_ = 0;                             // Not saved
+        lease->fixed_ = false;                      // Unused
+        lease->hostname_ = std::string("");         // Unused
+        lease->fqdn_fwd_ = false;                   // Unused
+        lease->fqdn_rev_ = false;                   // Unused
+        lease->comments_ = std::string("");         // Unused
+
+        // Set the other parameters
+        if (address == L1_ADDRESS) {
+            lease->type_ = Lease6::LEASE_IA_TA;
+            lease->prefixlen_ = 0;
+            lease->iaid_ = 42;
+            lease->duid_ = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0x42)));
+            lease->preferred_lft_ = 3600;  // Preferred lifetime
+            lease->valid_lft_ = 3677;      // Actual lifetime
+            lease->cltt_ = 123456;         // Current time of day
+            lease->subnet_id_ = 73;        // Arbitrary number
+
+        } else if (address == L2_ADDRESS) {
+            lease->type_ = Lease6::LEASE_IA_PD;
+            lease->prefixlen_ = 7;
+            lease->iaid_ = 89;
+            lease->duid_ = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0x3a)));
+            lease->preferred_lft_ = 1800;  // Preferred lifetime
+            lease->valid_lft_ = 5412;      // Actual lifetime
+            lease->cltt_ = 234567;         // Current time of day
+            lease->subnet_id_ = 73;        // Same as for L1_ADDRESS
+
+        
+        } else if (address == L3_ADDRESS) {
+            lease->type_ = Lease6::LEASE_IA_NA;
+            lease->prefixlen_ = 28;
+            lease->iaid_ = 0xfffffffe;
+            vector<uint8_t> duid;
+            for (uint8_t i = 0; i < 128; ++i) {
+                duid.push_back(i + 5);
+            }
+            lease->duid_ = boost::shared_ptr<DUID>(new DUID(duid));
+
+            // The times used in the next tests are deliberately restricted - we
+            // should be able to cope with valid lifetimes up to 0xffffffff.
+            //  However, this will lead to overflows.
+            // @TODO: test overflow conditions when code has been fixed
+            lease->preferred_lft_ = 7200;  // Preferred lifetime
+            lease->valid_lft_ = 7000;      // Actual lifetime
+            lease->cltt_ = 234567;         // Current time of day
+            lease->subnet_id_ = 37;        // Different from L1 and L2
+
+        } else {
+            // Unknown address, return an empty pointer.
+            lease.reset();
+
+        }
+
+        return (lease);
+    }
+
+    // Member variables
+
     LeaseMgr*   lmptr_;         // Pointer to the lease manager
+
+    string L1_ADDRESS;          // String form of address 1
+    string L2_ADDRESS;          // String form of address 2
+    string L3_ADDRESS;          // String form of address 3
+
+    IOAddress L1_IOADDRESS;     // IOAddress form of L1_ADDRESS
+    IOAddress L2_IOADDRESS;     // IOAddress form of L2_ADDRESS
+    IOAddress L3_IOADDRESS;     // IOAddress form of L3_ADDRESS
 };
 
 
-/// @brief Check that Database Can Be Opened
-///
-/// This test checks if the MySqlLeaseMgr can be instantiated.  This happens
-/// only if the database can be opened.  Note that this is not part of the
-/// MySqlLeaseMgr test fixure set.  This test checks that the database can be
-/// opened: the fixtures assume that and check basic operations.
+// @brief Check that Database Can Be Opened
+//
+// This test checks if the MySqlLeaseMgr can be instantiated.  This happens
+// only if the database can be opened.  Note that this is not part of the
+// MySqlLeaseMgr test fixure set.  This test checks that the database can be
+// opened: the fixtures assume that and check basic operations.
 
 TEST(MySqlOpenTest, OpenDatabase) {
     // Check that database opens correctly and tidy up.  If it fails, print
@@ -208,7 +307,7 @@ TEST(MySqlOpenTest, OpenDatabase) {
         NoDatabaseName);
 }
 
-/// @brief Check conversion functions
+// @brief Check conversion functions
 TEST_F(MySqlLeaseMgrTest, CheckTimeConversion) {
     const time_t cltt = time(NULL);
     const uint32_t valid_lft = 86400;       // 1 day
@@ -239,7 +338,15 @@ TEST_F(MySqlLeaseMgrTest, CheckTimeConversion) {
     EXPECT_EQ(cltt, converted_cltt);
 }
 
-/// @brief Check that getVersion() works
+
+// @brief Check getName() returns correct database name
+TEST_F(MySqlLeaseMgrTest, getName) {
+    EXPECT_EQ(std::string("keatest"), lmptr_->getName());
+
+    // @TODO: check for the negative
+}
+
+// @brief Check that getVersion() works
 TEST_F(MySqlLeaseMgrTest, CheckVersion) {
     // Check version
     pair<uint32_t, uint32_t> version;
@@ -265,81 +372,24 @@ detailCompareLease6(const Lease6Ptr& first, const Lease6Ptr& second) {
     EXPECT_EQ(first->subnet_id_, second->subnet_id_);
 }
 
-/// @brief Initialize Lease
-///
-/// Initializes the unused fields in a lease to known values for
-/// testing purposes.
-void initializeUnusedLease6(Lease6Ptr& lease) {
-    lease->t1_ = 0;                             // Not saved
-    lease->t2_ = 0;                             // Not saved
-    lease->fixed_ = false;                      // Unused
-    lease->hostname_ = std::string("");         // Unused
-    lease->fqdn_fwd_ = false;                   // Unused
-    lease->fqdn_rev_ = false;                   // Unused
-    lease->comments_ = std::string("");         // Unused
-}
 
-/// @brief Check individual Lease6 methods
-///
-/// Checks that the add/update/delete works.  All are done within one
-/// test so that "rollback" can be used to remove trace of the tests
-/// from the database.
-///
-/// Tests where a collection of leases can be returned are in the test
-/// Lease6Collection.
+// @brief Check individual Lease6 methods
+//
+// Checks that the add/update/delete works.  All are done within one
+// test so that "rollback" can be used to remove trace of the tests
+// from the database.
+//
+// Tests where a collection of leases can be returned are in the test
+// Lease6Collection.
 TEST_F(MySqlLeaseMgrTest, BasicLease6) {
 
     // Define the leases being used for testing.
-    const IOAddress L1_ADDRESS(std::string("2001:db8::1"));
-    Lease6Ptr l1(new Lease6());
-    initializeUnusedLease6(l1);
-
-    l1->type_ = Lease6::LEASE_IA_TA;
-    l1->addr_ = L1_ADDRESS;
-    l1->prefixlen_ = 0;
-    l1->iaid_ = 42;
-    l1->duid_ = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0x42)));
-    l1->preferred_lft_ = 3600;  // Preferred lifetime
-    l1->valid_lft_ = 3677;      // Actual lifetime
-    l1->cltt_ = 123456;         // Current time of day
-    l1->subnet_id_ = 73;        // Arbitrary number
-
-    const IOAddress L2_ADDRESS(std::string("2001:db8::2"));
-    Lease6Ptr l2(new Lease6());
-    initializeUnusedLease6(l2);
-
-    l2->type_ = Lease6::LEASE_IA_PD;
-    l2->addr_ = L2_ADDRESS;
-    l2->prefixlen_ = 7;
-    l2->iaid_ = 89;
-    l2->duid_ = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0x3a)));
-    l2->preferred_lft_ = 1800;  // Preferred lifetime
-    l2->valid_lft_ = 5412;      // Actual lifetime
-    l2->cltt_ = 234567;         // Current time of day
-    l2->subnet_id_ = l1->subnet_id_;    // Same as l1
-
-    const IOAddress L3_ADDRESS(std::string("2001:db8::3"));
-    Lease6Ptr l3(new Lease6());
-    initializeUnusedLease6(l3);
-
-    l3->type_ = Lease6::LEASE_IA_NA;
-    l3->addr_ = L3_ADDRESS;
-    l3->prefixlen_ = 28;
-    l3->iaid_ = 0xfffffffe;
-    vector<uint8_t> duid;
-    for (uint8_t i = 0; i < 128; ++i) {
-        duid.push_back(i + 5);
-    }
-    l3->duid_ = boost::shared_ptr<DUID>(new DUID(duid));
-
-    // The times used in the next tests are deliberately restricted - we should
-    // be avle to cope with valid lifetimes up to 0xffffffff.  However, this
-    // will lead to overflows.
-    // @TODO: test overflow conditions when code has been fixed
-    l3->preferred_lft_ = 7200;          // Preferred lifetime
-    l3->valid_lft_ = 7000;              // Actual lifetime
-    l3->cltt_ = 234567;                 // Current time of day
-    l3->subnet_id_ = l1->subnet_id_;    // Same as l1
+    Lease6Ptr l1 = initializeLease6(L1_ADDRESS);
+    ASSERT_TRUE(l1);
+    Lease6Ptr l2 = initializeLease6(L2_ADDRESS);
+    ASSERT_TRUE(l2);
+    Lease6Ptr l3 = initializeLease6(L3_ADDRESS);
+    ASSERT_TRUE(l3);
 
     // Sanity check that the leases are different
     ASSERT_TRUE(*l1 != *l2);
@@ -358,16 +408,15 @@ TEST_F(MySqlLeaseMgrTest, BasicLease6) {
     // Reopen the database to ensure that they actually got stored.
     reopen();
 
-    // check that the values returned are as expected.
-    l_returned = lmptr_->getLease6(L1_ADDRESS);
+    l_returned = lmptr_->getLease6(L1_IOADDRESS);
     EXPECT_TRUE(l_returned);
     detailCompareLease6(l1, l_returned);
 
-    l_returned = lmptr_->getLease6(L2_ADDRESS);
+    l_returned = lmptr_->getLease6(L2_IOADDRESS);
     EXPECT_TRUE(l_returned);
     detailCompareLease6(l2, l_returned);
 
-    l_returned = lmptr_->getLease6(L3_ADDRESS);
+    l_returned = lmptr_->getLease6(L3_IOADDRESS);
     EXPECT_TRUE(l_returned);
     detailCompareLease6(l3, l_returned);
 
@@ -376,22 +425,72 @@ TEST_F(MySqlLeaseMgrTest, BasicLease6) {
 
     // Delete a lease, check that it's gone, and that we can't delete it
     // a second time.
-    EXPECT_TRUE(lmptr_->deleteLease6(L1_ADDRESS));
-    l_returned = lmptr_->getLease6(L1_ADDRESS);
+    EXPECT_TRUE(lmptr_->deleteLease6(L1_IOADDRESS));
+    l_returned = lmptr_->getLease6(L1_IOADDRESS);
     EXPECT_FALSE(l_returned);
-    EXPECT_FALSE(lmptr_->deleteLease6(L1_ADDRESS));
+    EXPECT_FALSE(lmptr_->deleteLease6(L1_IOADDRESS));
 
     // Check that the second address is still there.
-    l_returned = lmptr_->getLease6(L2_ADDRESS);
+    l_returned = lmptr_->getLease6(L2_IOADDRESS);
     EXPECT_TRUE(l_returned);
     detailCompareLease6(l2, l_returned);
 }
 
-/// @brief Check getName() returns correct database name
-TEST_F(MySqlLeaseMgrTest, getName) {
-    EXPECT_EQ(std::string("keatest"), lmptr_->getName());
+// @brief Lease6 Update Tests
+//
+// Checks that we are able to update a lease in the database.
+TEST_F(MySqlLeaseMgrTest, UpdateLease6) {
 
-    // @TODO: check for the negative
+    // Define the leases being used for testing.
+    Lease6Ptr l1 = initializeLease6(L1_ADDRESS);
+    ASSERT_TRUE(l1);
+
+    // Add a lease to the database and check that the lease is there.
+    EXPECT_TRUE(lmptr_->addLease(l1));
+    lmptr_->commit();
+
+    reopen();
+    Lease6Ptr l_returned = lmptr_->getLease6(L1_IOADDRESS);
+    EXPECT_TRUE(l_returned);
+    detailCompareLease6(l1, l_returned);
+
+    // Modify some fields in lease 1 (not the address) and update it.
+    ++l1->iaid_;
+    l1->type_ = Lease6::LEASE_IA_PD;
+    l1->valid_lft_ *= 2;
+    lmptr_->updateLease6(l1);
+    lmptr_->commit();
+    reopen();
+
+    // ... and check what is returned is what is expected.
+    l_returned.reset();
+    l_returned = lmptr_->getLease6(L1_IOADDRESS);
+    EXPECT_TRUE(l_returned);
+    detailCompareLease6(l1, l_returned);
+
+    // Alter the lease again and check.
+    ++l1->iaid_;
+    l1->type_ = Lease6::LEASE_IA_TA;
+    l1->cltt_ += 6;
+    l1->prefixlen_ = 93;
+    lmptr_->updateLease6(l1);
+
+    l_returned.reset();
+    l_returned = lmptr_->getLease6(L1_IOADDRESS);
+    EXPECT_TRUE(l_returned);
+    detailCompareLease6(l1, l_returned);
+
+    // Check we can do an update without changing data.
+    lmptr_->updateLease6(l1);
+    l_returned.reset();
+    l_returned = lmptr_->getLease6(L1_IOADDRESS);
+    EXPECT_TRUE(l_returned);
+    detailCompareLease6(l1, l_returned);
+
+    // Try updating a non-existent lease.
+    Lease6Ptr l2 = initializeLease6(L2_ADDRESS);
+    ASSERT_TRUE(l2);
+    EXPECT_THROW(lmptr_->updateLease6(l2), isc::dhcp::NoSuchLease);
 }
 
 }; // end of anonymous namespace