d2_queue_mgr.h 13.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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
// Copyright (C) 2013 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 D2_QUEUE_MGR_H
#define D2_QUEUE_MGR_H

/// @file d2_queue_mgr.h This file defines the class D2QueueMgr.

#include <asiolink/io_address.h>
#include <asiolink/io_service.h>
#include <exceptions/exceptions.h>
#include <dhcp_ddns/ncr_msg.h>
#include <dhcp_ddns/ncr_io.h>

#include <deque>

namespace isc {
namespace d2 {

/// @brief Defines a queue of requests.
/// @todo This may be replaced with an actual class in the future.
typedef std::deque<dhcp_ddns::NameChangeRequestPtr> RequestQueue;

/// @brief Thrown if the queue manager encounters an general error.
class D2QueueMgrError : public isc::Exception {
public:
    D2QueueMgrError(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) { };
};

/// @brief Thrown if the queue manager's receive handler is passed
/// a failure result.
class D2QueueMgrReceiveError : public isc::Exception {
public:
    D2QueueMgrReceiveError(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) { };
};


/// @brief Thrown if the request queue is full when an enqueue is attempted.
52
class D2QueueMgrQueueFull : public isc::Exception {
53
public:
54
    D2QueueMgrQueueFull(const char* file, size_t line, const char* what) :
55 56 57 58
        isc::Exception(file, line, what) { };
};

/// @brief Thrown if the request queue empty and a read is attempted.
59
class D2QueueMgrQueueEmpty : public isc::Exception {
60
public:
61
    D2QueueMgrQueueEmpty(const char* file, size_t line, const char* what) :
62 63 64
        isc::Exception(file, line, what) { };
};

65 66 67 68 69 70 71 72
/// @brief Thrown if a queue index is beyond the end of the queue
class D2QueueMgrInvalidIndex : public isc::Exception {
public:
    D2QueueMgrInvalidIndex(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) { };
};


73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
/// @brief D2QueueMgr creates and manages a queue of DNS update requests.
///
/// D2QueueMgr is class specifically designed as an integral part of DHCP-DDNS.
/// Its primary responsibility is to listen for NameChangeRequests from
/// DHCP-DDNS clients (e.g. DHCP servers) and queue them for processing. In
/// addition it may provide a number services to locate entries in the queue
/// such as by FQDN or DHCID.  These services may eventually be used
/// for processing optimization.  The initial implementation will support
/// simple FIFO access.
///
/// D2QueueMgr uses a NameChangeListener to asynchronously receive requests.
/// It derives from NameChangeListener::RequestReceiveHandler and supplies an
/// implementation of the operator()(Result, NameChangeRequestPtr).  It is
/// through this operator() that D2QueueMgr is passed inbound NCRs. D2QueueMgr
/// will add each newly received request onto the back of the request queue
///
/// D2QueueMgr defines a simple state model constructed around the status of
/// its NameChangeListener, consisting of the following states:
///
///     * NOT_INITTED - D2QueueMgr has been constructed, but its listener has
///     not been initialized.
///
///     * INITTED - The listener has been initialized, but it is not open for
///     listening.   To move from NOT_INITTED to INITTED, one of the D2QueueMgr
///     listener initialization methods must be invoked.  Currently there is
///     only one type of listener, NameChangeUDPListener, hence there is only
///     one listener initialization method, initUDPListener.  As more listener
///     types are created, listener initialization methods will need to be
///     added.
///
///     * RUNNING - The listener is open and listening for requests.
///     Once initialized, in order to begin listening for requests, the
///     startListener() method must be invoked.  Upon successful completion of
///     of this call, D2QueueMgr will begin receiving requests as they arrive
///     without any further steps.   This method may be called from the INITTED
///     or one of the STOPPED states.
///
///     * STOPPED - The listener has been listening but has been stopped
///     without error. To return to listening, startListener() must be invoked.
///
///     * STOPPED_QUEUE_FULL - Request queue is full, the listener has been
///     stopped.  D2QueueMgr will enter this state when the request queue
///     reaches the maximum queue size.  Once this limit is reached, the
///     listener will be closed and no further requests will be received.
///     To return to listening, startListener() must be invoked.  Note that so
///     long as the queue is full, any attempt to queue a request will fail.
///
///     * STOPPED_RECV_ERROR - The listener has experienced a receive error
///     and has been stopped.  D2QueueMgr will enter this state when it is
///     passed a failed status into the request completion handler.  To return
///     to listening, startListener() must be invoked.
///
/// D2QueueMgr does not attempt to recover from stopped conditions, this is left
/// to upper layers.
///
/// It is important to note that the queue contents are preserved between
/// state transitions.  In other words entries in the queue remain there
/// until they are removed explicitly via the deque() or implicitly by
/// via the flushQue() method.
///
class D2QueueMgr : public dhcp_ddns::NameChangeListener::RequestReceiveHandler {
public:
    /// @brief Maximum number of entries allowed in the request queue.
    /// NOTE that 1024 is an arbitrary choice picked for the initial
    /// implementation.
    static const size_t MAX_QUEUE_DEFAULT=  1024;

    /// @brief Defines the list of possible states for D2QueueMgr.
    enum State {
      NOT_INITTED,
      INITTED,
      RUNNING,
      STOPPED_QUEUE_FULL,
      STOPPED_RECV_ERROR,
      STOPPED,
    };

    /// @brief Constructor
    ///
    /// Creates a D2QueueMgr instance.  Note that the listener is not created
    /// in the constructor. The initial state will be NOT_INITTED.
    ///
    /// @param io_service IOService instance to be passed into the listener for
    /// IO management.
    /// @param max_queue_size the maximum number of entries allowed in the
    /// queue.
    /// This value must be greater than zero. It defaults to MAX_QUEUE_DEFAULT.
    ///
    /// @throw D2QueueMgr error if max_queue_size is zero.
    D2QueueMgr(isc::asiolink::IOService& io_service,
               const size_t max_queue_size = MAX_QUEUE_DEFAULT);

    /// @brief Destructor
    ~D2QueueMgr();

    /// @brief Initializes the listener as a UDP listener.
    ///
    /// Instantiates the listener_ member as NameChangeUDPListener passing
    /// the given parameters.  Upon successful completion, the D2QueueMgr state
    /// will be INITTED.
    ///
    /// @param ip_address is the network address on which to listen
    /// @param port is the IP port on which to listen
    /// @param format is the wire format of the inbound requests.
    /// @param reuse_address enables IP address sharing when true
    /// It defaults to false.
    void initUDPListener(const isc::asiolink::IOAddress& ip_address,
                         const uint32_t& port,
                         dhcp_ddns::NameChangeFormat format,
                         bool reuse_address = false);

    /// @brief Starts actively listening for requests.
    ///
    /// Invokes the listener's startListening method passing in our
    /// IOService instance.
    ///
    /// @throw D2QueueMgrError if the listener has not been initialized,
    /// state is already RUNNING, or the listener fails to actually start.
    void startListening();

    /// @brief Function operator implementing the NCR receive callback.
    ///
    /// This method is invoked by the listener as part of its receive
    /// completion callback and is how the inbound NameChangeRequests are
    /// passed up to the D2QueueMgr for queueing.
    /// If the given result indicates a successful receive completion and
    /// there is room left in the queue, the given request is queued.
    ///
    /// If the queue is at maximum capacity, stopListening() is invoked and
    /// the state is set to STOPPED_QUEUE_FULL.
    ///
    /// If the result indicates a failed receive, stopListening() is invoked
    /// and the state is set to STOPPED_RECV_ERROR.
    ///
    /// This method specifically avoids throwing on an error as any such throw
    /// would surface at the io_service::run (or run variant) method invocation
    /// site. The upper layers are expected to monitor D2QueueMgr's state and
    /// act accordingly.
    ///
    /// @param result contains that receive outcome status.
    /// @param ncr is a pointer to the newly received NameChangeRequest if
    /// result is NameChangeListener::SUCCESS.  It is indeterminate other
    /// wise.
    virtual void operator ()(const dhcp_ddns::NameChangeListener::Result result,
                             dhcp_ddns::NameChangeRequestPtr& ncr);

    /// @brief Stops listening for requests.
    ///
    /// Invokes the listener's stopListening method which should cause it to
    /// cancel any pending IO and close its IO source.  It the sets the state
    /// to the given value.
    ///
    /// @param stop_state is one of the three stopped state values.
    ///
227
    /// @throw D2QueueMgrError if stop_state is a valid stop state.
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
    void stopListening(const State stop_state = STOPPED);

    /// @brief Deletes the current listener
    ///
    /// This method will delete the current listener and returns the manager
    /// to the NOT_INITTED state.  This is provided to support reconfiguring
    /// a new listener without losing queued requests.
    ///
    /// @throw D2QueMgrError if called when the manager state is RUNNING.
    void removeListener();

    /// @brief Returns the number of entries in the queue.
    size_t getQueueSize() const {
        return (ncr_queue_.size());
    };

    /// @brief Returns the maximum number of entries allowed in the queue.
    size_t getMaxQueueSize() const {
        return (max_queue_size_);
    }

    /// @brief Sets the maximum number of entries allowed in the queue.
    ///
    /// @param max_queue_size is the new maximum size of the queue.
    ///
253
    /// @throw D2QueueMgrError if the new value is less than one or if
254 255 256 257 258
    /// the new value is less than the number of entries currently in the
    /// queue.
    void setMaxQueueSize(const size_t max_queue_size);

    /// @brief Returns the current state.
259
    State getMgrState() const {
260 261 262 263 264 265 266 267 268
        return (mgr_state_);
    }

    /// @brief Returns the entry at the front of the queue.
    ///
    /// The entry returned is next in line to be processed, assuming a FIFO
    /// approach to task selection.  Note, the entry is not removed from the
    /// queue.
    ///
269 270 271
    /// @return Pointer reference to the queue entry.
    ///
    /// @throw D2QueueMgrQueEmpty if there are no entries in the queue.
272 273
    const dhcp_ddns::NameChangeRequestPtr& peek() const;

274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
    /// @brief Returns the entry at a given position in the queue.
    ///
    /// Note that the entry is not removed from the queue.
    /// @param index the index of the entry in the queue to fetch.
    /// Valid values are 0 (front of the queue) to (queue size - 1).
    ///
    /// @return Pointer reference to the queue entry.
    ///
    /// @throw D2QueueMgrInvalidIndex if the given index is beyond the
    /// end of the queue.
    const dhcp_ddns::NameChangeRequestPtr& peekAt(size_t index) const;

    /// @brief Removes the entry at a given position in the queue.
    ///
    /// @param index the index of the entry in the queue to remove.
    /// Valid values are 0 (front of the queue) to (queue size - 1).
    ///
    /// @throw D2QueueMgrInvalidIndex if the given index is beyond the
    /// end of the queue.
    void dequeueAt(size_t index);

295 296
    /// @brief Removes the entry at the front of the queue.
    ///
297
    /// @throw D2QueueMgrQueEmpty if there are no entries in the queue.
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
    void dequeue();

    /// @brief Adds a request to the end of the queue.
    ///
    /// @param ncr pointer to the NameChangeRequest to add to the queue.
    void enqueue(dhcp_ddns::NameChangeRequestPtr& ncr);

    /// @brief Removes all entries from the queue.
    void clearQueue();

  private:
    /// @brief IOService that our listener should use for IO management.
    isc::asiolink::IOService& io_service_;

    /// @brief Dictates the maximum number of entries allowed in the queue.
    size_t max_queue_size_;

    /// @brief Queue of received NameChangeRequests.
    RequestQueue ncr_queue_;

    /// @brief Listener instance from which requests are received.
    boost::shared_ptr<dhcp_ddns::NameChangeListener> listener_;

    /// @brief Current state of the manager.
    State mgr_state_;


};

/// @brief Defines a pointer for manager instances.
typedef boost::shared_ptr<D2QueueMgr> D2QueueMgrPtr;

} // namespace isc::d2
} // namespace isc

#endif