mysql_lease_mgr.h 15 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// 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 __MYSQL_LEASE_MGR_H
#define __MYSQL_LEASE_MGR_H

18
#include <time.h>
19
#include <mysql.h>
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
#include <dhcp/lease_mgr.h>

namespace isc {
namespace dhcp {

/// @brief Abstract Lease Manager
///
/// This is a concrete API for the backend for the MySQL database.
class MySqlLeaseMgr : public LeaseMgr {
public:
    /// @brief Constructor
    ///
    /// Uses the following keywords in the parameters passed to it to
    /// connect to the database:
    /// - name - Name of the database to which to connect
    /// - host - Host name to which to connect
    /// - user - Username under which to connect.
    /// - password - Password for "user" on the database.
    ///
    /// If the database is successfully opened, the version number in the
    /// schema_version table will be checked against hard-coded value in
    /// the implementation file.
    ///
    /// Finally, all the SQL commands are pre-compiled.
    ///
    /// @param parameters A data structure relating keywords and values
    ///        concerned with the database.
    MySqlLeaseMgr(const ParameterMap& parameters);

    /// @brief Destructor (closes database)
    virtual ~MySqlLeaseMgr();

    /// @brief Adds an IPv4 lease.
    ///
    /// @param lease lease to be added
55 56 57 58 59 60
    ///
    /// @result true if the lease was added, false if not (because a lease
    ///         with the same address was already there).
    ///
    /// @exception DbOperationError Database function failed
    virtual bool addLease(const Lease4Ptr& lease);
61 62 63 64

    /// @brief Adds an IPv6 lease.
    ///
    /// @param lease lease to be added
65 66 67 68 69 70
    ///
    /// @result true if the lease was added, false if not (because a lease
    ///         with the same address was already there).
    ///
    /// @exception DbOperationError Database function failed
    virtual bool addLease(const Lease6Ptr& lease);
71 72 73 74 75 76 77 78 79 80 81

    /// @brief Returns existing IPv4 lease for specified IPv4 address and subnet_id
    ///
    /// This method is used to get a lease for specific subnet_id. There can be
    /// at most one lease for any given subnet, so this method returns a single
    /// pointer.
    ///
    /// @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)
82
    virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr,
83
                                SubnetID subnet_id) const;
84 85 86 87 88 89 90 91 92 93 94 95 96 97

    /// @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
    /// @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)
98
    virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr) const;
99 100 101 102 103 104 105 106 107 108 109

    /// @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
110
    virtual Lease4Collection getLease4(const HWAddr& hwaddr) const;
111 112 113 114 115 116 117 118 119 120 121 122

    /// @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,
123
                                SubnetID subnet_id) const;
124 125 126 127 128 129 130 131 132 133 134

    /// @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
135
    virtual Lease4Collection getLease4(const ClientId& clientid) const;
136 137 138 139 140 141 142 143 144 145 146

    /// @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,
147
                                SubnetID subnet_id) const;
148 149 150 151 152 153 154 155 156 157

    /// @brief Returns existing IPv6 lease for a given IPv6 address.
    ///
    /// For a given address, we assume that there will be only one lease.
    /// The assumtion here is that there will not be site or link-local
    /// addresses used, so there is no way of having address duplication.
    ///
    /// @param addr address of the searched lease
    ///
    /// @return smart pointer to the lease (or NULL if a lease is not found)
158
    virtual Lease6Ptr getLease6(const isc::asiolink::IOAddress& addr) const;
159 160 161 162 163 164 165 166 167 168 169 170 171

    /// @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 duid client DUID
    /// @param iaid IA identifier
    ///
    /// @return smart pointer to the lease (or NULL if a lease is not found)
    virtual Lease6Collection getLease6(const DUID& duid,
172
                                       uint32_t iaid) const;
173 174 175 176 177 178 179 180 181

    /// @brief Returns existing IPv6 lease for a given DUID+IA combination
    ///
    /// @param duid client DUID
    /// @param iaid IA identifier
    /// @param subnet_id subnet id of the subnet the lease belongs to
    ///
    /// @return smart pointer to the lease (or NULL if a lease is not found)
    virtual Lease6Ptr getLease6(const DUID& duid, uint32_t iaid,
182
                                SubnetID subnet_id) const;
183 184 185 186 187 188

    /// @brief Updates IPv4 lease.
    ///
    /// @param lease4 The lease to be updated.
    ///
    /// If no such lease is present, an exception will be thrown.
189
    virtual void updateLease4(const Lease4Ptr& lease4);
190 191 192 193 194 195

    /// @brief Updates IPv4 lease.
    ///
    /// @param lease4 The lease to be updated.
    ///
    /// If no such lease is present, an exception will be thrown.
196
    virtual void updateLease6(const Lease6Ptr& lease6);
197 198 199 200 201 202

    /// @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
203
    virtual bool deleteLease4(const isc::asiolink::IOAddress& addr);
204 205 206 207 208 209

    /// @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
210
    virtual bool deleteLease6(const isc::asiolink::IOAddress& addr);
211 212 213 214

    /// @brief Returns backend name.
    ///
    /// Each backend have specific name, e.g. "mysql" or "sqlite".
215
    virtual std::string getName() const;
216 217 218 219

    /// @brief Returns description of the backend.
    ///
    /// This description may be multiline text that describes the backend.
220
    virtual std::string getDescription() const;
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237

    /// @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 std::pair<uint32_t, uint32_t> getVersion() const;

238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
    /// @brief Commit Transactions
    ///
    /// Commits all pending database operations.  On databases that don't
    /// support transactions, this is a no-op.
    ///
    /// @exception DbOperationError if the commit failed.
    virtual void commit();


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

    ///@{
    /// The following methods are used to convert between times and time
    /// intervals stored in the server in the Lease object, and the times
    /// stored in the database.  The reason for the difference is because
    /// in the DHCP server, the cltt (Client Time Since Last Transmission)
    /// is the natural data: in the lease file - which may be read by the
    /// user - it is the expiry time of the lease.

    /// @brief Convert Lease Time to Database Times
    ///
    /// Within the DHCP servers, times are stored as cltt (client last transmit
    /// time) and valid_lft (valid lifetime).  In the database, the information
    /// is stored as lease_time (lease time) and expire (time of expiry of the
    /// lease).  They are related by the equations:
    ///
    /// lease_time = valid_lft
    /// expire = cltt + valid_lft
    ///
    /// This method converts from the times in the lease object into times
    /// able to be added to the database.
    ///
    /// @param cltt Client last transmit time
    /// @param valid_lft Valid lifetime
    /// @param expire Reference to MYSQL_TIME object where the expiry time of
    ///        the lease will be put.
    /// @param lease_time Reference to the time_t object where the lease time
    ///         will be put.
    static
    void convertFromLeaseTime(time_t cltt, uint32_t valid_lft,
                               MYSQL_TIME& expire, uint32_t& lease_time);

    /// @brief Convert Database Time to Lease Times
    ///
    /// Within the database, time is stored as lease_time (lease time) and
    /// expire (time of expiry of the lease).  In the DHCP server, the
    /// information is stored as cltt (client last transmit time) and
    /// valid_lft (valid lifetime).  These arr related by the equations:
    ///
    /// valid_lft = lease_time
    /// cltt = expire - lease_time
    ///
    /// This method converts from the times in the database into times
    /// able to be inserted into the lease object.
    ///
    /// @param expire Reference to MYSQL_TIME object from where the expiry
    ///        time of the lease is taken.
    /// @param lease_time lifetime of the lease.
    /// @param cltt Reference to location where client last transmit time
    ///        is put.
    /// @param valid_lft Reference to location where valid lifetime is put.
    static
    void convertToLeaseTime(const MYSQL_TIME& expire, uint32_t lease_time,
                            time_t& cltt, uint32_t& valid_lft);

    ///@}


312
private:
313 314 315 316
    /// @brief Enum of Statements
    ///
    /// This is provided to set indexes into a list of prepared statements.
    enum StatementIndex {
317 318 319 320
        GET_LEASE6,
        GET_VERSION,        // Obtain version number
        INSERT_LEASE6,      // Add entry to lease6 table
        NUM_STATEMENTS      // Number of statements
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
    };

    /// @brief Prepare Single Statement
    ///
    /// Creates a prepared statement from the text given and adds it to the
    /// statements_ vector at the given index.
    ///
    /// @param index Index into the statements_ vector into which the text
    ///        should be placed.  The vector must be big enough for the index
    ///        to be valid, else an exception will be thrown.
    /// @param text Text of the SQL statement to be prepared.
    ///
    /// @exception DbOperationError MySQL operation failed, exception will give
    ///            text indicating the reason.
    /// @exception InvalidParameter 'index' is not valid for the vector.
    void prepareStatement(StatementIndex index, const char* text);

    /// @brief Prepare statements
    ///
    /// Creates the prepared statements for all of the SQL statements used
    /// by the MySQL backend.
    void prepareStatements();

344 345 346 347 348 349 350 351
    /// @brief Open Database
    ///
    /// Opens the database using the information supplied in the parameters
    /// passed to the constructor.
    ///
    /// @exception DbOpenError Error opening the database
    void openDatabase();

352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
    /// @brief Check Error and Throw Exception
    ///
    /// Virtually all MySQL functions return a status which, if non-zero,
    /// indicates an error.  This inline function conceals a lot of error
    /// checking/exception-throwing code.
    ///
    /// @param status Status code: non-zero implies an error
    /// @param index Index of statement that caused the error
    /// @param what High-level description of the error
    ///
    /// @exception DbOperationError Error doing a database operation
    inline void checkError(my_bool status, StatementIndex index,
                           const char* what) const {
        if (status != 0) {
            isc_throw(DbOperationError, what << " for <" <<
                      raw_statements_[index] << ">, reason: " <<
                      mysql_error(mysql_));
        }
    }

372
    // Members
373 374 375
    MYSQL*              mysql_;                 ///< MySQL context object
    std::vector<std::string> raw_statements_;   ///< Raw text of statements
    std::vector<MYSQL_STMT*> statements_;       ///< Prepared statements
376 377 378 379 380 381
};

}; // end of isc::dhcp namespace
}; // end of isc namespace

#endif // __MYSQL_LEASE_MGR_H