// Copyright (C) 2012-2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef LEASE_MGR_H
#define LEASE_MGR_H

#include <asiolink/io_address.h>
#include <asiolink/io_service.h>
#include <dhcp/duid.h>
#include <dhcp/option.h>
#include <dhcp/hwaddr.h>
#include <dhcpsrv/lease.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/db_exceptions.h>
#include <dhcpsrv/sql_common.h>

#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>

#include <fstream>
#include <iostream>
#include <map>
#include <string>
#include <utility>
#include <vector>

/// @file lease_mgr.h
/// @brief An abstract API for lease database
///
/// This file contains declarations of Lease4, Lease6 and LeaseMgr classes.
/// They are essential components of the interface to any database backend.
/// Each concrete database backend (e.g. MySQL) will define a class derived
/// from LeaseMgr class.
///
/// Failover considerations:
/// There are no intermediate plans to implement DHCPv4 failover
/// (draft-ietf-dhc-failover-12.txt). Currently (Oct. 2012) the DHCPv6 failover
/// is being defined in DHC WG in IETF (draft-ietf-dhcpv6-failover-requirements,
/// draft-ietf-dhcpv6-failover-design), but the work is not advanced enough
/// for implementation plans yet. v4 failover requires additional parameters
/// to be kept with a lease. It is likely that v6 failover will require similar
/// fields. Such implementation will require database schema extension.
/// We have designed a way to expand/upgrade schemas during upgrades: a database
/// schema is versioned and sanity checks about required version will be done
/// upon start and/or upgrade. With this mechanism in place, we can add new
/// fields to the database. In particular we can use that capability to
/// introduce failover related fields.
///
/// However, there is another approach that can be reliably used to provide
/// failover, even without the actual failover protocol implemented. As the
/// first backend will use MySQL, we will be able to use Multi-Master capability
/// offered by MySQL and use two separate Kea instances connecting to the
/// same database.
///
/// Nevertheless, we hope to have failover protocol eventually implemented in
/// the Kea.

namespace isc {
namespace dhcp {

/// @brief Pair containing major and minor versions
typedef std::pair<uint32_t, uint32_t> VersionPair;

/// @brief Contains a single row of lease statistical data
///
/// The contents of the row consist of a subnet ID, a lease
/// type, a lease state, and the number of leases in that state
/// for that type for that subnet ID.
struct LeaseStatsRow {
    /// @brief Default constructor
    LeaseStatsRow() :
        subnet_id_(0), lease_type_(Lease::TYPE_NA),
        lease_state_(Lease::STATE_DEFAULT), state_count_(0) {
    }

    /// @brief Constructor
    ///
    /// Constructor which defaults the type to TYPE_NA.
    ///
    /// @param subnet_id The subnet id to which this data applies
    /// @param lease_state The lease state counted
    /// @param state_count The count of leases in the lease state
    LeaseStatsRow(const SubnetID& subnet_id, const uint32_t lease_state,
                  const int64_t state_count)
        : subnet_id_(subnet_id), lease_type_(Lease::TYPE_NA),
          lease_state_(lease_state), state_count_(state_count) {
    }

    /// @brief Constructor
    ///
    /// @param subnet_id The subnet id to which this data applies
    /// @param lease_type The lease type for this state count
    /// @param lease_state The lease state counted
    /// @param state_count The count of leases in the lease state
    LeaseStatsRow(const SubnetID& subnet_id, const Lease::Type& lease_type,
                  const uint32_t lease_state, const int64_t state_count)
        : subnet_id_(subnet_id), lease_type_(lease_type),
          lease_state_(lease_state), state_count_(state_count) {
    }

    /// @brief Less-than operator
    bool operator< (const LeaseStatsRow &rhs) const {
        if (subnet_id_ < rhs.subnet_id_) {
            return (true);
        }

        if (subnet_id_ == rhs.subnet_id_ &&
            lease_type_ < rhs.lease_type_) {
                return (true);
        }

        if (subnet_id_ == rhs.subnet_id_ &&
            lease_type_ == rhs.lease_type_ &&
            lease_state_ < rhs.lease_state_) {
                return (true);
        }

        return (false);
    }

    /// @brief The subnet ID to which this data applies
    SubnetID subnet_id_;
    /// @brief The lease_type to which the count applies
    Lease::Type lease_type_;
    /// @brief The lease_state to which the count applies
    uint32_t lease_state_;
    /// @brief state_count The count of leases in the lease state
    int64_t state_count_;
};

/// @brief Base class for fulfilling a statistical lease data query
///
/// LeaseMgr derivations implement this class such that it provides
/// up to date statistical lease data organized as rows of LeaseStatsRow
/// instances. The rows must be accessible in ascending order by subnet id.
class LeaseStatsQuery {
public:
    /// @brief Defines the types of selection criteria supported
    typedef enum {
        ALL_SUBNETS,
        SINGLE_SUBNET,
        SUBNET_RANGE
    } SelectMode;

    /// @brief Default constructor
    /// The query created will return statistics for all subnets
    LeaseStatsQuery();

    /// @brief Constructor to query for a single subnet's stats
    ///
    /// The query created will return statistics for a single subnet
    ///
    /// @param subnet_id id of the subnet for which stats are desired
    /// @throw BadValue if sunbet_id given is 0.
    LeaseStatsQuery(const SubnetID& subnet_id);

    /// @brief Constructor to query for the stats for a range of subnets
    ///
    /// The query created will return statistics for the inclusive range of
    /// subnets described by first and last sunbet IDs.
    ///
    /// @param first_subnet_id first subnet in the range of subnets
    /// @param last_subnet_id last subnet in the range of subnets
    /// @throw BadValue if either value given is 0 or if last <= first.
    LeaseStatsQuery(const SubnetID& first_subnet_id, const SubnetID& last_subnet_id);

    /// @brief virtual destructor
    virtual ~LeaseStatsQuery() {};

    /// @brief Executes the query
    ///
    /// This method should conduct whatever steps are required to
    /// calculate the lease statistical data by examining the
    /// lease data and making that results available row by row.
    virtual void start() {};

    /// @brief Fetches the next row of data
    ///
    /// @param[out] row Storage into which the row is fetched
    ///
    /// @return True if a row was fetched, false if there are no
    /// more rows.
    virtual bool getNextRow(LeaseStatsRow& row);

    /// @brief Returns the value of first subnet ID specified (or zero)
    SubnetID getFirstSubnetID() const {
        return (first_subnet_id_);
    };

    /// @brief Returns the value of last subnet ID specified (or zero)
    SubnetID getLastSubnetID() const {
        return (last_subnet_id_);
    };

    /// @brief Returns the selection criteria mode
    /// The value returned is based upon the constructor variant used
    /// and it indicates which query variant will be executed.
    SelectMode getSelectMode() const {
        return (select_mode_);
    };

protected:
    /// @brief First (or only) subnet_id in the selection criteria
    SubnetID first_subnet_id_;

    /// @brief Last subnet_id in the selection criteria when a range is given
    SubnetID last_subnet_id_;

private:
    /// @brief Indicates the type of selection criteria specified
    SelectMode select_mode_;
};

/// @brief Defines a pointer to a LeaseStatsQuery.
typedef boost::shared_ptr<LeaseStatsQuery> LeaseStatsQueryPtr;

/// @brief Defines a pointer to a LeaseStatsRow.
typedef boost::shared_ptr<LeaseStatsRow> LeaseStatsRowPtr;

/// @brief Abstract Lease Manager
///
/// This is an abstract API for lease database backends. It provides unified
/// interface to all backends. As this is an abstract class, it should not
/// be used directly, but rather specialized derived class should be used
/// instead.
///
/// 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:
    /// @brief Constructor
    ///
    LeaseMgr()
    {}

    /// @brief Destructor
    virtual ~LeaseMgr()
    {}

    /// @brief Class method to return extended version info
    /// This class method must be redeclared and redefined in derived classes
    static std::string getDBVersion();

    /// @brief Adds an IPv4 lease.
    ///
    /// @param lease lease to be added
    ///
    /// @result true if the lease was added, false if not (because a lease
    ///         with the same address was already there).
    virtual bool addLease(const Lease4Ptr& lease) = 0;

    /// @brief Adds an IPv6 lease.
    ///
    /// @param lease lease to be added
    ///
    /// @result true if the lease was added, false if not (because a lease
    ///         with the same address was already there).
    virtual bool addLease(const Lease6Ptr& lease) = 0;

    /// @brief Returns an IPv4 lease for specified IPv4 address
    ///
    /// This method return a lease that is associated with a given address.
    /// For other query types (by hardware addr, by client-id) there can be
    /// several leases in different subnets (e.g. for mobile clients that
    /// got address in different subnets). However, for a single address
    /// there can be only one lease, so this method returns a pointer to
    /// a single lease, not a container of leases.
    ///
    /// @param addr address of the searched lease
    ///
    /// @return smart pointer to the lease (or NULL if a lease is not found)
    virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr) const = 0;

    /// @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 isc::dhcp::HWAddr& hwaddr) const = 0;

    /// @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 will 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 isc::dhcp::HWAddr& hwaddr,
                                SubnetID subnet_id) const = 0;

    /// @brief Returns existing IPv4 lease for specified client-id
    ///
    /// 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 clientid client identifier
    ///
    /// @return lease collection
    virtual Lease4Collection getLease4(const ClientId& clientid) const = 0;

    /// @brief Returns existing IPv4 lease for a given client identifier,
    /// HW address and subnet identifier.
    ///
    /// @todo Consider whether this function is needed or not. In the basic
    /// DHCPv4 server implementation it is not used by the allocation engine.
    ///
    /// @param client_id A client identifier.
    /// @param hwaddr Hardware address.
    /// @param subnet_id A subnet identifier.
    ///
    /// @return A pointer to the lease or NULL if the lease is not found.
    virtual Lease4Ptr getLease4(const ClientId& client_id, const HWAddr& hwaddr,
                                SubnetID subnet_id) const = 0;

    /// @brief Returns existing IPv4 lease for specified client-id
    ///
    /// There can be at most one lease for a given client-id in a single
    /// pool, so this method will 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 = 0;

    /// @brief Returns all IPv4 leases for the particular subnet identifier.
    ///
    /// @param subnet_id subnet identifier.
    ///
    /// @return Lease collection (may be empty if no IPv4 lease found).
    virtual Lease4Collection getLeases4(SubnetID subnet_id) const = 0;

    /// @brief Returns all IPv4 leases.
    ///
    /// @return Lease collection (may be empty if no IPv4 lease found).
    virtual Lease4Collection getLeases4() const = 0;

    /// @brief Returns existing IPv6 lease for a given IPv6 address.
    ///
    /// For a given address, we assume that there will be only one lease.
    /// The assumption here is that there will not be site or link-local
    /// addresses used, so there is no way of having address duplication.
    ///
    /// @param type specifies lease type: (NA, TA or PD)
    /// @param addr address of the searched lease
    ///
    /// @return smart pointer to the lease (or NULL if a lease is not found)
    virtual Lease6Ptr getLease6(Lease::Type type,
                                const isc::asiolink::IOAddress& addr) const = 0;

    /// @brief Returns existing IPv6 leases for a given DUID+IA combination
    ///
    /// 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 type specifies lease type: (NA, TA or PD)
    /// @param duid client DUID
    /// @param iaid IA identifier
    ///
    /// @return Lease collection (may be empty if no lease is found)
    virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid,
                                        uint32_t iaid) const = 0;

    /// @brief Returns existing IPv6 lease for a given DUID+IA combination
    ///
    /// There may be more than one address, temp. address or prefix
    /// for specified duid/iaid/subnet-id tuple.
    ///
    /// @param type specifies lease type: (NA, TA or PD)
    /// @param duid client DUID
    /// @param iaid IA identifier
    /// @param subnet_id subnet id of the subnet the lease belongs to
    ///
    /// @return Lease collection (may be empty if no lease is found)
    virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid,
                                        uint32_t iaid, SubnetID subnet_id) const = 0;

    /// @brief returns zero or one IPv6 lease for a given duid+iaid+subnet_id
    ///
    /// This function is mostly intended to be used in unit-tests during the
    /// transition from single to multi address per IA. It may also be used
    /// in other cases where at most one lease is expected in the database.
    ///
    /// It is a wrapper around getLeases6(), which returns a collection of
    /// leases. That collection can be converted into a single pointer if
    /// there are no leases (NULL pointer) or one lease (use that single lease).
    /// If there are more leases in the collection, the function will
    /// throw MultipleRecords exception.
    ///
    /// Note: This method is not virtual on purpose. It is common for all
    /// backends.
    ///
    /// @param type specifies lease type: (NA, TA or PD)
    /// @param duid client DUID
    /// @param iaid IA identifier
    /// @param subnet_id subnet id of the subnet the lease belongs to
    ///
    /// @throw MultipleRecords if there is more than one lease matching
    ///
    /// @return Lease pointer (or NULL if none is found)
    Lease6Ptr getLease6(Lease::Type type, const DUID& duid,
                        uint32_t iaid, SubnetID subnet_id) const;

    /// @brief Returns all IPv6 leases for the particular subnet identifier.
    ///
    /// @param subnet_id subnet identifier.
    ///
    /// @return Lease collection (may be empty if no IPv6 lease found).
    virtual Lease6Collection getLeases6(SubnetID subnet_id) const = 0;

    /// @brief Returns all IPv6 leases.
    ///
    /// @return Lease collection (may be empty if no IPv6 lease found).
    virtual Lease6Collection getLeases6() const = 0;

    /// @brief Returns a collection of expired DHCPv4 leases.
    ///
    /// This method returns at most @c max_leases expired leases. The leases
    /// returned haven't been reclaimed, i.e. the database query must exclude
    /// reclaimed leases from the results returned.
    ///
    /// @param [out] expired_leases A container to which expired leases returned
    /// by the database backend are added.
    /// @param max_leases A maximum number of leases to be returned. If this
    /// value is set to 0, all expired (but not reclaimed) leases are returned.
    virtual void getExpiredLeases4(Lease4Collection& expired_leases,
                                   const size_t max_leases) const = 0;

    /// @brief Returns a collection of expired DHCPv6 leases.
    ///
    /// This method returns at most @c max_leases expired leases. The leases
    /// returned haven't been reclaimed, i.e. the database query must exclude
    /// reclaimed leases from the results returned.
    ///
    /// @param [out] expired_leases A container to which expired leases returned
    /// by the database backend are added.
    /// @param max_leases A maximum number of leases to be returned. If this
    /// value is set to 0, all expired (but not reclaimed) leases are returned.
    virtual void getExpiredLeases6(Lease6Collection& expired_leases,
                                   const size_t max_leases) const = 0;

    /// @brief Updates IPv4 lease.
    ///
    /// @param lease4 The lease to be updated.
    ///
    /// If no such lease is present, an exception will be thrown.
    virtual void updateLease4(const Lease4Ptr& lease4) = 0;

    /// @brief Updates IPv6 lease.
    ///
    /// @param lease6 The lease to be updated.
    virtual void updateLease6(const Lease6Ptr& lease6) = 0;

    /// @brief Deletes a lease.
    ///
    /// @param addr Address of the lease to be deleted. This can be an IPv4
    ///             address or an IPv6 address.
    ///
    /// @return true if deletion was successful, false if no such lease exists
    ///
    /// @throw isc::dhcp::DbOperationError An operation on the open database has
    ///        failed.
    virtual bool deleteLease(const isc::asiolink::IOAddress& addr) = 0;

    /// @brief Deletes all expired and reclaimed DHCPv4 leases.
    ///
    /// @param secs Number of seconds since expiration of leases before
    /// they can be removed. Leases which have expired later than this
    /// time will not be deleted.
    ///
    /// @return Number of leases deleted.
    virtual uint64_t deleteExpiredReclaimedLeases4(const uint32_t secs) = 0;

    /// @brief Deletes all expired and reclaimed DHCPv6 leases.
    ///
    /// @param secs Number of seconds since expiration of leases before
    /// they can be removed. Leases which have expired later than this
    /// time will not be deleted.
    ///
    /// @return Number of leases deleted.
    virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t secs) = 0;

    /// @brief Recalculates per-subnet and global stats for IPv4 leases
    ///
    /// This method recalculates the following statistics:
    /// per-subnet:
    /// - assigned-addresses
    /// - declined-addresses
    /// - declined-reclaimed-addresses (reset to zero)
    /// global:
    /// - declined-addresses
    /// - declined-reclaimed-addresses (reset to zero)
    ///
    /// It invokes the virtual method, startLeaseStatsQuery4(), which
    /// returns an instance of an LeaseStatsQuery.  The query
    /// query contains a "result set"  where each row is an LeaseStatRow
    /// that contains a subnet id, a lease type (currently always TYPE_NA),
    /// a lease state, and the number of leases of that type, in that state
    /// and is ordered by subnet id.  The method iterates over the
    /// result set rows, setting the appropriate statistic per subnet and
    /// adding to the appropriate global statistic.
    void recountLeaseStats4();

    /// @brief Creates and runs the IPv4 lease stats query for all subnets
    ///
    /// LeaseMgr derivations implement this method such that it creates and
    /// returns an instance of an LeaseStatsQuery whose result set has been
    /// populated with up to date IPv4 lease statistical data for all subnets.
    /// Each row of the result set is an LeaseStatRow which ordered ascending
    /// by subnet ID.
    ///
    /// @return A populated LeaseStatsQuery
    virtual LeaseStatsQueryPtr startLeaseStatsQuery4();

    /// @brief Creates and runs the IPv4 lease stats query for a single subnet
    ///
    /// LeaseMgr derivations implement this method such that it creates and
    /// returns an instance of an LeaseStatsQuery whose result set has been
    /// populated with up to date IPv4 lease statistical data for a single
    /// subnet.  Each row of the result set is an LeaseStatRow.
    ///
    /// @param subnet_id id of the subnet for which stats are desired
    /// @return A populated LeaseStatsQuery
    virtual LeaseStatsQueryPtr startSubnetLeaseStatsQuery4(const SubnetID& subnet_id);

    /// @brief Creates and runs the IPv4 lease stats query for a single subnet
    ///
    /// LeaseMgr derivations implement this method such that it creates and
    /// returns an instance of an LeaseStatsQuery whose result set has been
    /// populated with up to date IPv4 lease statistical data for an inclusive
    /// range of subnets. Each row of the result set is an LeaseStatRow which
    /// ordered ascending by subnet ID.
    ///
    /// @param first_subnet_id first subnet in the range of subnets
    /// @param last_subnet_id last subnet in the range of subnets
    /// @return A populated LeaseStatsQuery
    virtual LeaseStatsQueryPtr startSubnetRangeLeaseStatsQuery4(const SubnetID& first_subnet_id,
                                                                const SubnetID& last_subnet_id);

    /// @brief Recalculates per-subnet and global stats for IPv6 leases
    ///
    /// This method recalculates the following statistics:
    /// per-subnet:
    /// - assigned-addresses
    /// - declined-addresses
    /// - declined-reclaimed-addresses (reset to zero)
    /// - assigned-pds
    /// global:
    /// - declined-addresses
    /// - declined-reclaimed-addresses (reset to zero)
    ///
    /// It invokes the virtual method, startLeaseStatsQuery6(), which
    /// returns an instance of an LeaseStatsQuery.  The query contains
    /// a "result set" where each row is an LeaseStatRow that contains
    /// a subnet id, a lease type, a lease state, and the number of leases
    /// of that type, in that state and is ordered by subnet id. The method
    /// iterates over the result set rows, setting the appropriate statistic
    /// per subnet and adding to the appropriate global statistic.
    void recountLeaseStats6();

    /// @brief Creates and runs the IPv6 lease stats query for all subnets
    ///
    /// LeaseMgr derivations implement this method such that it creates and
    /// returns an instance of an LeaseStatsQuery whose result set has been
    /// populated with up to date IPv6 lease statistical data for all subnets.
    /// Each row of the result set is an LeaseStatRow which ordered ascending
    /// by subnet ID.
    ///
    /// @return A populated LeaseStatsQuery
    virtual LeaseStatsQueryPtr startLeaseStatsQuery6();

    /// @brief Creates and runs the IPv6 lease stats query for a single subnet
    ///
    /// LeaseMgr derivations implement this method such that it creates and
    /// returns an instance of an LeaseStatsQuery whose result set has been
    /// populated with up to date IPv6 lease statistical data for a single
    /// subnet.  Each row of the result set is an LeaseStatRow.
    ///
    /// @param subnet_id id of the subnet for which stats are desired
    /// @return A populated LeaseStatsQuery
    virtual LeaseStatsQueryPtr startSubnetLeaseStatsQuery6(const SubnetID& subnet_id);

    /// @brief Creates and runs the IPv6 lease stats query for a single subnet
    ///
    /// LeaseMgr derivations implement this method such that it creates and
    /// returns an instance of an LeaseStatsQuery whose result set has been
    /// populated with up to date IPv6 lease statistical data for an inclusive
    /// range of subnets. Each row of the result set is an LeaseStatRow which
    /// ordered ascending by subnet ID.
    ///
    /// @param first_subnet_id first subnet in the range of subnets
    /// @param last_subnet_id last subnet in the range of subnets
    /// @return A populated LeaseStatsQuery
    virtual LeaseStatsQueryPtr startSubnetRangeLeaseStatsQuery6(const SubnetID& first_subnet_id,
                                                                const SubnetID& last_subnet_id);

    /// @brief Virtual method which removes specified leases.
    ///
    /// This rather dangerous method is able to remove all leases from specified
    /// subnet.
    ///
    /// @param subnet_id identifier of the subnet (or 0 for all subnets)
    /// @return number of leases removed.
    virtual size_t wipeLeases4(const SubnetID& subnet_id) = 0;

    /// @brief Virtual method which removes specified leases.
    ///
    /// This rather dangerous method is able to remove all leases from specified
    /// subnet.
    ///
    /// @param subnet_id identifier of the subnet (or 0 for all subnets)
    /// @return number of leases removed.
    virtual size_t wipeLeases6(const SubnetID& subnet_id) = 0;

    /// @brief Return backend type
    ///
    /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
    ///
    /// @return Type of the backend.
    virtual std::string getType() const = 0;

    /// @brief Returns backend name.
    ///
    /// If the backend is a database, this is the name of the database or the
    /// file.  Otherwise it is just the same as the type.
    ///
    /// @return Name of the backend.
    virtual std::string getName() const = 0;

    /// @brief Returns description of the backend.
    ///
    /// This description may be multiline text that describes the backend.
    ///
    /// @return Description of the backend.
    virtual std::string getDescription() const = 0;

    /// @brief Returns backend version.
    ///
    /// @return Version number as a pair of unsigned integers.  "first" is the
    ///         major version number, "second" the minor number.
    ///
    /// @todo: We will need to implement 3 version functions eventually:
    /// A. abstract API version
    /// B. backend version
    /// C. database version (stored in the database scheme)
    ///
    /// and then check that:
    /// 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 VersionPair getVersion() const = 0;

    /// @brief Commit Transactions
    ///
    /// Commits all pending database operations.  On databases that don't
    /// support transactions, this is a no-op.
    virtual void commit() = 0;

    /// @brief Rollback Transactions
    ///
    /// Rolls back all pending database operations.  On databases that don't
    /// support transactions, this is a no-op.
    virtual void rollback() = 0;

    /// @todo: Add host management here
    /// As host reservation is outside of scope for 2012, support for hosts
    /// is currently postponed.
};

}  // namespace dhcp
}  // namespace isc

#endif // LEASE_MGR_H
