]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[2342] Merge branch 'master' into trac2342
authorStephen Morris <stephen@isc.org>
Sun, 11 Nov 2012 22:55:41 +0000 (22:55 +0000)
committerStephen Morris <stephen@isc.org>
Sun, 11 Nov 2012 22:55:41 +0000 (22:55 +0000)
Conflicts:
doc/devel/mainpage.dox
src/lib/dhcp/Makefile.am
src/lib/dhcp/lease_mgr.cc
src/lib/dhcp/lease_mgr.h
src/lib/dhcp/tests/Makefile.am
src/lib/dhcp/tests/lease_mgr_unittest.cc

A number of other files were modified to resolve incompatibilities
in the way lease managers were created, and to promote the dummy
"memfile" lease manager to a fully-fledged lease manager with
unit tests.

20 files changed:
1  2 
configure.ac
doc/devel/mainpage.dox
src/bin/dhcp6/dhcp6_srv.cc
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
src/lib/dhcp/Makefile.am
src/lib/dhcp/addr_utilities.h
src/lib/dhcp/alloc_engine.cc
src/lib/dhcp/cfgmgr.h
src/lib/dhcp/dhcp6.h
src/lib/dhcp/iface_mgr.h
src/lib/dhcp/lease_mgr.cc
src/lib/dhcp/lease_mgr.h
src/lib/dhcp/lease_mgr_factory.cc
src/lib/dhcp/libdhcp++.h
src/lib/dhcp/memfile_lease_mgr.cc
src/lib/dhcp/memfile_lease_mgr.h
src/lib/dhcp/tests/Makefile.am
src/lib/dhcp/tests/alloc_engine_unittest.cc
src/lib/dhcp/tests/lease_mgr_unittest.cc
src/lib/dhcp/tests/memfile_lease_mgr_unittest.cc

diff --cc configure.ac
Simple merge
index a36d2270aa347cb588f5aef06bfc38a206a40b26,e1761ec74aa0010bcaa15e72d4f8bbd2360ad038..db42c147dbbc66ff3f02e123848fe88055f5044b
   * - @subpage libdhcp
   *   - @subpage libdhcpIntro
   *   - @subpage libdhcpIfaceMgr
+  * - @subpage libdhcpsrv
+  *   - @subpage leasemgr
+  *   - @subpage cfgmgr
+  *   - @subpage allocengine
 + * - @subpage dhcp-database-backends
   * - @subpage perfdhcpInternals
   *
   * @section misc Miscellaneous topics
index 7c57a6139cfaeaa591f4018a62a109011560f713,7b1f721aa4fc8bebc5dbb889ac9eba9acf8a6eb9..5f844a3d94786b47f3d36b131c06a8e57599a5c6
@@@ -12,6 -12,6 +12,8 @@@
  // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  // PERFORMANCE OF THIS SOFTWARE.
  
++#include <config.h>
++
  #include <stdlib.h>
  #include <time.h>
  
  #include <exceptions/exceptions.h>
  #include <util/io_utilities.h>
  #include <util/range_utilities.h>
+ #include <dhcp/duid.h>
+ #include <dhcp/lease_mgr.h>
++#include <dhcp/lease_mgr_factory.h>
+ #include <dhcp/cfgmgr.h>
+ #include <dhcp/option6_iaaddr.h>
+ // @todo: Replace this with MySQL_LeaseMgr (or a LeaseMgr factory)
+ // once it is merged
+ #include <dhcp/memfile_lease_mgr.h>
+ #include <boost/foreach.hpp>
  
  using namespace isc;
  using namespace isc::asiolink;
@@@ -73,11 -85,22 +88,25 @@@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port) : a
          return;
      }
  
-     shutdown_ = false;
+     // Instantiate LeaseMgr
+     // @todo: Replace this with MySQL_LeaseMgr (or a LeaseMgr factory)
+     // once it is merged
 -    new isc::dhcp::test::Memfile_LeaseMgr("");
 -
++#ifdef HAVE_MYSQL
++    LeaseMgrFactory::create("type=mysql user=kea password=kea name=kea host=localhost");
++#else
++    LeaseMgrFactory::create("type=memfile");
++#endif
+     LOG_INFO(dhcp6_logger, DHCP6_DB_BACKEND_STARTED)
 -        .arg(LeaseMgr::instance().getName());
++        .arg(LeaseMgrFactory::instance().getName());
+     // Instantiate allocation engine
+     alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));
  }
  
  Dhcpv6Srv::~Dhcpv6Srv() {
      IfaceMgr::instance().closeSockets();
 -    LeaseMgr::destroy_instance();
++    LeaseMgrFactory::destroy();
  }
  
  void Dhcpv6Srv::shutdown() {
index 20287066dabccea86a721618523eec586bd95421,f1cf7de098152129e2dc7ebb866f708b1c2f815e..afb05d188b06fa6b2ea1d8303d06b9e577a0f459
  // PERFORMANCE OF THIS SOFTWARE.
  
  #include <config.h>
- #include <iostream>
  #include <fstream>
+ #include <iostream>
  #include <sstream>
  
- #include <arpa/inet.h>
  #include <gtest/gtest.h>
  
+ #include <asiolink/io_address.h>
+ #include <boost/scoped_ptr.hpp>
+ #include <config/ccsession.h>
+ #include <dhcp/cfgmgr.h>
  #include <dhcp/dhcp6.h>
+ #include <dhcp/duid.h>
+ #include <dhcp/lease_mgr.h>
++#include <dhcp/lease_mgr_factory.h>
+ #include <dhcp/option.h>
+ #include <dhcp/option6_addrlst.h>
  #include <dhcp/option6_ia.h>
+ #include <dhcp/option6_iaaddr.h>
+ #include <dhcp/option6_int_array.h>
+ #include <dhcp6/config_parser.h>
  #include <dhcp6/dhcp6_srv.h>
  #include <util/buffer.h>
  #include <util/range_utilities.h>
@@@ -54,12 -63,144 +64,144 @@@ public
  class Dhcpv6SrvTest : public ::testing::Test {
  public:
      // these are empty for now, but let's keep them around
-     Dhcpv6SrvTest() {
+     Dhcpv6SrvTest() : rcode_(-1) {
+         subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 48, 1000,
+                                          2000, 3000, 4000));
+         pool_ = Pool6Ptr(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1:1::"), 64));
+         subnet_->addPool6(pool_);
+         CfgMgr::instance().addSubnet6(subnet_);
+     }
+     // Generate IA_NA option with specified parameters
+     boost::shared_ptr<Option6IA> generateIA(uint32_t iaid, uint32_t t1, uint32_t t2) {
+         boost::shared_ptr<Option6IA> ia =
+             boost::shared_ptr<Option6IA>(new Option6IA(D6O_IA_NA, iaid));
+         ia->setT1(t1);
+         ia->setT2(t2);
+         return (ia);
+     }
+     // Generate client-id option
+     OptionPtr generateClientId(size_t duid_size = 32) {
+         OptionBuffer clnt_duid(duid_size);
+         for (int i = 0; i < duid_size; i++) {
+             clnt_duid[i] = 100 + i;
+         }
+         duid_ = DuidPtr(new DUID(clnt_duid));
+         return (OptionPtr(new Option(Option::V6, D6O_CLIENTID,
+                                      clnt_duid.begin(),
+                                      clnt_duid.begin() + duid_size)));
+     }
+     // Checks if server response (ADVERTISE or REPLY) includes proper server-id.
+     void checkServerId(const Pkt6Ptr& rsp, const OptionPtr& expected_srvid) {
+         // check that server included its server-id
+         OptionPtr tmp = rsp->getOption(D6O_SERVERID);
+         EXPECT_EQ(tmp->getType(), expected_srvid->getType() );
+         ASSERT_EQ(tmp->len(), expected_srvid->len() );
+         EXPECT_TRUE(tmp->getData() == expected_srvid->getData());
+     }
+     // Checks if server response (ADVERTISE or REPLY) includes proper client-id.
+     void checkClientId(const Pkt6Ptr& rsp, const OptionPtr& expected_clientid) {
+         // check that server included our own client-id
+         OptionPtr tmp = rsp->getOption(D6O_CLIENTID);
+         ASSERT_TRUE(tmp);
+         EXPECT_EQ(expected_clientid->getType(), tmp->getType());
+         ASSERT_EQ(expected_clientid->len(), tmp->len());
+         // check that returned client-id is valid
+         EXPECT_TRUE(expected_clientid->getData() == tmp->getData());
+     }
+     // Checks that server response (ADVERTISE or REPLY) contains proper IA_NA option
+     // It returns IAADDR option for each chaining with checkIAAddr method.
+     boost::shared_ptr<Option6IAAddr> checkIA_NA(const Pkt6Ptr& rsp, uint32_t expected_iaid,
+                                          uint32_t expected_t1, uint32_t expected_t2) {
+         OptionPtr tmp = rsp->getOption(D6O_IA_NA);
+         // Can't use ASSERT_TRUE() in method that returns something
+         if (!tmp) {
+             ADD_FAILURE() << "IA_NA option not present in response";
+             return (boost::shared_ptr<Option6IAAddr>());
+         }
+         boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
+         EXPECT_EQ(expected_iaid, ia->getIAID() );
+         EXPECT_EQ(expected_t1, ia->getT1());
+         EXPECT_EQ(expected_t2, ia->getT2());
+         tmp = ia->getOption(D6O_IAADDR);
+         boost::shared_ptr<Option6IAAddr> addr = boost::dynamic_pointer_cast<Option6IAAddr>(tmp);
+         return (addr);
+     }
+     // Check that generated IAADDR option contains expected address.
+     void checkIAAddr(const boost::shared_ptr<Option6IAAddr>& addr,
+                      const IOAddress& expected_addr,
+                      uint32_t expected_preferred, uint32_t expected_valid) {
+         // Check that the assigned address is indeed from the configured pool.
+         // Note that when comparing addresses, we compare the textual
+         // representation. IOAddress does not support being streamed to
+         // an ostream, which means it can't be used in EXPECT_EQ.
+         EXPECT_TRUE(subnet_->inPool(addr->getAddress()));
+         EXPECT_EQ(expected_addr.toText(), addr->getAddress().toText());
+         EXPECT_EQ(addr->getPreferred(), subnet_->getPreferred());
+         EXPECT_EQ(addr->getValid(), subnet_->getValid());
+     }
+     // Basic checks for generated response (message type and transaction-id).
+     void checkResponse(const Pkt6Ptr& rsp, uint8_t expected_message_type,
+                        uint32_t expected_transid) {
+         ASSERT_TRUE(rsp);
+         EXPECT_EQ(expected_message_type, rsp->getType());
+         EXPECT_EQ(expected_transid, rsp->getTransid());
+     }
+     // Checks if the lease sent to client is present in the database
+     Lease6Ptr checkLease(const DuidPtr& duid, const OptionPtr& ia_na,
+                          boost::shared_ptr<Option6IAAddr> addr) {
+         boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(ia_na);
 -        Lease6Ptr lease = LeaseMgr::instance().getLease6(addr->getAddress());
++        Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(addr->getAddress());
+         if (!lease) {
+             cout << "Lease for " << addr->getAddress().toText()
+                  << " not found in the database backend.";
+             return (Lease6Ptr());
+         }
+         EXPECT_EQ(addr->getAddress().toText(), lease->addr_.toText());
+         EXPECT_TRUE(*lease->duid_ == *duid);
+         EXPECT_EQ(ia->getIAID(), lease->iaid_);
+         EXPECT_EQ(subnet_->getID(), lease->subnet_id_);
+         return (lease);
      }
      ~Dhcpv6SrvTest() {
+         CfgMgr::instance().deleteSubnets6();
      };
+     // A subnet used in most tests
+     Subnet6Ptr subnet_;
+     // A pool used in most tests
+     Pool6Ptr pool_;
+     // A DUID used in most tests (typically as client-id)
+     DuidPtr duid_;
+     int rcode_;
+     ConstElementPtr comment_;
  };
  
+ // Test verifies that the Dhcpv6_srv class can be instantiated. It checks a mode
+ // without open sockets and with sockets opened on a high port (to not require
+ // root privileges).
  TEST_F(Dhcpv6SrvTest, basic) {
      // srv has stubbed interface detection. It will read
      // interfaces.txt instead. It will pretend to have detected
@@@ -150,79 -295,460 +296,460 @@@ TEST_F(Dhcpv6SrvTest, DUID) 
      }
  }
  
- TEST_F(Dhcpv6SrvTest, Solicit_basic) {
+ // This test checks if Option Request Option (ORO) is parsed correctly
+ // and the requested options are actually assigned.
+ TEST_F(Dhcpv6SrvTest, advertiseOptions) {
+     ConstElementPtr x;
+     string config = "{ \"interface\": [ \"all\" ],"
+         "\"preferred-lifetime\": 3000,"
+         "\"rebind-timer\": 2000, "
+         "\"renew-timer\": 1000, "
+         "\"subnet6\": [ { "
+         "    \"pool\": [ \"2001:db8:1::/64\" ],"
+         "    \"subnet\": \"2001:db8:1::/48\", "
+         "    \"option-data\": [ {"
+         "          \"name\": \"OPTION_DNS_SERVERS\","
+         "          \"code\": 23,"
+         "          \"data\": \"2001 0DB8 1234 FFFF 0000 0000 0000 0001"
+         "2001 0DB8 1234 FFFF 0000 0000 0000 0002\""
+         "        },"
+         "        {"
+         "          \"name\": \"OPTION_FOO\","
+         "          \"code\": 1000,"
+         "          \"data\": \"1234\""
+         "        } ]"
+         " } ],"
+         "\"valid-lifetime\": 4000 }";
+     ElementPtr json = Element::fromJSON(config);
      boost::scoped_ptr<NakedDhcpv6Srv> srv;
-     ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv()) );
+     ASSERT_NO_THROW(srv.reset(new NakedDhcpv6Srv(0)));
  
-     // a dummy content for client-id
-     OptionBuffer clntDuid(32);
-     for (int i = 0; i < 32; i++) {
-         clntDuid[i] = 100 + i;
-     }
+     EXPECT_NO_THROW(x = configureDhcp6Server(*srv, json));
+     ASSERT_TRUE(x);
+     comment_ = parseAnswer(rcode_, x);
+     ASSERT_EQ(0, rcode_);
  
      Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
+     sol->setRemoteAddr(IOAddress("fe80::abcd"));
+     sol->addOption(generateIA(234, 1500, 3000));
+     OptionPtr clientid = generateClientId();
+     sol->addOption(clientid);
+     // Pass it to the server and get an advertise
+     boost::shared_ptr<Pkt6> adv = srv->processSolicit(sol);
+     // check if we get response at all
+     ASSERT_TRUE(adv);
+     // We have not requested option with code 1000 so it should not
+     // be included in the response.
+     ASSERT_FALSE(adv->getOption(1000));
+     ASSERT_FALSE(adv->getOption(D6O_NAME_SERVERS));
+     // Let's now request option with code 1000.
+     // We expect that server will include this option in its reply.
+     boost::shared_ptr<Option6IntArray<uint16_t> >
+         option_oro(new Option6IntArray<uint16_t>(D6O_ORO));
+     // Create vector with two option codes.
+     std::vector<uint16_t> codes(2);
+     codes[0] = 1000;
+     codes[1] = D6O_NAME_SERVERS;
+     // Pass this code to option.
+     option_oro->setValues(codes);
+     // Append ORO to SOLICIT message.
+     sol->addOption(option_oro);
+     // Need to process SOLICIT again after requesting new option.
+     adv = srv->processSolicit(sol);
+     ASSERT_TRUE(adv);
+     OptionPtr tmp = adv->getOption(D6O_NAME_SERVERS);
+     ASSERT_TRUE(tmp);
+     boost::shared_ptr<Option6AddrLst> reply_nameservers =
+         boost::dynamic_pointer_cast<Option6AddrLst>(tmp);
+     ASSERT_TRUE(reply_nameservers);
+     Option6AddrLst::AddressContainer addrs = reply_nameservers->getAddresses();
+     ASSERT_EQ(2, addrs.size());
+     EXPECT_TRUE(addrs[0] == IOAddress("2001:db8:1234:FFFF::1"));
+     EXPECT_TRUE(addrs[1] == IOAddress("2001:db8:1234:FFFF::2"));
+     // There is a dummy option with code 1000 we requested from a server.
+     // Expect that this option is in server's response.
+     tmp = adv->getOption(1000);
+     ASSERT_TRUE(tmp);
+     // Check that the option contains valid data (from configuration).
+     std::vector<uint8_t> data = tmp->getData();
+     ASSERT_EQ(2, data.size());
+     const uint8_t foo_expected[] = {
+         0x12, 0x34
+     };
+     EXPECT_EQ(0, memcmp(&data[0], foo_expected, 2));
+     // more checks to be implemented
+ }
+ // There are no dedicated tests for Dhcpv6Srv::handleIA_NA and Dhcpv6Srv::assignLeases
+ // as they are indirectly tested in Solicit and Request tests.
+ // This test verifies that incoming SOLICIT can be handled properly, that an
+ // ADVERTISE is generated, that the response has an address and that address
+ // really belongs to the configured pool.
+ //
+ // This test sends a SOLICIT without any hint in IA_NA.
+ //
+ // constructed very simple SOLICIT message with:
+ // - client-id option (mandatory)
+ // - IA option (a request for address, without any addresses)
+ //
+ // expected returned ADVERTISE message:
+ // - copy of client-id
+ // - server-id
+ // - IA that includes IAADDR
+ TEST_F(Dhcpv6SrvTest, SolicitBasic) {
+     boost::scoped_ptr<NakedDhcpv6Srv> srv;
+     ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) );
+     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
+     sol->setRemoteAddr(IOAddress("fe80::abcd"));
+     sol->addOption(generateIA(234, 1500, 3000));
+     OptionPtr clientid = generateClientId();
+     sol->addOption(clientid);
+     // Pass it to the server and get an advertise
+     Pkt6Ptr reply = srv->processSolicit(sol);
+     // check if we get response at all
+     checkResponse(reply, DHCPV6_ADVERTISE, 1234);
  
-     boost::shared_ptr<Option6IA> ia =
-         boost::shared_ptr<Option6IA>(new Option6IA(D6O_IA_NA, 234));
-     ia->setT1(1501);
-     ia->setT2(2601);
+     // check that IA_NA was returned and that there's an address included
+     boost::shared_ptr<Option6IAAddr> addr = checkIA_NA(reply, 234, subnet_->getT1(),
+                                                 subnet_->getT2());
+     // Check that the assigned address is indeed from the configured pool
+     checkIAAddr(addr, addr->getAddress(), subnet_->getPreferred(), subnet_->getValid());
+     // check DUIDs
+     checkServerId(reply, srv->getServerID());
+     checkClientId(reply, clientid);
+ }
+ // This test verifies that incoming SOLICIT can be handled properly, that an
+ // ADVERTISE is generated, that the response has an address and that address
+ // really belongs to the configured pool.
+ //
+ // This test sends a SOLICIT with IA_NA that contains a valid hint.
+ //
+ // constructed very simple SOLICIT message with:
+ // - client-id option (mandatory)
+ // - IA option (a request for address, with an address that belongs to the
+ //              configured pool, i.e. is valid as hint)
+ //
+ // expected returned ADVERTISE message:
+ // - copy of client-id
+ // - server-id
+ // - IA that includes IAADDR
+ TEST_F(Dhcpv6SrvTest, SolicitHint) {
+     boost::scoped_ptr<NakedDhcpv6Srv> srv;
+     ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) );
+     // Let's create a SOLICIT
+     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
+     sol->setRemoteAddr(IOAddress("fe80::abcd"));
+     boost::shared_ptr<Option6IA> ia = generateIA(234, 1500, 3000);
+     // with a valid hint
+     IOAddress hint("2001:db8:1:1::dead:beef");
+     ASSERT_TRUE(subnet_->inPool(hint));
+     OptionPtr hint_opt(new Option6IAAddr(D6O_IAADDR, hint, 300, 500));
+     ia->addOption(hint_opt);
      sol->addOption(ia);
+     OptionPtr clientid = generateClientId();
+     sol->addOption(clientid);
+     // Pass it to the server and get an advertise
+     Pkt6Ptr reply = srv->processSolicit(sol);
  
-     // Let's not send address in solicit yet
-     // boost::shared_ptr<Option6IAAddr> addr(new Option6IAAddr(D6O_IAADDR,
-     //    IOAddress("2001:db8:1234:ffff::ffff"), 5001, 7001));
-     // ia->addOption(addr);
-     // sol->addOption(ia);
+     // check if we get response at all
+     checkResponse(reply, DHCPV6_ADVERTISE, 1234);
+     OptionPtr tmp = reply->getOption(D6O_IA_NA);
+     ASSERT_TRUE(tmp);
  
-     // constructed very simple SOLICIT message with:
-     // - client-id option (mandatory)
-     // - IA option (a request for address, without any addresses)
+     // check that IA_NA was returned and that there's an address included
+     boost::shared_ptr<Option6IAAddr> addr = checkIA_NA(reply, 234, subnet_->getT1(),
+                                                 subnet_->getT2());
  
-     // expected returned ADVERTISE message:
-     // - copy of client-id
-     // - server-id
-     // - IA that includes IAADDR
+     // check that we've got the address we requested
+     checkIAAddr(addr, hint, subnet_->getPreferred(), subnet_->getValid());
  
-     OptionPtr clientid = OptionPtr(new Option(Option::V6, D6O_CLIENTID,
-                                               clntDuid.begin(),
-                                               clntDuid.begin() + 16));
+     // check DUIDs
+     checkServerId(reply, srv->getServerID());
+     checkClientId(reply, clientid);
+ }
+ // This test verifies that incoming SOLICIT can be handled properly, that an
+ // ADVERTISE is generated, that the response has an address and that address
+ // really belongs to the configured pool.
+ //
+ // This test sends a SOLICIT with IA_NA that contains an invalid hint.
+ //
+ // constructed very simple SOLICIT message with:
+ // - client-id option (mandatory)
+ // - IA option (a request for address, with an address that does not
+ //              belong to the configured pool, i.e. is valid as hint)
+ //
+ // expected returned ADVERTISE message:
+ // - copy of client-id
+ // - server-id
+ // - IA that includes IAADDR
+ TEST_F(Dhcpv6SrvTest, SolicitInvalidHint) {
+     boost::scoped_ptr<NakedDhcpv6Srv> srv;
+     ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) );
+     // Let's create a SOLICIT
+     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
+     sol->setRemoteAddr(IOAddress("fe80::abcd"));
+     boost::shared_ptr<Option6IA> ia = generateIA(234, 1500, 3000);
+     IOAddress hint("2001:db8:1::cafe:babe");
+     ASSERT_FALSE(subnet_->inPool(hint));
+     OptionPtr hint_opt(new Option6IAAddr(D6O_IAADDR, hint, 300, 500));
+     ia->addOption(hint_opt);
+     sol->addOption(ia);
+     OptionPtr clientid = generateClientId();
      sol->addOption(clientid);
  
-     boost::shared_ptr<Pkt6> reply = srv->processSolicit(sol);
+     // Pass it to the server and get an advertise
+     Pkt6Ptr reply = srv->processSolicit(sol);
+     // check if we get response at all
+     checkResponse(reply, DHCPV6_ADVERTISE, 1234);
+     // check that IA_NA was returned and that there's an address included
+     boost::shared_ptr<Option6IAAddr> addr = checkIA_NA(reply, 234, subnet_->getT1(),
+                                                 subnet_->getT2());
+     // Check that the assigned address is indeed from the configured pool
+     checkIAAddr(addr, addr->getAddress(), subnet_->getPreferred(), subnet_->getValid());
+     EXPECT_TRUE(subnet_->inPool(addr->getAddress()));
+     // check DUIDs
+     checkServerId(reply, srv->getServerID());
+     checkClientId(reply, clientid);
+ }
+ // This test checks that the server is offering different addresses to different
+ // clients in ADVERTISEs. Please note that ADVERTISE is not a guarantee that such
+ // and address will be assigned. Had the pool was very small and contained only
+ // 2 addresses, the third client would get the same advertise as the first one
+ // and this is a correct behavior. It is REQUEST that will fail for the third
+ // client. ADVERTISE is basically saying "if you send me a request, you will
+ // probably get an address like this" (there are no guarantees).
+ TEST_F(Dhcpv6SrvTest, ManySolicits) {
+     boost::scoped_ptr<NakedDhcpv6Srv> srv;
+     ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) );
+     Pkt6Ptr sol1 = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
+     Pkt6Ptr sol2 = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 2345));
+     Pkt6Ptr sol3 = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 3456));
+     sol1->setRemoteAddr(IOAddress("fe80::abcd"));
+     sol2->setRemoteAddr(IOAddress("fe80::1223"));
+     sol3->setRemoteAddr(IOAddress("fe80::3467"));
+     sol1->addOption(generateIA(1, 1500, 3000));
+     sol2->addOption(generateIA(2, 1500, 3000));
+     sol3->addOption(generateIA(3, 1500, 3000));
+     // different client-id sizes
+     OptionPtr clientid1 = generateClientId(12);
+     OptionPtr clientid2 = generateClientId(14);
+     OptionPtr clientid3 = generateClientId(16);
+     sol1->addOption(clientid1);
+     sol2->addOption(clientid2);
+     sol3->addOption(clientid3);
+     // Pass it to the server and get an advertise
+     Pkt6Ptr reply1 = srv->processSolicit(sol1);
+     Pkt6Ptr reply2 = srv->processSolicit(sol2);
+     Pkt6Ptr reply3 = srv->processSolicit(sol3);
  
      // check if we get response at all
-     ASSERT_TRUE( reply != boost::shared_ptr<Pkt6>() );
+     checkResponse(reply1, DHCPV6_ADVERTISE, 1234);
+     checkResponse(reply2, DHCPV6_ADVERTISE, 2345);
+     checkResponse(reply3, DHCPV6_ADVERTISE, 3456);
+     // check that IA_NA was returned and that there's an address included
+     boost::shared_ptr<Option6IAAddr> addr1 = checkIA_NA(reply1, 1, subnet_->getT1(),
+                                                 subnet_->getT2());
+     boost::shared_ptr<Option6IAAddr> addr2 = checkIA_NA(reply2, 2, subnet_->getT1(),
+                                                 subnet_->getT2());
+     boost::shared_ptr<Option6IAAddr> addr3 = checkIA_NA(reply3, 3, subnet_->getT1(),
+                                                 subnet_->getT2());
+     // Check that the assigned address is indeed from the configured pool
+     checkIAAddr(addr1, addr1->getAddress(), subnet_->getPreferred(), subnet_->getValid());
+     checkIAAddr(addr2, addr2->getAddress(), subnet_->getPreferred(), subnet_->getValid());
+     checkIAAddr(addr3, addr3->getAddress(), subnet_->getPreferred(), subnet_->getValid());
+     // check DUIDs
+     checkServerId(reply1, srv->getServerID());
+     checkServerId(reply2, srv->getServerID());
+     checkServerId(reply3, srv->getServerID());
+     checkClientId(reply1, clientid1);
+     checkClientId(reply2, clientid2);
+     checkClientId(reply3, clientid3);
+     // Finally check that the addresses offered are different
+     EXPECT_NE(addr1->getAddress().toText(), addr2->getAddress().toText());
+     EXPECT_NE(addr2->getAddress().toText(), addr3->getAddress().toText());
+     EXPECT_NE(addr3->getAddress().toText(), addr1->getAddress().toText());
+     cout << "Offered address to client1=" << addr1->getAddress().toText() << endl;
+     cout << "Offered address to client2=" << addr2->getAddress().toText() << endl;
+     cout << "Offered address to client3=" << addr3->getAddress().toText() << endl;
+ }
+ // This test verifies that incoming REQUEST can be handled properly, that a
+ // REPLY is generated, that the response has an address and that address
+ // really belongs to the configured pool.
+ //
+ // This test sends a REQUEST with IA_NA that contains a valid hint.
+ //
+ // constructed very simple REQUEST message with:
+ // - client-id option (mandatory)
+ // - IA option (a request for address, with an address that belongs to the
+ //              configured pool, i.e. is valid as hint)
+ //
+ // expected returned REPLY message:
+ // - copy of client-id
+ // - server-id
+ // - IA that includes IAADDR
+ TEST_F(Dhcpv6SrvTest, RequestBasic) {
+     boost::scoped_ptr<NakedDhcpv6Srv> srv;
+     ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) );
+     // Let's create a REQUEST
+     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234));
+     req->setRemoteAddr(IOAddress("fe80::abcd"));
+     boost::shared_ptr<Option6IA> ia = generateIA(234, 1500, 3000);
+     // with a valid hint
+     IOAddress hint("2001:db8:1:1::dead:beef");
+     ASSERT_TRUE(subnet_->inPool(hint));
+     OptionPtr hint_opt(new Option6IAAddr(D6O_IAADDR, hint, 300, 500));
+     ia->addOption(hint_opt);
+     req->addOption(ia);
+     OptionPtr clientid = generateClientId();
+     req->addOption(clientid);
  
-     EXPECT_EQ( DHCPV6_ADVERTISE, reply->getType() );
-     EXPECT_EQ( 1234, reply->getTransid() );
+     // Pass it to the server and hope for a REPLY
+     Pkt6Ptr reply = srv->processRequest(req);
+     // check if we get response at all
+     checkResponse(reply, DHCPV6_REPLY, 1234);
  
      OptionPtr tmp = reply->getOption(D6O_IA_NA);
-     ASSERT_TRUE( tmp );
+     ASSERT_TRUE(tmp);
  
-     Option6IA* reply_ia = dynamic_cast<Option6IA*>(tmp.get());
-     EXPECT_EQ( 234, reply_ia->getIAID() );
+     // check that IA_NA was returned and that there's an address included
+     boost::shared_ptr<Option6IAAddr> addr = checkIA_NA(reply, 234, subnet_->getT1(),
+                                                 subnet_->getT2());
  
-     // check that there's an address included
-     EXPECT_TRUE( reply_ia->getOption(D6O_IAADDR));
+     // check that we've got the address we requested
+     checkIAAddr(addr, hint, subnet_->getPreferred(), subnet_->getValid());
  
-     // check that server included our own client-id
-     tmp = reply->getOption(D6O_CLIENTID);
-     ASSERT_TRUE( tmp );
-     EXPECT_EQ(clientid->getType(), tmp->getType() );
-     ASSERT_EQ(clientid->len(), tmp->len() );
+     // check DUIDs
+     checkServerId(reply, srv->getServerID());
+     checkClientId(reply, clientid);
  
-     EXPECT_TRUE( clientid->getData() == tmp->getData() );
+     // check that the lease is really in the database
+     Lease6Ptr l = checkLease(duid_, reply->getOption(D6O_IA_NA), addr);
+     EXPECT_TRUE(l);
 -    LeaseMgr::instance().deleteLease6(addr->getAddress());
++    LeaseMgrFactory::instance().deleteLease6(addr->getAddress());
+ }
  
-     // check that server included its server-id
-     tmp = reply->getOption(D6O_SERVERID);
-     EXPECT_EQ(tmp->getType(), srv->getServerID()->getType() );
-     ASSERT_EQ(tmp->len(),  srv->getServerID()->len() );
+ // This test checks that the server is offering different addresses to different
+ // clients in REQUEST. Please note that ADVERTISE is not a guarantee that such
+ // and address will be assigned. Had the pool was very small and contained only
+ // 2 addresses, the third client would get the same advertise as the first one
+ // and this is a correct behavior. It is REQUEST that will fail for the third
+ // client. ADVERTISE is basically saying "if you send me a request, you will
+ // probably get an address like this" (there are no guarantees).
+ TEST_F(Dhcpv6SrvTest, ManyRequests) {
+     boost::scoped_ptr<NakedDhcpv6Srv> srv;
+     ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) );
  
-     EXPECT_TRUE(tmp->getData() == srv->getServerID()->getData());
+     Pkt6Ptr req1 = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234));
+     Pkt6Ptr req2 = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 2345));
+     Pkt6Ptr req3 = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 3456));
  
-     // more checks to be implemented
+     req1->setRemoteAddr(IOAddress("fe80::abcd"));
+     req2->setRemoteAddr(IOAddress("fe80::1223"));
+     req3->setRemoteAddr(IOAddress("fe80::3467"));
+     req1->addOption(generateIA(1, 1500, 3000));
+     req2->addOption(generateIA(2, 1500, 3000));
+     req3->addOption(generateIA(3, 1500, 3000));
+     // different client-id sizes
+     OptionPtr clientid1 = generateClientId(12);
+     OptionPtr clientid2 = generateClientId(14);
+     OptionPtr clientid3 = generateClientId(16);
+     req1->addOption(clientid1);
+     req2->addOption(clientid2);
+     req3->addOption(clientid3);
+     // Pass it to the server and get an advertise
+     Pkt6Ptr reply1 = srv->processRequest(req1);
+     Pkt6Ptr reply2 = srv->processRequest(req2);
+     Pkt6Ptr reply3 = srv->processRequest(req3);
+     // check if we get response at all
+     checkResponse(reply1, DHCPV6_REPLY, 1234);
+     checkResponse(reply2, DHCPV6_REPLY, 2345);
+     checkResponse(reply3, DHCPV6_REPLY, 3456);
+     // check that IA_NA was returned and that there's an address included
+     boost::shared_ptr<Option6IAAddr> addr1 = checkIA_NA(reply1, 1, subnet_->getT1(),
+                                                 subnet_->getT2());
+     boost::shared_ptr<Option6IAAddr> addr2 = checkIA_NA(reply2, 2, subnet_->getT1(),
+                                                 subnet_->getT2());
+     boost::shared_ptr<Option6IAAddr> addr3 = checkIA_NA(reply3, 3, subnet_->getT1(),
+                                                 subnet_->getT2());
+     // Check that the assigned address is indeed from the configured pool
+     checkIAAddr(addr1, addr1->getAddress(), subnet_->getPreferred(), subnet_->getValid());
+     checkIAAddr(addr2, addr2->getAddress(), subnet_->getPreferred(), subnet_->getValid());
+     checkIAAddr(addr3, addr3->getAddress(), subnet_->getPreferred(), subnet_->getValid());
+     // check DUIDs
+     checkServerId(reply1, srv->getServerID());
+     checkServerId(reply2, srv->getServerID());
+     checkServerId(reply3, srv->getServerID());
+     checkClientId(reply1, clientid1);
+     checkClientId(reply2, clientid2);
+     checkClientId(reply3, clientid3);
+     // Finally check that the addresses offered are different
+     EXPECT_NE(addr1->getAddress().toText(), addr2->getAddress().toText());
+     EXPECT_NE(addr2->getAddress().toText(), addr3->getAddress().toText());
+     EXPECT_NE(addr3->getAddress().toText(), addr1->getAddress().toText());
+     cout << "Assigned address to client1=" << addr1->getAddress().toText() << endl;
+     cout << "Assigned address to client2=" << addr2->getAddress().toText() << endl;
+     cout << "Assigned address to client3=" << addr3->getAddress().toText() << endl;
  }
  
  TEST_F(Dhcpv6SrvTest, serverReceivedPacketName) {
      // Check all possible packet types
      for (int itype = 0; itype < 256; ++itype) {
index 8cede9bba8d71918989e104419c2dae71cd4a76a,0d54a07acf05e5b3b545bc511950951166b77e78..904bce30baf9dfad8a6f73b50529bef9c606184a
@@@ -18,19 -15,24 +18,23 @@@ CLEANFILES = *.gcno *.gcd
  
  lib_LTLIBRARIES = libb10-dhcp++.la libb10-dhcpsrv.la
  libb10_dhcp___la_SOURCES  =
- libb10_dhcp___la_SOURCES += dhcp6.h dhcp4.h
+ libb10_dhcp___la_SOURCES += libdhcp++.cc libdhcp++.h
  libb10_dhcp___la_SOURCES += iface_mgr.cc iface_mgr.h
 -libb10_dhcp___la_SOURCES += iface_mgr_linux.cc
  libb10_dhcp___la_SOURCES += iface_mgr_bsd.cc
 +libb10_dhcp___la_SOURCES += iface_mgr_linux.cc
  libb10_dhcp___la_SOURCES += iface_mgr_sun.cc
- libb10_dhcp___la_SOURCES += libdhcp++.cc libdhcp++.h
- libb10_dhcp___la_SOURCES += option4_addrlst.cc option4_addrlst.h
- libb10_dhcp___la_SOURCES += option6_addrlst.cc option6_addrlst.h
- libb10_dhcp___la_SOURCES += option6_iaaddr.cc option6_iaaddr.h
- libb10_dhcp___la_SOURCES += option6_ia.cc option6_ia.h
  libb10_dhcp___la_SOURCES += option.cc option.h
- libb10_dhcp___la_SOURCES += pkt4.cc pkt4.h
+ libb10_dhcp___la_SOURCES += option_data_types.h
+ libb10_dhcp___la_SOURCES += option_definition.cc option_definition.h
+ libb10_dhcp___la_SOURCES += option6_ia.cc option6_ia.h
+ libb10_dhcp___la_SOURCES += option6_iaaddr.cc option6_iaaddr.h
+ libb10_dhcp___la_SOURCES += option6_addrlst.cc option6_addrlst.h
+ libb10_dhcp___la_SOURCES += option4_addrlst.cc option4_addrlst.h
+ libb10_dhcp___la_SOURCES += option6_int.h
+ libb10_dhcp___la_SOURCES += option6_int_array.h
+ libb10_dhcp___la_SOURCES += dhcp6.h dhcp4.h
  libb10_dhcp___la_SOURCES += pkt6.cc pkt6.h
 -libb10_dhcp___la_SOURCES += duid.cc duid.h
+ libb10_dhcp___la_SOURCES += pkt4.cc pkt4.h
  
  libb10_dhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
  libb10_dhcp___la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
@@@ -38,28 -40,23 +42,32 @@@ libb10_dhcp___la_LIBADD   = $(top_build
  libb10_dhcp___la_LIBADD  += $(top_builddir)/src/lib/util/libb10-util.la
  libb10_dhcp___la_LDFLAGS  = -no-undefined -version-info 2:0:0
  
--libb10_dhcpsrv_la_SOURCES  = cfgmgr.cc cfgmgr.h
++libb10_dhcpsrv_la_SOURCES  =
 +libb10_dhcpsrv_la_SOURCES += addr_utilities.cc addr_utilities.h
++libb10_dhcpsrv_la_SOURCES += alloc_engine.cc alloc_engine.h
++libb10_dhcpsrv_la_SOURCES += cfgmgr.cc cfgmgr.h
 +libb10_dhcpsrv_la_SOURCES += duid.cc duid.h
 +libb10_dhcpsrv_la_SOURCES += lease_mgr.cc lease_mgr.h
 +libb10_dhcpsrv_la_SOURCES += lease_mgr_factory.cc lease_mgr_factory.h
++libb10_dhcpsrv_la_SOURCES += memfile_lease_mgr.cc memfile_lease_mgr.h
 +if HAVE_MYSQL
 +libb10_dhcpsrv_la_SOURCES += mysql_lease_mgr.cc mysql_lease_mgr.h
 +endif
  libb10_dhcpsrv_la_SOURCES += pool.cc pool.h
  libb10_dhcpsrv_la_SOURCES += subnet.cc subnet.h
  libb10_dhcpsrv_la_SOURCES += triplet.h
 -libb10_dhcpsrv_la_SOURCES += lease_mgr.cc lease_mgr.h
 -libb10_dhcpsrv_la_SOURCES += memfile_lease_mgr.cc memfile_lease_mgr.h
  
 -libb10_dhcpsrv_la_SOURCES += addr_utilities.cc addr_utilities.h
 -libb10_dhcpsrv_la_SOURCES += alloc_engine.cc alloc_engine.h
  libb10_dhcpsrv_la_CXXFLAGS = $(AM_CXXFLAGS)
  libb10_dhcpsrv_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
- libb10_dhcpsrv_la_LIBADD   = $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+ libb10_dhcpsrv_la_LIBADD   = $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
+ libb10_dhcpsrv_la_LIBADD  += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
  libb10_dhcpsrv_la_LIBADD  += $(top_builddir)/src/lib/util/libb10-util.la
  libb10_dhcpsrv_la_LDFLAGS  = -no-undefined -version-info 2:0:0
 +if HAVE_MYSQL
 +libb10_dhcpsrv_la_LDFLAGS += $(MYSQL_LIBS)
 +endif
  
 -EXTRA_DIST  = README
 +EXTRA_DIST  = README database_backends.dox
  
  if USE_CLANGPP
  # Disable unused parameter warning caused by some of the
index 15532d0543df2d51252cbbd87a84983e15eea164,15532d0543df2d51252cbbd87a84983e15eea164..a1d856c632306f1d37652557ebae7c257c4d399a
@@@ -12,6 -12,6 +12,9 @@@
  // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  // PERFORMANCE OF THIS SOFTWARE.
  
++#ifndef ADDR_UTILITIES_H
++#define ADDR_UTILITIES_H
++
  #include <asiolink/io_address.h>
  
  namespace isc {
@@@ -51,3 -51,3 +54,5 @@@ isc::asiolink::IOAddress lastAddrInPref
  
  };
  };
++
++#endif // ADDR_UTILITIES_H
index 0000000000000000000000000000000000000000,650fd5f98f7d5929ff4df56202bf249a01ca5f50..53ef7f3036a171ccefc106d51dc4c1bb96aea71a
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,275 +1,276 @@@
 -    Lease6Ptr existing = LeaseMgr::instance().getLease6(*duid, iaid, subnet->getID());
+ // Copyright (C) 2012 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
+ // copyright notice and this permission notice appear in all copies.
+ //
+ // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ // AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ // PERFORMANCE OF THIS SOFTWARE.
+ #include <alloc_engine.h>
++#include <lease_mgr_factory.h>
+ #include <string.h>
+ #include <cstring>
+ using namespace isc::asiolink;
+ namespace isc {
+ namespace dhcp {
+ AllocEngine::IterativeAllocator::IterativeAllocator()
+     :Allocator() {
+ }
+ isc::asiolink::IOAddress
+ AllocEngine::IterativeAllocator::increaseAddress(const isc::asiolink::IOAddress& addr) {
+     uint8_t packed[V6ADDRESS_LEN];
+     int len;
+     // First we copy the whole address as 16 bytes.
+     if (addr.getFamily()==AF_INET) {
+         // IPv4
+         std::memcpy(packed, addr.getAddress().to_v4().to_bytes().data(), 4);
+         len = 4;
+     } else {
+         // IPv6
+         std::memcpy(packed, addr.getAddress().to_v6().to_bytes().data(), 16);
+         len = 16;
+     }
+     for (int i = len - 1; i >= 0; --i) {
+         ++packed[i];
+         if (packed[i] != 0) {
+             break;
+         }
+     }
+     return (IOAddress::from_bytes(addr.getFamily(), packed));
+ }
+ isc::asiolink::IOAddress
+ AllocEngine::IterativeAllocator::pickAddress(const Subnet6Ptr& subnet,
+                                              const DuidPtr&,
+                                              const IOAddress&) {
+     // Let's get the last allocated address. It is usually set correctly,
+     // but there are times when it won't be (like after removing a pool or
+     // perhaps restaring the server).
+     IOAddress last = subnet->getLastAllocated();
+     const Pool6Collection& pools = subnet->getPools();
+     if (pools.size() == 0) {
+         isc_throw(AllocFailed, "No pools defined in selected subnet");
+     }
+     // first we need to find a pool the last address belongs to.
+     Pool6Collection::const_iterator it;
+     for (it = pools.begin(); it != pools.end(); ++it) {
+         if ((*it)->inRange(last)) {
+             break;
+         }
+     }
+     // last one was bogus for one of several reasons:
+     // - we just booted up and that's the first address we're allocating
+     // - a subnet was removed or other reconfiguration just completed
+     // - perhaps allocation algorithm was changed
+     if (it == pools.end()) {
+         // ok to access first element directly. We checked that pools is non-empty
+         IOAddress next = pools[0]->getFirstAddress();
+         subnet->setLastAllocated(next);
+         return (next);
+     }
+     // Ok, we have a pool that the last address belonged to, let's use it.
+     IOAddress next = increaseAddress(last); // basically addr++
+     if ((*it)->inRange(next)) {
+         // the next one is in the pool as well, so we haven't hit pool boundary yet
+         subnet->setLastAllocated(next);
+         return (next);
+     }
+     // We hit pool boundary, let's try to jump to the next pool and try again
+     ++it;
+     if (it == pools.end()) {
+         // Really out of luck today. That was the last pool. Let's rewind
+         // to the beginning.
+         next = pools[0]->getFirstAddress();
+         subnet->setLastAllocated(next);
+         return (next);
+     }
+     // there is a next pool, let's try first adddress from it
+     next = (*it)->getFirstAddress();
+     subnet->setLastAllocated(next);
+     return (next);
+ }
+ AllocEngine::HashedAllocator::HashedAllocator()
+     :Allocator() {
+     isc_throw(NotImplemented, "Hashed allocator is not implemented");
+ }
+ isc::asiolink::IOAddress
+ AllocEngine::HashedAllocator::pickAddress(const Subnet6Ptr&,
+                                              const DuidPtr&,
+                                              const IOAddress&) {
+     isc_throw(NotImplemented, "Hashed allocator is not implemented");
+ }
+ AllocEngine::RandomAllocator::RandomAllocator()
+     :Allocator() {
+     isc_throw(NotImplemented, "Random allocator is not implemented");
+ }
+ isc::asiolink::IOAddress
+ AllocEngine::RandomAllocator::pickAddress(const Subnet6Ptr&,
+                                              const DuidPtr&,
+                                              const IOAddress&) {
+     isc_throw(NotImplemented, "Random allocator is not implemented");
+ }
+ AllocEngine::AllocEngine(AllocType engine_type, unsigned int attempts)
+     :attempts_(attempts) {
+     switch (engine_type) {
+     case ALLOC_ITERATIVE:
+         allocator_ = boost::shared_ptr<Allocator>(new IterativeAllocator());
+         break;
+     case ALLOC_HASHED:
+         allocator_ = boost::shared_ptr<Allocator>(new HashedAllocator());
+         break;
+     case ALLOC_RANDOM:
+         allocator_ = boost::shared_ptr<Allocator>(new RandomAllocator());
+         break;
+     default:
+         isc_throw(BadValue, "Invalid/unsupported allocation algorithm");
+     }
+ }
+ Lease6Ptr
+ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
+                               const DuidPtr& duid,
+                               uint32_t iaid,
+                               const IOAddress& hint,
+                               bool fake_allocation /* = false */ ) {
+     // That check is not necessary. We create allocator in AllocEngine
+     // constructor
+     if (!allocator_) {
+         isc_throw(InvalidOperation, "No allocator selected");
+     }
+     // check if there's existing lease for that subnet/duid/iaid combination.
 -        existing = LeaseMgr::instance().getLease6(hint);
++    Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(*duid, iaid, subnet->getID());
+     if (existing) {
+         // we have a lease already. This is a returning client, probably after
+         // his reboot.
+         return (existing);
+     }
+     // check if the hint is in pool and is available
+     if (subnet->inPool(hint)) {
 -        Lease6Ptr existing = LeaseMgr::instance().getLease6(candidate);
++        existing = LeaseMgrFactory::instance().getLease6(hint);
+         if (!existing) {
+             /// @todo: check if the hint is reserved once we have host support
+             /// implemented
+             // the hint is valid and not currently used, let's create a lease for it
+             Lease6Ptr lease = createLease(subnet, duid, iaid, hint, fake_allocation);
+             // It can happen that the lease allocation failed (we could have lost
+             // the race condition. That means that the hint is lo longer usable and
+             // we need to continue the regular allocation path.
+             if (lease) {
+                 return (lease);
+             }
+         }
+     }
+     unsigned int i = attempts_;
+     do {
+         IOAddress candidate = allocator_->pickAddress(subnet, duid, hint);
+         /// @todo: check if the address is reserved once we have host support
+         /// implemented
 -        bool status = LeaseMgr::instance().addLease(lease);
++        Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(candidate);
+         // there's no existing lease for selected candidate, so it is
+         // free. Let's allocate it.
+         if (!existing) {
+             Lease6Ptr lease = createLease(subnet, duid, iaid, candidate,
+                                           fake_allocation);
+             if (lease) {
+                 return (lease);
+             }
+             // Although the address was free just microseconds ago, it may have
+             // been taken just now. If the lease insertion fails, we continue
+             // allocation attempts.
+         }
+         // continue trying allocation until we run out of attempts
+         // (or attempts are set to 0, which means infinite)
+         --i;
+     } while ( i || !attempts_);
+     isc_throw(AllocFailed, "Failed to allocate address after " << attempts_
+               << " tries");
+ }
+ Lease6Ptr AllocEngine::createLease(const Subnet6Ptr& subnet,
+                                    const DuidPtr& duid,
+                                    uint32_t iaid,
+                                    const IOAddress& addr,
+                                    bool fake_allocation /*= false */ ) {
+     Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, duid, iaid,
+                                subnet->getPreferred(), subnet->getValid(),
+                                subnet->getT1(), subnet->getT2(), subnet->getID()));
+     if (!fake_allocation) {
+         // That is a real (REQUEST) allocation
 -        Lease6Ptr existing = LeaseMgr::instance().getLease6(addr);
++        bool status = LeaseMgrFactory::instance().addLease(lease);
+         if (status) {
+             return (lease);
+         } else {
+             // One of many failures with LeaseMgr (e.g. lost connection to the
+             // database, database failed etc.). One notable case for that
+             // is that we are working in multi-process mode and we lost a race
+             // (some other process got that address first)
+             return (Lease6Ptr());
+         }
+     } else {
+         // That is only fake (SOLICIT without rapid-commit) allocation
+         // It is for advertise only. We should not insert the lease into LeaseMgr,
+         // but rather check that we could have inserted it.
++        Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(addr);
+         if (!existing) {
+             return (lease);
+         } else {
+             return (Lease6Ptr());
+         }
+     }
+ }
+ AllocEngine::~AllocEngine() {
+     // no need to delete allocator. smart_ptr will do the trick for us
+ }
+ }; // end of isc::dhcp namespace
+ }; // end of isc namespace
index 2911d059de3b07680b4dcb0c2ca771ad1e4a9995,2911d059de3b07680b4dcb0c2ca771ad1e4a9995..ea8125fa06099481cf641e92066d8f5216bc0064
@@@ -162,4 -162,4 +162,4 @@@ protected
  } // namespace isc::dhcp
  } // namespace isc
  
--#endif
++#endif // CFGMGR_H
index 15c306da6676a8b3bcbdccf2d0833e39b50907bc,22e7c8d0e05909c3e1c8c3c5fba7172f3155798e..116019e3fef728904950e042a3ee38fe15ae280e
@@@ -190,4 -191,4 +191,4 @@@ extern const int dhcpv6_type_name_max
  #define IRT_DEFAULT     86400
  #define IRT_MINIMUM     600
  
--#endif
++#endif /* DHCP6_H */
index a7b9a789ff60ca13e7f4a490ec7f383c4366896d,a7b9a789ff60ca13e7f4a490ec7f383c4366896d..1b15595a82120d7b97247d1da824769ecbd98c17
@@@ -681,4 -681,4 +681,4 @@@ private
  }; // namespace isc::dhcp
  }; // namespace isc
  
--#endif
++#endif // IFACE_MGR_H
index 809c528ef33467031dd6abee5916a64f16cf967a,59582fb1152f77bf9b42aa893a07d0b530fc7968..8fed9169a5f246964a1820727859585f95cdd49f
  // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  // PERFORMANCE OF THIS SOFTWARE.
  
 -#include "lease_mgr.h"
 +#include <dhcp/lease_mgr.h>
+ #include <exceptions/exceptions.h>
++
+ #include <boost/foreach.hpp>
+ #include <boost/algorithm/string.hpp>
 -#include <sstream>
++
++#include <algorithm>
+ #include <iostream>
++#include <iterator>
+ #include <map>
 -#include <iostream>
 -#include <string>
+ #include <sstream>
 -#include <algorithm>
 -#include <iterator>
++#include <string>
++
+ #include <time.h>
  
  using namespace std;
  
  using namespace isc::dhcp;
  
- LeaseMgr::LeaseMgr(const LeaseMgr::ParameterMap& parameters)
-     : parameters_(parameters) {
- }
 -LeaseMgr* LeaseMgr::instance_ = NULL;
 -
+ Lease6::Lease6(LeaseType type, const isc::asiolink::IOAddress& addr, DuidPtr duid,
+                uint32_t iaid, uint32_t preferred, uint32_t valid, uint32_t t1,
+                uint32_t t2, SubnetID subnet_id, uint8_t prefixlen)
+     :type_(type), addr_(addr), prefixlen_(prefixlen), iaid_(iaid), duid_(duid),
+      preferred_lft_(preferred), valid_lft_(valid), t1_(t1), t2_(t2),
+      subnet_id_(subnet_id), fixed_(false), fqdn_fwd_(false),
+      fqdn_rev_(false) {
+     if (!duid) {
+         isc_throw(InvalidOperation, "DUID must be specified for a lease");
+     }
  
- LeaseMgr::~LeaseMgr() {
+     cltt_ = time(NULL);
  }
  
 -LeaseMgr& LeaseMgr::instance() {
 -    if (!instance_) {
 -        isc_throw(InvalidOperation, "LeaseManager not instantiated yet");
 +std::string LeaseMgr::getParameter(const std::string& name) const {
 +    ParameterMap::const_iterator param = parameters_.find(name);
 +    if (param == parameters_.end()) {
 +        isc_throw(BadValue, "Parameter not found");
      }
 -    return (*instance_);
 +    return (param->second);
  }
  
 -void LeaseMgr::destroy_instance() {
 -    if (!instance_) {
 -        isc_throw(InvalidOperation, "LeaseManager not instantiated yet");
 +std::string
 +Lease6::toText() {
 +    ostringstream stream;
 +
 +    stream << "Type:          " << static_cast<int>(type_) << " (";
 +    switch (type_) {
 +        case Lease6::LEASE_IA_NA:
 +            stream << "IA_NA)\n";
 +            break;
 +        case Lease6::LEASE_IA_TA:
 +            stream << "IA_TA)\n";
 +            break;
 +        case Lease6::LEASE_IA_PD:
 +            stream << "IA_PD)\n";
 +            break;
 +        default:
 +            stream << "unknown)\n";
      }
 -    delete instance_;
 -    instance_ = NULL;
 +    stream << "Address:       " << addr_.toText() << "\n"
 +           << "Prefix length: " << static_cast<int>(prefixlen_) << "\n"
 +           << "IAID:          " << iaid_ << "\n"
 +           << "Pref life:     " << preferred_lft_ << "\n"
 +           << "Valid life:    " << valid_lft_ << "\n"
 +           << "Cltt:          " << cltt_ << "\n"
 +           << "Subnet ID:     " << subnet_id_ << "\n";
 +
 +    return (stream.str());
  }
  
 -LeaseMgr::LeaseMgr(const std::string& dbconfig) {
 -    if (instance_) {
 -        isc_throw(InvalidOperation, "LeaseManager already instantiated");
 -    }
 -
 -    // remember the pointer to the singleton instance
 -    instance_ = this;
 -
 -    if (dbconfig.length() == 0) {
 -        return;
 -    }
 -
 -    vector<string> tokens;
 -
 -    // we need to pass a string to is_any_of, not just char *. Otherwise there
 -    // are cryptic warnings on Debian6 running g++ 4.4 in /usr/include/c++/4.4
 -    // /bits/stl_algo.h:2178 "array subscript is above array bounds"
 -    boost::split(tokens, dbconfig, boost::is_any_of( string("\t ") ));
 -    BOOST_FOREACH(std::string token, tokens) {
 -        size_t pos = token.find("=");
 -        if (pos != string::npos) {
 -            string name = token.substr(0, pos);
 -            string value = token.substr(pos + 1);
 -            parameters_.insert(pair<string,string>(name, value));
 -        } else {
 -            isc_throw(InvalidParameter, "Cannot parse " << token
 -                      << ", expected format is name=value");
 -        }
 -
 -    }
 +bool
 +Lease6::operator==(const Lease6& other) const {
 +    return (
 +        type_ == other.type_ &&
 +        addr_ == other.addr_ &&
 +        prefixlen_ == other.prefixlen_ &&
 +        iaid_ == other.iaid_ &&
 +        *duid_ == *other.duid_ &&
 +        preferred_lft_ == other.preferred_lft_ &&
 +        valid_lft_ == other.valid_lft_ &&
 +        cltt_ == other.cltt_ &&
 +        subnet_id_ == other.subnet_id_
 +        );
  }
 -std::string LeaseMgr::getParameter(const std::string& name) const {
 -    std::map<std::string, std::string>::const_iterator param
 -        = parameters_.find(name);
 -    if (param == parameters_.end()) {
 -        isc_throw(BadValue, "Parameter not found");
 -    }
 -    return (param->second);
 -    instance_ = NULL;
++LeaseMgr::LeaseMgr(const LeaseMgr::ParameterMap& parameters)
++    : parameters_(parameters) {
+ }
+ LeaseMgr::~LeaseMgr() {
+ }
index 4b7c60753e6d420514f12a2b673a2977903d75a0,e859deaf64a6cfa235c66d669e30d3c61c53684b..05eca4dd05004e36738b884c9a29d5e3493dda08
  #ifndef LEASE_MGR_H
  #define LEASE_MGR_H
  
 -#include <string>
 -#include <fstream>
 -#include <vector>
 -#include <map>
+ #include <asiolink/io_address.h>
 -#include <boost/noncopyable.hpp>
 -#include <boost/shared_ptr.hpp>
 -#include <dhcp/option.h>
+ #include <dhcp/duid.h>
++#include <dhcp/option.h>
+ #include <dhcp/subnet.h>
++#include <exceptions/exceptions.h>
++
++#include <boost/noncopyable.hpp>
++#include <boost/shared_ptr.hpp>
++
 +#include <fstream>
 +#include <map>
 +#include <string>
 +#include <utility>
 +#include <vector>
  
- #include <asiolink/io_address.h>
- #include <boost/shared_ptr.hpp>
- #include <dhcp/duid.h>
- #include <dhcp/option.h>
- #include <exceptions/exceptions.h>
  /// @file dhcp/lease_mgr.h
  /// @brief An abstract API for lease database
  ///
  namespace isc {
  namespace dhcp {
  
- /// @brief specifies unique subnet identifier
- /// @todo: Move this to subnet.h once ticket #2237 is merged
- typedef uint32_t SubnetID;
 +/// @brief Exception thrown if name of database is not specified
 +class NoDatabaseName : public Exception {
 +public:
 +    NoDatabaseName(const char* file, size_t line, const char* what) :
 +        isc::Exception(file, line, what) {}
 +};
 +
 +/// @brief Exception thrown on failure to open database
 +class DbOpenError : public Exception {
 +public:
 +    DbOpenError(const char* file, size_t line, const char* what) :
 +        isc::Exception(file, line, what) {}
 +};
 +
 +/// @brief Exception thrown on failure to execute a database function
 +class DbOperationError : public Exception {
 +public:
 +    DbOperationError(const char* file, size_t line, const char* what) :
 +        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 Structure that holds a lease for IPv4 address
  ///
  /// For performance reasons it is a simple structure, not a class. If we chose
@@@ -325,26 -276,31 +330,25 @@@ typedef std::vector<Lease6Ptr> Lease6Co
  /// be used directly, but rather specialized derived class should be used
  /// instead.
  ///
 -/// This class is a meta-singleton. At any given time, there is only one
 -/// instance of any classes derived from that class. That is achieved with
 -/// defining only a single protected constructor, so every derived class has
 -/// to use it. Furthermore, this sole constructor registers the first instance
 -/// (and throws InvalidOperation if there is an attempt to create a second one).
 -class LeaseMgr : public boost::noncopyable {
 +/// As all methods are virtual, this class throws no exceptions.  However,
 +/// methods in concrete implementations of this class may throw exceptions:
 +/// see the documentation of those classes for details.
 +class LeaseMgr {
  public:
      /// Client Hardware address
      typedef std::vector<uint8_t> HWAddr;
  
 -    /// @brief returns a single instance of LeaseMgr
 -    ///
 -    /// LeaseMgr is a singleton and this method is the only way of
 -    /// accessing it. LeaseMgr must be created first. See
 -    /// isc::dhcp::LeaseMgrFactory class (work of ticket #2342.
 -    /// Otherwise instance() will throw InvalidOperation exception.
 -    /// @throw InvalidOperation if LeaseMgr not instantiated
 -    static LeaseMgr& instance();
 +    /// Database configuration parameter map
 +    typedef std::map<std::string, std::string> ParameterMap;
  
-     /// @brief The sole lease manager constructor
 -    /// @brief destroys the only instance of LeaseMgr
++    /// @brief Constructor
      ///
 -    /// This method is used mostly in tests, where LeaseMgr is destroyed
 -    /// at the end of each test, just to be created at the beginning of
 -    /// the next one.
 -    static void destroy_instance();
 +    /// @param parameters A data structure relating keywords and values
 +    ///        concerned with the database.
 +    LeaseMgr(const ParameterMap& parameters);
 +
-     /// @brief Destructor (closes file)
-     virtual ~LeaseMgr();
++    /// @brief Destructor
++    ~LeaseMgr();
  
      /// @brief Adds an IPv4 lease.
      ///
      /// B>=A and B=C (it is ok to have newer backend, as it should be backward
      /// compatible)
      /// Also if B>C, some database upgrade procedure may be triggered
 -    virtual std::string getVersion() const = 0;
 -
 -    /// @todo: Add host management here
 -    /// As host reservation is outside of scope for 2012, support for hosts
 -    /// is currently postponed.
 +    virtual std::pair<uint32_t, uint32_t> getVersion() const = 0;
  
 -protected:
 -    /// @brief The sole lease manager constructor
 +    /// @brief Commit Transactions
      ///
 -    /// dbconfig is a generic way of passing parameters. Parameters are passed
 -    /// in the "name=value" format, separated by spaces. Values may be enclosed
 -    /// in double quotes, if needed. This ctor guarantees that there will be
 -    /// only one instance of any derived classes. If there is a second instance
 -    /// being created with the first one still around, it will throw
 -    /// InvalidOperation.
 +    /// Commits all pending database operations.  On databases that don't
 +    /// support transactions, this is a no-op.
 +    virtual void commit() = 0;
 +
 +    /// @brief Rollback Transactions
      ///
 -    /// @param dbconfig database configuration
 -    /// @throw InvalidOperation when trying to create second LeaseMgr
 -    LeaseMgr(const std::string& dbconfig);
 +    /// Rolls back all pending database operations.  On databases that don't
 +    /// support transactions, this is a no-op.
 +    virtual void rollback() = 0;
  
 -    /// @brief Destructor
 -    virtual ~LeaseMgr();
 +    /// @todo: Add host management here
 +    /// As host reservation is outside of scope for 2012, support for hosts
 +    /// is currently postponed.
  
- protected:
      /// @brief returns value of the parameter
      std::string getParameter(const std::string& name) const;
  
++private:
      /// @brief list of parameters passed in dbconfig
      ///
      /// That will be mostly used for storing database name, username,
index adc8ce0a36e9ab32850accd299a5b7f038c7550d,0000000000000000000000000000000000000000..08a04e4978b7ab1dfb352d5e94dc05154544b749
mode 100644,000000..100644
--- /dev/null
@@@ -1,114 -1,0 +1,119 @@@
 +// Copyright (C) 2012 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
 +// copyright notice and this permission notice appear in all copies.
 +//
 +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
 +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 +// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
 +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 +// PERFORMANCE OF THIS SOFTWARE.
 +
 +#include "config.h"
 +
 +#include <algorithm>
 +#include <iostream>
 +#include <iterator>
 +#include <map>
 +#include <sstream>
 +#include <string>
 +#include <utility>
 +
 +#include <boost/foreach.hpp>
 +#include <boost/scoped_ptr.hpp>
 +#include <boost/algorithm/string.hpp>
 +#include <exceptions/exceptions.h>
 +#include <dhcp/lease_mgr_factory.h>
 +
++#include <dhcp/memfile_lease_mgr.h>
 +#ifdef HAVE_MYSQL
 +#include <dhcp/mysql_lease_mgr.h>
 +#endif
 +
 +using namespace std;
 +
 +namespace isc {
 +namespace dhcp {
 +
 +boost::scoped_ptr<LeaseMgr>&
 +LeaseMgrFactory::getLeaseMgrPtr() {
 +    static boost::scoped_ptr<LeaseMgr> leaseMgrPtr;
 +    return (leaseMgrPtr);
 +}
 +
 +LeaseMgr::ParameterMap
 +LeaseMgrFactory::parse(const std::string& dbconfig) {
 +    LeaseMgr::ParameterMap mapped_tokens;
 +
 +    if (! dbconfig.empty()) {
 +        vector<string> tokens;
 +
 +        // We need to pass a string to is_any_of, not just char*. Otherwise
 +        // there are cryptic warnings on Debian6 running g++ 4.4 in
 +        // /usr/include/c++/4.4/bits/stl_algo.h:2178 "array subscript is above
 +        // array bounds"
 +        boost::split(tokens, dbconfig, boost::is_any_of( string("\t ") ));
 +        BOOST_FOREACH(std::string token, tokens) {
 +            size_t pos = token.find("=");
 +            if (pos != string::npos) {
 +                string name = token.substr(0, pos);
 +                string value = token.substr(pos + 1);
 +                mapped_tokens.insert(make_pair(name, value));
 +            } else {
 +                isc_throw(InvalidParameter, "Cannot parse " << token
 +                          << ", expected format is name=value");
 +            }
 +        }
 +    }
 +
 +    return (mapped_tokens);
 +}
 +
 +void
 +LeaseMgrFactory::create(const std::string& dbconfig) {
 +    const std::string type = "type";
 +
 +    // Is "type" present?
 +    LeaseMgr::ParameterMap parameters = parse(dbconfig);
 +    if (parameters.find(type) == parameters.end()) {
 +        isc_throw(InvalidParameter, "Database configuration parameters do not "
 +                  "contain the 'type' keyword");
 +    }
 +
 +    // Yes, check what it is.
 +#ifdef HAVE_MYSQL
 +    if (parameters[type] == string("mysql")) {
 +        getLeaseMgrPtr().reset(new MySqlLeaseMgr(parameters));
 +        return;
 +    }
 +#endif
++    if (parameters[type] == string("memfile")) {
++        getLeaseMgrPtr().reset(new Memfile_LeaseMgr(parameters));
++        return;
++    }
 +
 +    // Get here on no match
 +    isc_throw(InvalidType, "Database configuration parameter 'type' does "
 +              "not specify a supported database backend");
 +}
 +
 +void
 +LeaseMgrFactory::destroy() {
 +    getLeaseMgrPtr().reset();
 +}
 +
 +LeaseMgr&
 +LeaseMgrFactory::instance() {
 +    LeaseMgr* lmptr = getLeaseMgrPtr().get();
 +    if (lmptr == NULL) {
 +        isc_throw(NoLeaseManager, "no current lease manager is available");
 +    }
 +    return (*lmptr);
 +}
 +
 +
 +}; // namespace dhcp
 +}; // namespace isc
Simple merge
index 0000000000000000000000000000000000000000,63023c402a8a557af597120549067fb8931bc231..1424b397787f44c601c45c8f473f73e128e4176c
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,124 +1,132 @@@
 -#include "memfile_lease_mgr.h"
+ // Copyright (C) 2012 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
+ // copyright notice and this permission notice appear in all copies.
+ //
+ // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ // AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ // PERFORMANCE OF THIS SOFTWARE.
+ #include <iostream>
 -using namespace isc::dhcp::test;
++
++#include <dhcp/memfile_lease_mgr.h>
+ using namespace isc::dhcp;
 -Memfile_LeaseMgr::Memfile_LeaseMgr(const std::string& dbconfig)
 -    : LeaseMgr(dbconfig) {
 -Lease4Ptr Memfile_LeaseMgr::getLease4(isc::asiolink::IOAddress) const {
++Memfile_LeaseMgr::Memfile_LeaseMgr(const ParameterMap& parameters)
++    : LeaseMgr(parameters) {
+     std::cout << "Warning: Using memfile database backend. It is usable for" << std::endl;
+     std::cout << "Warning: limited testing only. File support not implemented yet." << std::endl;
+     std::cout << "Warning: Leases will be lost after restart." << std::endl;
+ }
+ Memfile_LeaseMgr::~Memfile_LeaseMgr() {
+ }
+ bool Memfile_LeaseMgr::addLease(const Lease4Ptr&) {
+     return (false);
+ }
+ bool Memfile_LeaseMgr::addLease(const Lease6Ptr& lease) {
+     if (getLease6(lease->addr_)) {
+         // there is a lease with specified address already
+         return (false);
+     }
+     storage6_.insert(lease);
+     return (true);
+ }
 -Lease4Ptr Memfile_LeaseMgr::getLease4(isc::asiolink::IOAddress ,
++Lease4Ptr Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress&) const {
+     return (Lease4Ptr());
+ }
+ Lease4Collection Memfile_LeaseMgr::getLease4(const HWAddr& ) const {
+     return (Lease4Collection());
+ }
 -bool Memfile_LeaseMgr::deleteLease4(uint32_t ) {
++Lease4Ptr Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress&,
+                                       SubnetID) const {
+     return (Lease4Ptr());
+ }
+ Lease4Ptr Memfile_LeaseMgr::getLease4(const HWAddr&,
+                                       SubnetID) const {
+     return (Lease4Ptr());
+ }
+ Lease4Ptr Memfile_LeaseMgr::getLease4(const ClientId&,
+                                       SubnetID) const {
+     return (Lease4Ptr());
+ }
+ Lease4Collection Memfile_LeaseMgr::getLease4(const ClientId& ) const {
+     return (Lease4Collection());
+ }
+ Lease6Ptr Memfile_LeaseMgr::getLease6(const isc::asiolink::IOAddress& addr) const {
+     Lease6Storage::iterator l = storage6_.find(addr);
+     if (l == storage6_.end()) {
+         return (Lease6Ptr());
+     } else {
+         return (*l);
+     }
+ }
+ Lease6Collection Memfile_LeaseMgr::getLease6(const DUID& , uint32_t ) const {
+     return (Lease6Collection());
+ }
+ Lease6Ptr Memfile_LeaseMgr::getLease6(const DUID& duid, uint32_t iaid,
+                                       SubnetID subnet_id) const {
+     /// @todo: Slow, naive implementation. Write it using additional indexes
+     for (Lease6Storage::iterator l = storage6_.begin(); l != storage6_.end(); ++l) {
+         if ( (*((*l)->duid_) == duid) &&
+              ( (*l)->iaid_ == iaid) &&
+              ( (*l)->subnet_id_ == subnet_id)) {
+             return (*l);
+         }
+     }
+     return (Lease6Ptr());
+ }
+ void Memfile_LeaseMgr::updateLease4(const Lease4Ptr& ) {
+ }
+ void Memfile_LeaseMgr::updateLease6(const Lease6Ptr& ) {
+ }
++bool Memfile_LeaseMgr::deleteLease4(const isc::asiolink::IOAddress&) {
+     return (false);
+ }
+ bool Memfile_LeaseMgr::deleteLease6(const isc::asiolink::IOAddress& addr) {
+     Lease6Storage::iterator l = storage6_.find(addr);
+     if (l == storage6_.end()) {
+         // no such lease
+         return (false);
+     } else {
+         storage6_.erase(l);
+         return (true);
+     }
+ }
+ std::string Memfile_LeaseMgr::getDescription() const {
+     return (std::string("This is a dummy memfile backend implementation.\n"
+                         "It does not offer any useful lease management and its only\n"
+                         "purpose is to test abstract lease manager API."));
+ }
++
++void
++Memfile_LeaseMgr::commit() {
++}
++
++void
++Memfile_LeaseMgr::rollback() {
++}
index 0000000000000000000000000000000000000000,c5a41e6c7fe9780e9af5b6ddaaec94b34b22ac9a..e3f415d36dd4afbb0b0570a0577b0fa1d73003bf
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,228 +1,241 @@@
 -#include <dhcp/lease_mgr.h>
+ // Copyright (C) 2012 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
+ // copyright notice and this permission notice appear in all copies.
+ //
+ // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ // AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ // PERFORMANCE OF THIS SOFTWARE.
+ #ifndef MEMFILE_LEASE_MGR_H
+ #define MEMFILE_LEASE_MGR_H
 -namespace test {
+ #include <boost/multi_index_container.hpp>
+ #include <boost/multi_index/indexed_by.hpp>
+ #include <boost/multi_index/ordered_index.hpp>
+ #include <boost/multi_index/member.hpp>
++#include <dhcp/lease_mgr.h>
++
+ namespace isc {
+ namespace dhcp {
 -    /// @param dbconfig database configuration
 -    Memfile_LeaseMgr(const std::string& dbconfig);
+ // This is a concrete implementation of a Lease database.
+ //
+ // It is for testing purposes only. It is NOT a production code.
+ //
+ // It does not do anything useful now, and is used for abstract LeaseMgr
+ // class testing. It may later evolve into more useful backend if the
+ // need arises. We can reuse code from memfile benchmark. See code in
+ // tests/tools/dhcp-ubench/memfile_bench.{cc|h}
+ class Memfile_LeaseMgr : public LeaseMgr {
+ public:
+     /// @brief The sole lease manager constructor
+     ///
+     /// dbconfig is a generic way of passing parameters. Parameters
+     /// are passed in the "name=value" format, separated by spaces.
+     /// Values may be enclosed in double quotes, if needed.
+     ///
 -    virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr) const;
++    /// @param parameters A data structure relating keywords and values
++    ///        concerned with the database.
++    Memfile_LeaseMgr(const ParameterMap& parameters);
+     /// @brief Destructor (closes file)
+     virtual ~Memfile_LeaseMgr();
+     /// @brief Adds an IPv4 lease.
+     ///
+     /// @todo Not implemented yet
+     /// @param lease lease to be added
+     virtual bool addLease(const Lease4Ptr& lease);
+     /// @brief Adds an IPv6 lease.
+     ///
+     /// @param lease lease to be added
+     virtual bool addLease(const Lease6Ptr& lease);
+     /// @brief Returns existing IPv4 lease for specified IPv4 address.
+     ///
+     /// @todo Not implemented yet
+     /// @param addr address of the searched lease
+     ///
+     /// @return a collection of leases
 -    virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr,
++    virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr) const;
+     /// @brief Returns existing IPv4 lease for specific address and subnet
+     ///
+     /// @todo Not implemented yet
+     /// @param addr address of the searched lease
+     /// @param subnet_id ID of the subnet the lease must belong to
+     ///
+     /// @return smart pointer to the lease (or NULL if a lease is not found)
 -    /// @todo Not implemented yet
 -    ///
++    virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr,
+                                 SubnetID subnet_id) const;
+     /// @brief Returns existing IPv4 leases for specified hardware address.
+     ///
+     /// @todo Not implemented yet
+     ///
+     /// Although in the usual case there will be only one lease, for mobile
+     /// clients or clients with multiple static/fixed/reserved leases there
+     /// can be more than one. Thus return type is a container, not a single
+     /// pointer.
+     ///
+     /// @param hwaddr hardware address of the client
+     ///
+     /// @return lease collection
+     virtual Lease4Collection getLease4(const HWAddr& hwaddr) const;
+     /// @brief Returns existing IPv4 leases for specified hardware address
+     ///        and a subnet
+     ///
+     /// @todo Not implemented yet
+     ///
+     /// There can be at most one lease for a given HW address in a single
+     /// pool, so this method with either return a single lease or NULL.
+     ///
+     /// @param hwaddr hardware address of the client
+     /// @param subnet_id identifier of the subnet that lease must belong to
+     ///
+     /// @return a pointer to the lease (or NULL if a lease is not found)
+     virtual Lease4Ptr getLease4(const HWAddr& hwaddr,
+                                 SubnetID subnet_id) const;
+     /// @brief Returns existing IPv4 lease for specified client-id
+     ///
+     /// @todo Not implemented yet
+     ///
+     /// @param clientid client identifier
+     virtual Lease4Collection getLease4(const ClientId& clientid) const;
+     /// @brief Returns existing IPv4 lease for specified client-id
+     ///
+     /// There can be at most one lease for a given HW address in a single
+     /// pool, so this method with either return a single lease or NULL.
+     ///
+     /// @todo Not implemented yet
+     ///
+     /// @param clientid client identifier
+     /// @param subnet_id identifier of the subnet that lease must belong to
+     ///
+     /// @return a pointer to the lease (or NULL if a lease is not found)
+     virtual Lease4Ptr getLease4(const ClientId& clientid,
+                                 SubnetID subnet_id) const;
+     /// @brief Returns existing IPv6 lease for a given IPv6 address.
+     ///
+     /// @param addr address of the searched lease
+     ///
+     /// @return smart pointer to the lease (or NULL if a lease is not found)
+     Lease6Ptr getLease6(const isc::asiolink::IOAddress& addr) const;
+     /// @brief Returns existing IPv6 lease for a given DUID+IA combination
+     ///
+     /// @todo Not implemented yet
+     ///
+     /// @param duid client DUID
+     /// @param iaid IA identifier
+     ///
+     /// @return collection of IPv6 leases
+     Lease6Collection getLease6(const DUID& duid, uint32_t iaid) const;
+     /// @brief Returns existing IPv6 lease for a given DUID+IA combination
+     ///
+     /// @todo Not implemented yet
+     ///
+     /// @param duid client DUID
+     /// @param iaid IA identifier
+     /// @param subnet_id identifier of the subnet the lease must belong to
+     ///
+     /// @return smart pointer to the lease (or NULL if a lease is not found)
+     Lease6Ptr getLease6(const DUID& duid, uint32_t iaid, SubnetID subnet_id) const;
+     /// @brief Updates IPv4 lease.
+     ///
+     /// @todo Not implemented yet
+     ///
+     /// @param lease4 The lease to be updated.
+     ///
+     /// If no such lease is present, an exception will be thrown.
+     void updateLease4(const Lease4Ptr& lease4);
+     /// @brief Updates IPv4 lease.
+     ///
+     /// @todo Not implemented yet
+     ///
+     /// @param lease4 The lease to be updated.
+     ///
+     /// If no such lease is present, an exception will be thrown.
+     void updateLease6(const Lease6Ptr& lease6);
+     /// @brief Deletes a lease.
+     ///
 -    bool deleteLease4(uint32_t addr);
+     /// @param addr IPv4 address of the lease to be deleted.
+     ///
+     /// @return true if deletion was successful, false if no such lease exists
 -    std::string getVersion() const { return ("test-version"); }
++    virtual bool deleteLease4(const isc::asiolink::IOAddress& addr);
+     /// @brief Deletes a lease.
+     ///
+     /// @param addr IPv4 address of the lease to be deleted.
+     ///
+     /// @return true if deletion was successful, false if no such lease exists
+     bool deleteLease6(const isc::asiolink::IOAddress& addr);
+     /// @brief Returns backend name.
+     ///
+     /// Each backend have specific name, e.g. "mysql" or "sqlite".
+     std::string getName() const { return ("memfile"); }
+     /// @brief Returns description of the backend.
+     ///
+     /// This description may be multiline text that describes the backend.
+     std::string getDescription() const;
+     /// @brief Returns backend version.
 -}; // end of isc::dhcp::test namespace
++    virtual std::pair<uint32_t, uint32_t> getVersion() const {
++        return (std::make_pair(1, 0));
++    }
++
++    /// @brief Commit Transactions
++    ///
++    /// Commits all pending database operations.  On databases that don't
++    /// support transactions, this is a no-op.
++    virtual void commit();
++
++    /// @brief Rollback Transactions
++    ///
++    /// Rolls back all pending database operations.  On databases that don't
++    /// support transactions, this is a no-op.
++    virtual void rollback();
+     using LeaseMgr::getParameter;
+ protected:
+     typedef boost::multi_index_container< // this is a multi-index container...
+     Lease6Ptr, // it will hold shared_ptr to leases6
+         boost::multi_index::indexed_by< // and will be sorted by
+             // IPv6 address that are unique. That particular key is a member
+             // of the Lease6 structure, is of type IOAddress and can be accessed
+             // by doing &Lease6::addr_
+             boost::multi_index::ordered_unique< 
+                 boost::multi_index::member<Lease6, isc::asiolink::IOAddress, &Lease6::addr_> 
+             >
+         >
+     > Lease6Storage; // Let the whole contraption be called Lease6Storage.
+     Lease6Storage storage6_;
+ };
 -#endif // MEMFILE_LEASE_MGR_H
+ }; // end of isc::dhcp namespace
+ }; // end of isc namespace
++#endif // MEMFILE_LEASE_MGR_HSE4
++
index 99548f63db9ce096e2fc85cea5eff78c96206158,57055a7ec2abbd9b98828adb54a888d840a7f89d..bf5e2e764e79071cc0ec4030806b7d3b02bc9401
@@@ -29,33 -26,30 +29,38 @@@ TESTS 
  if HAVE_GTEST
  TESTS += libdhcp++_unittests libdhcpsrv_unittests
  libdhcp___unittests_SOURCES  = run_unittests.cc
 -libdhcp___unittests_SOURCES += libdhcp++_unittest.cc
  libdhcp___unittests_SOURCES += iface_mgr_unittest.cc
 -libdhcp___unittests_SOURCES += option6_iaaddr_unittest.cc
 -libdhcp___unittests_SOURCES += option6_ia_unittest.cc
 -libdhcp___unittests_SOURCES += option6_addrlst_unittest.cc
 +libdhcp___unittests_SOURCES += libdhcp++_unittest.cc
  libdhcp___unittests_SOURCES += option4_addrlst_unittest.cc
 -libdhcp___unittests_SOURCES += option6_int_unittest.cc
 +libdhcp___unittests_SOURCES += option6_addrlst_unittest.cc
- libdhcp___unittests_SOURCES += option6_iaaddr_unittest.cc
 +libdhcp___unittests_SOURCES += option6_ia_unittest.cc
++libdhcp___unittests_SOURCES += option6_iaaddr_unittest.cc
+ libdhcp___unittests_SOURCES += option6_int_array_unittest.cc
 -libdhcp___unittests_SOURCES += option_unittest.cc
++libdhcp___unittests_SOURCES += option6_int_unittest.cc
+ libdhcp___unittests_SOURCES += option_definition_unittest.cc
 -libdhcp___unittests_SOURCES += pkt6_unittest.cc
 +libdhcp___unittests_SOURCES += option_unittest.cc
  libdhcp___unittests_SOURCES += pkt4_unittest.cc
 -libdhcp___unittests_SOURCES += duid_unittest.cc
 +libdhcp___unittests_SOURCES += pkt6_unittest.cc
 +libdhcp___unittests_SOURCES += schema_copy.h
  
  libdhcp___unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
  libdhcp___unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
  libdhcp___unittests_CXXFLAGS = $(AM_CXXFLAGS)
  
  libdhcpsrv_unittests_SOURCES  = run_unittests.cc
 -libdhcpsrv_unittests_SOURCES += cfgmgr_unittest.cc triplet_unittest.cc
 -libdhcpsrv_unittests_SOURCES += pool_unittest.cc subnet_unittest.cc
  libdhcpsrv_unittests_SOURCES += addr_utilities_unittest.cc
 -libdhcpsrv_unittests_SOURCES += lease_mgr_unittest.cc
+ libdhcpsrv_unittests_SOURCES += alloc_engine_unittest.cc
 +libdhcpsrv_unittests_SOURCES += cfgmgr_unittest.cc
 +libdhcpsrv_unittests_SOURCES += duid_unittest.cc
 +libdhcpsrv_unittests_SOURCES += lease_mgr_factory_unittest.cc
 +libdhcpsrv_unittests_SOURCES += lease_mgr_unittest.cc
++libdhcpsrv_unittests_SOURCES += memfile_lease_mgr_unittest.cc
 +if HAVE_MYSQL
 +libdhcpsrv_unittests_SOURCES += mysql_lease_mgr_unittest.cc
 +endif
 +libdhcpsrv_unittests_SOURCES += pool_unittest.cc
 +libdhcpsrv_unittests_SOURCES += subnet_unittest.cc
 +libdhcpsrv_unittests_SOURCES += triplet_unittest.cc
  
  libdhcpsrv_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
  libdhcpsrv_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
index 0000000000000000000000000000000000000000,91e08d802fb0991e9eda02f5a43beb4991ec5cf0..d4290a37e40fa93b5453f15a873ffd931d08a2d0
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,343 +1,341 @@@
 -using namespace isc::dhcp::test; // Memfile_LeaseMgr
+ // Copyright (C) 2012 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
+ // copyright notice and this permission notice appear in all copies.
+ //
+ // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ // AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ // PERFORMANCE OF THIS SOFTWARE.
+ #include <config.h>
+ #include <asiolink/io_address.h>
+ #include <dhcp/lease_mgr.h>
++#include <dhcp/lease_mgr_factory.h>
+ #include <dhcp/duid.h>
+ #include <dhcp/alloc_engine.h>
+ #include <dhcp/cfgmgr.h>
+ #include <dhcp/memfile_lease_mgr.h>
+ #include <boost/shared_ptr.hpp>
+ #include <boost/scoped_ptr.hpp>
+ #include <iostream>
+ #include <sstream>
+ #include <map>
+ #include <gtest/gtest.h>
+ using namespace std;
+ using namespace isc;
+ using namespace isc::asiolink;
+ using namespace isc::dhcp;
 -        leasemgr_ = new Memfile_LeaseMgr("");
+ namespace {
+ class NakedAllocEngine : public AllocEngine {
+ public:
+     NakedAllocEngine(AllocEngine::AllocType engine_type, unsigned int attempts)
+         :AllocEngine(engine_type, attempts) {
+     }
+     using AllocEngine::Allocator;
+     using AllocEngine::IterativeAllocator;
+ };
+ // empty class for now, but may be extended once Addr6 becomes bigger
+ class AllocEngineTest : public ::testing::Test {
+ public:
+     AllocEngineTest() {
+         duid_ = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0x42)));
+         iaid_ = 42;
+         // instantiate cfg_mgr
+         CfgMgr& cfg_mgr = CfgMgr::instance();
+         subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
+         pool_ = Pool6Ptr(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1::10"),
+                                    IOAddress("2001:db8:1::20")));
+         subnet_->addPool6(pool_);
+         cfg_mgr.addSubnet6(subnet_);
 -        LeaseMgr::instance().destroy_instance();
 -        leasemgr_ = NULL;
++        factory_.create("type=memfile");
+     }
+     void checkLease6(const Lease6Ptr& lease) {
+         // that is belongs to the right subnet
+         EXPECT_EQ(lease->subnet_id_, subnet_->getID());
+         EXPECT_TRUE(subnet_->inRange(lease->addr_));
+         EXPECT_TRUE(subnet_->inPool(lease->addr_));
+         // that it have proper parameters
+         EXPECT_EQ(iaid_, lease->iaid_);
+         EXPECT_EQ(subnet_->getValid(), lease->valid_lft_);
+         EXPECT_EQ(subnet_->getPreferred(), lease->preferred_lft_);
+         EXPECT_EQ(subnet_->getT1(), lease->t1_);
+         EXPECT_EQ(subnet_->getT2(), lease->t2_);
+         EXPECT_EQ(0, lease->prefixlen_); // this is IA_NA, not IA_PD
+         EXPECT_TRUE(false == lease->fqdn_fwd_);
+         EXPECT_TRUE(false == lease->fqdn_rev_);
+         EXPECT_TRUE(*lease->duid_ == *duid_);
+         // @todo: check cltt
+      }
+     ~AllocEngineTest() {
 -    LeaseMgr* leasemgr_;
++        factory_.destroy();
+     }
+     DuidPtr duid_;
+     uint32_t iaid_;
+     Subnet6Ptr subnet_;
+     Pool6Ptr pool_;
 -    EXPECT_TRUE(first->hwaddr_ == second->hwaddr_);
++    LeaseMgrFactory factory_;
+ };
+ // This test checks if the Allocation Engine can be instantiated and that it
+ // parses parameters string properly.
+ TEST_F(AllocEngineTest, constructor) {
+     AllocEngine* x = NULL;
+     // Hashed and random allocators are not supported yet
+     ASSERT_THROW(x = new AllocEngine(AllocEngine::ALLOC_HASHED, 5), NotImplemented);
+     ASSERT_THROW(x = new AllocEngine(AllocEngine::ALLOC_RANDOM, 5), NotImplemented);
+     ASSERT_NO_THROW(x = new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));
+     delete x;
+ }
+ /// @todo: This method is taken from mysql_lease_mgr_utilities.cc from ticket
+ /// #2342. Get rid of one instance once the code is merged
+ void
+ detailCompareLease6(const Lease6Ptr& first, const Lease6Ptr& second) {
+     EXPECT_EQ(first->type_, second->type_);
+     // Compare address strings - odd things happen when they are different
+     // as the EXPECT_EQ appears to call the operator uint32_t() function,
+     // which causes an exception to be thrown for IPv6 addresses.
+     EXPECT_EQ(first->addr_.toText(), second->addr_.toText());
+     EXPECT_EQ(first->prefixlen_, second->prefixlen_);
+     EXPECT_EQ(first->iaid_, second->iaid_);
 -    Lease6Ptr from_mgr = LeaseMgr::instance().getLease6(lease->addr_);
+     EXPECT_TRUE(*first->duid_ == *second->duid_);
+     EXPECT_EQ(first->preferred_lft_, second->preferred_lft_);
+     EXPECT_EQ(first->valid_lft_, second->valid_lft_);
+     EXPECT_EQ(first->cltt_, second->cltt_);
+     EXPECT_EQ(first->subnet_id_, second->subnet_id_);
+ }
+ // This test checks if the simple allocation can succeed
+ TEST_F(AllocEngineTest, simpleAlloc) {
+     boost::scoped_ptr<AllocEngine> engine;
+     ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+     ASSERT_TRUE(engine);
+     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
+                                                false);
+     // check that we got a lease
+     ASSERT_TRUE(lease);
+     // do all checks on the lease
+     checkLease6(lease);
+     // Check that the lease is indeed in LeaseMgr
 -    Lease6Ptr from_mgr = LeaseMgr::instance().getLease6(lease->addr_);
++    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->addr_);
+     ASSERT_TRUE(from_mgr);
+     // Now check that the lease in LeaseMgr has the same parameters
+     detailCompareLease6(lease, from_mgr);
+ }
+ // This test checks if the fake allocation (for SOLICIT) can succeed
+ TEST_F(AllocEngineTest, fakeAlloc) {
+     boost::scoped_ptr<AllocEngine> engine;
+     ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+     ASSERT_TRUE(engine);
+     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
+                                                true);
+     // check that we got a lease
+     ASSERT_TRUE(lease);
+     // do all checks on the lease
+     checkLease6(lease);
+     // Check that the lease is NOT in LeaseMgr
 -    Lease6Ptr from_mgr = LeaseMgr::instance().getLease6(lease->addr_);
++    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->addr_);
+     ASSERT_FALSE(from_mgr);
+ }
+ // This test checks if the allocation with a hint that is valid (in range,
+ // in pool and free) can succeed
+ TEST_F(AllocEngineTest, allocWithValidHint) {
+     boost::scoped_ptr<AllocEngine> engine;
+     ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+     ASSERT_TRUE(engine);
+     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
+                                                IOAddress("2001:db8:1::15"),
+                                                false);
+     // check that we got a lease
+     ASSERT_TRUE(lease);
+     // we should get what we asked for
+     EXPECT_EQ(lease->addr_.toText(), "2001:db8:1::15");
+     // do all checks on the lease
+     checkLease6(lease);
+     // Check that the lease is indeed in LeaseMgr
 -    ASSERT_TRUE(LeaseMgr::instance().addLease(used));
++    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->addr_);
+     ASSERT_TRUE(from_mgr);
+     // Now check that the lease in LeaseMgr has the same parameters
+     detailCompareLease6(lease, from_mgr);
+ }
+ // This test checks if the allocation with a hint that is in range,
+ // in pool, but is currently used) can succeed
+ TEST_F(AllocEngineTest, allocWithUsedHint) {
+     boost::scoped_ptr<AllocEngine> engine;
+     ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+     ASSERT_TRUE(engine);
+     // let's create a lease and put it in the LeaseMgr
+     DuidPtr duid2 = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0xff)));
+     Lease6Ptr used(new Lease6(Lease6::LEASE_IA_NA, IOAddress("2001:db8:1::1f"),
+                               duid2, 1, 2, 3, 4, 5, subnet_->getID()));
 -    Lease6Ptr from_mgr = LeaseMgr::instance().getLease6(lease->addr_);
++    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
+     // another client comes in and request an address that is in pool, but
+     // unfortunately it is used already. The same address must not be allocated
+     // twice.
+     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
+                                                IOAddress("2001:db8:1::1f"),
+                                                false);
+     // check that we got a lease
+     ASSERT_TRUE(lease);
+     // allocated address must be different
+     EXPECT_TRUE(used->addr_.toText() != lease->addr_.toText());
+     // we should NOT get what we asked for, because it is used already
+     EXPECT_TRUE(lease->addr_.toText() != "2001:db8:1::1f");
+     // do all checks on the lease
+     checkLease6(lease);
+     // Check that the lease is indeed in LeaseMgr
 -    Lease6Ptr from_mgr = LeaseMgr::instance().getLease6(lease->addr_);
++    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->addr_);
+     ASSERT_TRUE(from_mgr);
+     // Now check that the lease in LeaseMgr has the same parameters
+     detailCompareLease6(lease, from_mgr);
+ }
+ // This test checks if the allocation with a hint that is out the blue
+ // can succeed. The invalid hint should be ignored completely.
+ TEST_F(AllocEngineTest, allocBogusHint) {
+     boost::scoped_ptr<AllocEngine> engine;
+     ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+     ASSERT_TRUE(engine);
+     // Client would like to get a 3000::abc lease, which does not belong to any
+     // supported lease. Allocation engine should ignore it and carry on
+     // with the normal allocation
+     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
+                                                IOAddress("3000::abc"),
+                                                false);
+     // check that we got a lease
+     ASSERT_TRUE(lease);
+     // we should NOT get what we asked for, because it is used already
+     EXPECT_TRUE(lease->addr_.toText() != "3000::abc");
+     // do all checks on the lease
+     checkLease6(lease);
+     // Check that the lease is indeed in LeaseMgr
++    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->addr_);
+     ASSERT_TRUE(from_mgr);
+     // Now check that the lease in LeaseMgr has the same parameters
+     detailCompareLease6(lease, from_mgr);
+ }
+ // This test verifies that the allocator picks addresses that belong to the
+ // pool
+ TEST_F(AllocEngineTest, IterativeAllocator) {
+     NakedAllocEngine::Allocator* alloc = new NakedAllocEngine::IterativeAllocator();
+     for (int i = 0; i < 1000; ++i) {
+         IOAddress candidate = alloc->pickAddress(subnet_, duid_, IOAddress("::"));
+         EXPECT_TRUE(subnet_->inPool(candidate));
+     }
+     delete alloc;
+ }
+ // This test verifies that the iterative allocator really walks over all addresses
+ // in all pools in specified subnet. It also must not pick the same address twice
+ // unless it runs out of pool space and must start over.
+ TEST_F(AllocEngineTest, IterativeAllocator_manyPools) {
+     NakedAllocEngine::IterativeAllocator* alloc = new NakedAllocEngine::IterativeAllocator();
+     // let's start from 2, as there is 2001:db8:1::10 - 2001:db8:1::20 pool already.
+     for (int i = 2; i < 10; ++i) {
+         stringstream min, max;
+         min << "2001:db8:1::" << hex << i*16 + 1;
+         max << "2001:db8:1::" << hex << i*16 + 9;
+         Pool6Ptr pool(new Pool6(Pool6::TYPE_IA, IOAddress(min.str()),
+                                 IOAddress(max.str())));
+         // cout << "Adding pool: " << min.str() << "-" << max.str() << endl;
+         subnet_->addPool6(pool);
+     }
+     int total = 17 + 8*9; // first pool (::10 - ::20) has 17 addresses in it,
+                           // there are 8 extra pools with 9 addresses in each.
+     // Let's keep picked addresses here and check their uniqueness.
+     std::map<IOAddress, int> generated_addrs;
+     int cnt = 0;
+     while (++cnt) {
+         IOAddress candidate = alloc->pickAddress(subnet_, duid_, IOAddress("::"));
+         EXPECT_TRUE(subnet_->inPool(candidate));
+         // One way to easily verify that the iterative allocator really works is
+         // to uncomment the following line and observe its output that it
+         // covers all defined subnets.
+         // cout << candidate.toText() << endl;
+         if (generated_addrs.find(candidate) == generated_addrs.end()) {
+             // we haven't had this
+             generated_addrs[candidate] = 0;
+         } else {
+             // we have seen this address before. That should mean that we
+             // iterated over all addresses.
+             if (generated_addrs.size() == total) {
+                 // we have exactly the number of address in all pools
+                 break;
+             }
+             ADD_FAILURE() << "Too many or not enough unique addresses generated.";
+             break;
+         }
+         if ( cnt>total ) {
+             ADD_FAILURE() << "Too many unique addresses generated.";
+             break;
+         }
+     }
+     delete alloc;
+ }
+ }; // end of anonymous namespace
index 897301a7fa881d945c79c3b570fa426175fe7daa,66521bae617ef6186037c6261e8b511a864fea16..64ce9b1653c8aec07323a4e6abe19a3bafa8fb12
@@@ -24,281 -25,167 +24,257 @@@ using namespace std
  using namespace isc;
  using namespace isc::asiolink;
  using namespace isc::dhcp;
 -using namespace isc::dhcp::test; // Memfile_LeaseMgr
  
- // This is a concrete implementation of a Lease database.
- // It does not do anything useful now, and is used for abstract LeaseMgr
- // class testing. It may later evolve into more useful backend if the
- // need arises. We can reuse code from memfile benchmark. See code in
- // tests/tools/dhcp-ubench/memfile_bench.{cc|h}
- class Memfile_LeaseMgr : public LeaseMgr {
 -namespace {
 -// empty class for now, but may be extended once Addr6 becomes bigger
 -class LeaseMgrTest : public ::testing::Test {
++// This is a concrete implementation of a Lease database.  It does not do
++// anything useful and is used for abstract LeaseMgr class testing.
++class ConcreteLeaseMgr : public LeaseMgr {
  public:
 -    LeaseMgrTest() {
 -    }
 -};
  
 -// This test checks if the LeaseMgr can be instantiated and that it
 -// parses parameters string properly.
 -TEST_F(LeaseMgrTest, constructor) {
 -
 -    // should not throw any exceptions here
 -    Memfile_LeaseMgr * leaseMgr = new Memfile_LeaseMgr("");
 -    delete leaseMgr;
 -
 -    leaseMgr = new Memfile_LeaseMgr("param1=value1 param2=value2");
 -
 -    EXPECT_EQ("value1", leaseMgr->getParameter("param1"));
 -    EXPECT_EQ("value2", leaseMgr->getParameter("param2"));
 -    EXPECT_THROW(leaseMgr->getParameter("param3"), BadValue);
 +    /// @brief The sole lease manager constructor
 +    ///
 +    /// dbconfig is a generic way of passing parameters. Parameters
 +    /// are passed in the "name=value" format, separated by spaces.
 +    /// Values may be enclosed in double quotes, if needed.
 +    ///
 +    /// @param parameters A data structure relating keywords and values
 +    ///        concerned with the database.
-     Memfile_LeaseMgr(const LeaseMgr::ParameterMap& parameters);
++    ConcreteLeaseMgr(const LeaseMgr::ParameterMap& parameters)
++        : LeaseMgr(parameters)
++    {}
 +
-     /// @brief Destructor (closes file)
-     virtual ~Memfile_LeaseMgr();
++    /// @brief Destructor
++    virtual ~ConcreteLeaseMgr()
++    {}
 +
 +    /// @brief Adds an IPv4 lease.
 +    ///
 +    /// @param lease lease to be added
-     virtual bool addLease(const Lease4Ptr& lease);
++    virtual bool addLease(const Lease4Ptr&) {
++        return (false);
++    }
  
 -    delete leaseMgr;
 -}
 +    /// @brief Adds an IPv6 lease.
 +    ///
 +    /// @param lease lease to be added
-     virtual bool addLease(const Lease6Ptr& lease);
++    virtual bool addLease(const Lease6Ptr&) {
++        return (false);
++    }
  
 -// There's no point in calling any other methods in LeaseMgr, as they
 -// are purely virtual, so we would only call Memfile_LeaseMgr methods.
 -// Those methods are just stubs that does not return anything.
 -// It seems likely that we will need to extend the memfile code for
 -// allocation engine tests, so we may implement tests that call
 -// Memfile_LeaseMgr methods then.
 +    /// @brief Returns existing IPv4 lease for specified IPv4 address.
 +    ///
 +    /// @param addr address of the searched lease
 +    ///
-     /// @return a collection of leases
-     virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr) const;
++    /// @return smart pointer to the lease (or NULL if a lease is not found)
++    virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress&) const {
++        return (Lease4Ptr());
++    }
  
 -TEST_F(LeaseMgrTest, addGetDelete) {
 -    Memfile_LeaseMgr * leaseMgr = new Memfile_LeaseMgr("");
 +    /// @brief Returns existing IPv4 lease for specific address and subnet
 +    /// @param addr address of the searched lease
 +    /// @param subnet_id ID of the subnet the lease must belong to
 +    ///
 +    /// @return smart pointer to the lease (or NULL if a lease is not found)
-     virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr,
-                                 SubnetID subnet_id) const;
++    virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress&,
++                                SubnetID) const {
++        return (Lease4Ptr());
++    }
  
 -    IOAddress addr("2001:db8:1::456");
 +    /// @brief Returns existing IPv4 leases for specified hardware address.
 +    ///
 +    /// Although in the usual case there will be only one lease, for mobile
 +    /// clients or clients with multiple static/fixed/reserved leases there
 +    /// can be more than one. Thus return type is a container, not a single
 +    /// pointer.
 +    ///
 +    /// @param hwaddr hardware address of the client
 +    ///
 +    /// @return lease collection
-     virtual Lease4Collection getLease4(const HWAddr& hwaddr) const;
++    virtual Lease4Collection getLease4(const HWAddr&) const {
++        return (Lease4Collection());
++    }
  
 -    uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
 -    DuidPtr duid(new DUID(llt, sizeof(llt)));
 +    /// @brief Returns existing IPv4 leases for specified hardware address
 +    ///        and a subnet
 +    ///
 +    /// There can be at most one lease for a given HW address in a single
 +    /// pool, so this method with either return a single lease or NULL.
 +    ///
 +    /// @param hwaddr hardware address of the client
 +    /// @param subnet_id identifier of the subnet that lease must belong to
 +    ///
 +    /// @return a pointer to the lease (or NULL if a lease is not found)
-     virtual Lease4Ptr getLease4(const HWAddr& hwaddr,
-                                 SubnetID subnet_id) const;
++    virtual Lease4Ptr getLease4(const HWAddr&, SubnetID) const {
++        return (Lease4Ptr());
++    }
  
 -    uint32_t iaid = 7; // just a number
 +    /// @brief Returns existing IPv4 lease for specified client-id
 +    ///
 +    /// @param clientid client identifier
-     virtual Lease4Collection getLease4(const ClientId& clientid) const;
++    ///
++    /// @return lease collection
++    virtual Lease4Collection getLease4(const ClientId&) const {
++        return (Lease4Collection());
++    }
  
 -    SubnetID subnet_id = 8; // just another number
 +    /// @brief Returns existing IPv4 lease for specified client-id
 +    ///
 +    /// There can be at most one lease for a given HW address in a single
 +    /// pool, so this method with either return a single lease or NULL.
 +    ///
 +    /// @param clientid client identifier
 +    /// @param subnet_id identifier of the subnet that lease must belong to
 +    ///
 +    /// @return a pointer to the lease (or NULL if a lease is not found)
-     virtual Lease4Ptr getLease4(const ClientId& clientid,
-                                 SubnetID subnet_id) const;
++    virtual Lease4Ptr getLease4(const ClientId&, SubnetID) const {
++        return (Lease4Ptr());
++    }
  
 -    Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr,
 -                               duid, iaid, 100, 200, 50, 80,
 -                               subnet_id));
 +    /// @brief Returns existing IPv6 lease for a given IPv6 address.
 +    ///
 +    /// @param addr address of the searched lease
 +    ///
 +    /// @return smart pointer to the lease (or NULL if a lease is not found)
-     Lease6Ptr getLease6(const isc::asiolink::IOAddress& addr) const;
++    Lease6Ptr getLease6(const isc::asiolink::IOAddress&) const {
++        return (Lease6Ptr());
++    }
  
 -    EXPECT_TRUE(leaseMgr->addLease(lease));
 +    /// @brief Returns existing IPv6 lease for a given DUID+IA combination
 +    ///
 +    /// @param duid client DUID
 +    /// @param iaid IA identifier
 +    ///
 +    /// @return collection of IPv6 leases
-     Lease6Collection getLease6(const DUID& duid, uint32_t iaid) const;
++    Lease6Collection getLease6(const DUID&, uint32_t) const {
++        return (Lease6Collection());
++    }
  
 -    // should not be allowed to add a second lease with the same address
 -    EXPECT_FALSE(leaseMgr->addLease(lease));
 +    /// @brief Returns existing IPv6 lease for a given DUID+IA combination
 +    ///
 +    /// @param duid client DUID
 +    /// @param iaid IA identifier
 +    /// @param subnet_id identifier of the subnet the lease must belong to
 +    ///
 +    /// @return smart pointer to the lease (or NULL if a lease is not found)
-     Lease6Ptr getLease6(const DUID& duid, uint32_t iaid, SubnetID subnet_id) const;
++    Lease6Ptr getLease6(const DUID&, uint32_t, SubnetID) const {
++        return (Lease6Ptr());
++    }
  
 -    Lease6Ptr x = leaseMgr->getLease6(IOAddress("2001:db8:1::234"));
 -    EXPECT_EQ(Lease6Ptr(), x);
 +    /// @brief Updates IPv4 lease.
 +    ///
 +    /// @param lease4 The lease to be updated.
 +    ///
 +    /// If no such lease is present, an exception will be thrown.
-     void updateLease4(const Lease4Ptr& lease4);
++    void updateLease4(const Lease4Ptr&) {}
 +
 +    /// @brief Updates IPv4 lease.
 +    ///
 +    /// @param lease4 The lease to be updated.
 +    ///
 +    /// If no such lease is present, an exception will be thrown.
-     void updateLease6(const Lease6Ptr& lease6);
++    void updateLease6(const Lease6Ptr&) {}
 +
 +    /// @brief Deletes a lease.
 +    ///
 +    /// @param addr IPv4 address of the lease to be deleted.
 +    ///
 +    /// @return true if deletion was successful, false if no such lease exists
-     bool deleteLease4(const isc::asiolink::IOAddress& addr);
++    bool deleteLease4(const isc::asiolink::IOAddress&) {
++        return (false);
++    }
  
 -    x = leaseMgr->getLease6(IOAddress("2001:db8:1::456"));
 -    ASSERT_TRUE(x);
 +    /// @brief Deletes a lease.
 +    ///
 +    /// @param addr IPv4 address of the lease to be deleted.
 +    ///
 +    /// @return true if deletion was successful, false if no such lease exists
-     bool deleteLease6(const isc::asiolink::IOAddress& addr);
++    bool deleteLease6(const isc::asiolink::IOAddress&) {
++        return (false);
++    }
  
 -    EXPECT_EQ(x->addr_.toText(), addr.toText());
 -    EXPECT_TRUE(*x->duid_ == *duid);
 -    EXPECT_EQ(x->iaid_, iaid);
 -    EXPECT_EQ(x->subnet_id_, subnet_id);
 -
 -    // These are not important from lease management perspective, but
 -    // let's check them anyway.
 -    EXPECT_EQ(x->type_, Lease6::LEASE_IA_NA);
 -    EXPECT_EQ(x->preferred_lft_, 100);
 -    EXPECT_EQ(x->valid_lft_, 200);
 -    EXPECT_EQ(x->t1_, 50);
 -    EXPECT_EQ(x->t2_, 80);
 -
 -    // Test getLease6(duid, iaid, subnet_id) - positive case
 -    Lease6Ptr y = leaseMgr->getLease6(*duid, iaid, subnet_id);
 -    ASSERT_TRUE(y);
 -    EXPECT_TRUE(*y->duid_ == *duid);
 -    EXPECT_EQ(y->iaid_, iaid);
 -    EXPECT_EQ(y->addr_.toText(), addr.toText());
 -
 -    // Test getLease6(duid, iaid, subnet_id) - wrong iaid
 -    uint32_t invalid_iaid = 9; // no such iaid
 -    y = leaseMgr->getLease6(*duid, invalid_iaid, subnet_id);
 -    EXPECT_FALSE(y);
 -
 -    uint32_t invalid_subnet_id = 999;
 -    y = leaseMgr->getLease6(*duid, iaid, invalid_subnet_id);
 -    EXPECT_FALSE(y);
 -
 -    // truncated duid
 -    DuidPtr invalid_duid(new DUID(llt, sizeof(llt) - 1));
 -    y = leaseMgr->getLease6(*invalid_duid, iaid, subnet_id);
 -    EXPECT_FALSE(y);
 -
 -    // should return false - there's no such address
 -    EXPECT_FALSE(leaseMgr->deleteLease6(IOAddress("2001:db8:1::789")));
 -
 -    // this one should succeed
 -    EXPECT_TRUE(leaseMgr->deleteLease6(IOAddress("2001:db8:1::456")));
 -
 -    // after the lease is deleted, it should really be gone
 -    x = leaseMgr->getLease6(IOAddress("2001:db8:1::456"));
 -    EXPECT_EQ(Lease6Ptr(), x);
 -
 -    delete leaseMgr;
 -}
 +    /// @brief Returns backend name.
 +    ///
 +    /// Each backend have specific name, e.g. "mysql" or "sqlite".
-     std::string getName() const { return "memfile"; }
++    std::string getName() const {
++        return (std::string("concrete"));
++    }
  
 -// This test checks there that leaseMgr is really a singleton and that
 -// no more than one can be created.
 -TEST_F(LeaseMgrTest, singleton) {
 -    Memfile_LeaseMgr* leaseMgr1 = NULL;
 -    Memfile_LeaseMgr* leaseMgr2 = NULL;
 +    /// @brief Returns description of the backend.
 +    ///
 +    /// This description may be multiline text that describes the backend.
-     std::string getDescription() const;
++    std::string getDescription() const {
++        return (std::string("This is a dummy concrete backend implementation."));
++    }
  
 -    EXPECT_THROW(LeaseMgr::instance(), InvalidOperation);
 +    /// @brief Returns backend version.
 +    std::pair<uint32_t, uint32_t> getVersion() const {
 +        return (make_pair(uint32_t(0), uint32_t(0)));
 +    }
  
 -    EXPECT_NO_THROW( leaseMgr1 = new Memfile_LeaseMgr("") );
 +    /// @brief Commit transactions
 +    void commit() {
 +    }
  
 -    EXPECT_NO_THROW(LeaseMgr::instance());
 +    /// @brief Rollback transactions
 +    void rollback() {
 +    }
-     using LeaseMgr::getParameter;
- protected:
 +};
  
- Memfile_LeaseMgr::Memfile_LeaseMgr(const LeaseMgr::ParameterMap& parameters)
-     : LeaseMgr(parameters) {
- }
- Memfile_LeaseMgr::~Memfile_LeaseMgr() {
- }
- bool Memfile_LeaseMgr::addLease(const boost::shared_ptr<isc::dhcp::Lease4>&) {
-     return (false);
- }
- bool Memfile_LeaseMgr::addLease(const boost::shared_ptr<isc::dhcp::Lease6>&) {
-     return (false);
- }
- Lease4Ptr Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress&) const {
-     return (Lease4Ptr());
- }
- Lease4Collection Memfile_LeaseMgr::getLease4(const HWAddr& ) const {
-     return (Lease4Collection());
- }
- Lease4Ptr Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress & ,
-                                       SubnetID) const {
-     return (Lease4Ptr());
- }
- Lease4Ptr Memfile_LeaseMgr::getLease4(const HWAddr&,
-                                       SubnetID) const {
-     return (Lease4Ptr());
- }
- Lease4Ptr Memfile_LeaseMgr::getLease4(const ClientId&,
-                                       SubnetID) const {
-     return (Lease4Ptr());
- }
- Lease4Collection Memfile_LeaseMgr::getLease4(const ClientId& ) const {
-     return (Lease4Collection());
- }
- Lease6Ptr Memfile_LeaseMgr::getLease6(const isc::asiolink::IOAddress&) const {
-     return (Lease6Ptr());
- }
- Lease6Collection Memfile_LeaseMgr::getLease6(const DUID& , uint32_t ) const {
-     return (Lease6Collection());
- }
- Lease6Ptr Memfile_LeaseMgr::getLease6(const DUID&, uint32_t,
-                                       SubnetID) const {
-     return (Lease6Ptr());
- }
- void Memfile_LeaseMgr::updateLease4(const Lease4Ptr&) {
- }
- void Memfile_LeaseMgr::updateLease6(const Lease6Ptr&) {
- }
- bool Memfile_LeaseMgr::deleteLease4(const isc::asiolink::IOAddress&) {
-     return (false);
- }
- bool Memfile_LeaseMgr::deleteLease6(const isc::asiolink::IOAddress&) {
-     return (false);
- }
- std::string Memfile_LeaseMgr::getDescription() const {
-     return (string("This is a dummy memfile backend implementation.\n"
-                    "It does not offer any useful lease management and its only\n"
-                    "purpose is to test abstract lease manager API."));
- }
 -    // There can be only one instance of any LeaseMgr derived
 -    // objects instantiated at any time.
 -    ASSERT_THROW(leaseMgr2 = new Memfile_LeaseMgr(""), InvalidOperation);
 +namespace {
 +// empty class for now, but may be extended once Addr6 becomes bigger
 +class LeaseMgrTest : public ::testing::Test {
 +public:
 +    LeaseMgrTest() {
 +    }
 +};
  
 -    delete leaseMgr1;
 +// This test checks if the LeaseMgr can be instantiated and that it
 +// parses parameters string properly.
 +TEST_F(LeaseMgrTest, getParameter) {
  
 -    ASSERT_NO_THROW(leaseMgr2 = new Memfile_LeaseMgr("") );
 +    LeaseMgr::ParameterMap pmap;
 +    pmap[std::string("param1")] = std::string("value1");
 +    pmap[std::string("param2")] = std::string("value2");
-     Memfile_LeaseMgr leasemgr(pmap);
++    ConcreteLeaseMgr leasemgr(pmap);
  
 -    delete leaseMgr2;
 +    EXPECT_EQ("value1", leasemgr.getParameter("param1"));
 +    EXPECT_EQ("value2", leasemgr.getParameter("param2"));
 +    EXPECT_THROW(leasemgr.getParameter("param3"), BadValue);
  }
  
- // are purely virtual, so we would only call Memfile_LeaseMgr methods.
- // Those methods are just stubs that does not return anything.
- // It seems likely that we will need to extend the memfile code for
- // allocation engine tests, so we may implement tests that call
- // Memfile_LeaseMgr methods then.
 +// There's no point in calling any other methods in LeaseMgr, as they
 -TEST(Lease6, ctor) {
++// are purely virtual, so we would only call ConcreteLeaseMgr methods.
++// Those methods are just stubs that do not return anything.
 +
++// 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, Lease6Constructor) {
+     IOAddress addr("2001:db8:1::456");
+     uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+     DuidPtr duid(new DUID(llt, sizeof(llt)));
+     uint32_t iaid = 7; // just a number
+     SubnetID subnet_id = 8; // just another number
+     Lease6Ptr x(new Lease6(Lease6::LEASE_IA_NA, addr,
+                            duid, iaid, 100, 200, 50, 80,
+                            subnet_id));
+     EXPECT_TRUE(x->addr_ == addr);
+     EXPECT_TRUE(*x->duid_ == *duid);
+     EXPECT_TRUE(x->iaid_ == iaid);
+     EXPECT_TRUE(x->subnet_id_ == subnet_id);
+     EXPECT_TRUE(x->type_ == Lease6::LEASE_IA_NA);
+     EXPECT_TRUE(x->preferred_lft_ == 100);
+     EXPECT_TRUE(x->valid_lft_ == 200);
+     EXPECT_TRUE(x->t1_ == 50);
+     EXPECT_TRUE(x->t2_ == 80);
+     // Lease6 must be instantiated with a DUID, not with NULL pointer
+     EXPECT_THROW(new Lease6(Lease6::LEASE_IA_NA, addr,
+                             DuidPtr(), iaid, 100, 200, 50, 80,
+                             subnet_id), InvalidOperation);
+ }
  }; // end of anonymous namespace
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c625a16f28c48a76de6a7890584f5ab965fed1b7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,130 @@@
++// Copyright (C) 2012 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
++// copyright notice and this permission notice appear in all copies.
++//
++// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
++// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
++// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
++// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
++// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
++// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
++// PERFORMANCE OF THIS SOFTWARE.
++
++#include <config.h>
++#include <iostream>
++#include <sstream>
++#include <gtest/gtest.h>
++#include <asiolink/io_address.h>
++
++#include <dhcp/lease_mgr.h>
++#include <dhcp/duid.h>
++#include <dhcp/memfile_lease_mgr.h>
++
++using namespace std;
++using namespace isc;
++using namespace isc::asiolink;
++using namespace isc::dhcp;
++
++namespace {
++// empty class for now, but may be extended once Addr6 becomes bigger
++class MemfileLeaseMgrTest : public ::testing::Test {
++public:
++    MemfileLeaseMgrTest() {
++    }
++};
++
++// This test checks if the LeaseMgr can be instantiated and that it
++// parses parameters string properly.
++TEST_F(MemfileLeaseMgrTest, constructor) {
++
++    const LeaseMgr::ParameterMap pmap;  // Empty parameter map
++    boost::scoped_ptr<Memfile_LeaseMgr> lease_mgr;
++
++    ASSERT_NO_THROW(lease_mgr.reset(new Memfile_LeaseMgr(pmap)));
++}
++
++// There's no point in calling any other methods in LeaseMgr, as they
++// are purely virtual, so we would only call Memfile_LeaseMgr methods.
++// Those methods are just stubs that does not return anything.
++// It seems likely that we will need to extend the memfile code for
++// allocation engine tests, so we may implement tests that call
++// Memfile_LeaseMgr methods then.
++
++TEST_F(MemfileLeaseMgrTest, addGetDelete) {
++    const LeaseMgr::ParameterMap pmap;  // Empty parameter map
++    boost::scoped_ptr<Memfile_LeaseMgr> lease_mgr(new Memfile_LeaseMgr(pmap));
++
++    IOAddress addr("2001:db8:1::456");
++
++    uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
++    DuidPtr duid(new DUID(llt, sizeof(llt)));
++
++    uint32_t iaid = 7; // just a number
++
++    SubnetID subnet_id = 8; // just another number
++
++    Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr,
++                               duid, iaid, 100, 200, 50, 80,
++                               subnet_id));
++
++    EXPECT_TRUE(lease_mgr->addLease(lease));
++
++    // should not be allowed to add a second lease with the same address
++    EXPECT_FALSE(lease_mgr->addLease(lease));
++
++    Lease6Ptr x = lease_mgr->getLease6(IOAddress("2001:db8:1::234"));
++    EXPECT_EQ(Lease6Ptr(), x);
++
++    x = lease_mgr->getLease6(IOAddress("2001:db8:1::456"));
++    ASSERT_TRUE(x);
++
++    EXPECT_EQ(x->addr_.toText(), addr.toText());
++    EXPECT_TRUE(*x->duid_ == *duid);
++    EXPECT_EQ(x->iaid_, iaid);
++    EXPECT_EQ(x->subnet_id_, subnet_id);
++
++    // These are not important from lease management perspective, but
++    // let's check them anyway.
++    EXPECT_EQ(x->type_, Lease6::LEASE_IA_NA);
++    EXPECT_EQ(x->preferred_lft_, 100);
++    EXPECT_EQ(x->valid_lft_, 200);
++    EXPECT_EQ(x->t1_, 50);
++    EXPECT_EQ(x->t2_, 80);
++
++    // Test getLease6(duid, iaid, subnet_id) - positive case
++    Lease6Ptr y = lease_mgr->getLease6(*duid, iaid, subnet_id);
++    ASSERT_TRUE(y);
++    EXPECT_TRUE(*y->duid_ == *duid);
++    EXPECT_EQ(y->iaid_, iaid);
++    EXPECT_EQ(y->addr_.toText(), addr.toText());
++
++    // Test getLease6(duid, iaid, subnet_id) - wrong iaid
++    uint32_t invalid_iaid = 9; // no such iaid
++    y = lease_mgr->getLease6(*duid, invalid_iaid, subnet_id);
++    EXPECT_FALSE(y);
++
++    uint32_t invalid_subnet_id = 999;
++    y = lease_mgr->getLease6(*duid, iaid, invalid_subnet_id);
++    EXPECT_FALSE(y);
++
++    // truncated duid
++    DuidPtr invalid_duid(new DUID(llt, sizeof(llt) - 1));
++    y = lease_mgr->getLease6(*invalid_duid, iaid, subnet_id);
++    EXPECT_FALSE(y);
++
++    // should return false - there's no such address
++    EXPECT_FALSE(lease_mgr->deleteLease6(IOAddress("2001:db8:1::789")));
++
++    // this one should succeed
++    EXPECT_TRUE(lease_mgr->deleteLease6(IOAddress("2001:db8:1::456")));
++
++    // after the lease is deleted, it should really be gone
++    x = lease_mgr->getLease6(IOAddress("2001:db8:1::456"));
++    EXPECT_EQ(Lease6Ptr(), x);
++}
++
++// TODO: Write more memfile tests
++
++}; // end of anonymous namespace