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
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 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
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
// Copyright (C) 2013-2024 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 D2_UPDATE_MGR_H
#define D2_UPDATE_MGR_H

/// @file d2_update_mgr.h This file defines the class D2UpdateMgr.

#include <asiolink/io_service.h>
#include <d2/d2_queue_mgr.h>
#include <d2srv/nc_trans.h>
#include <d2srv/d2_cfg_mgr.h>
#include <d2srv/d2_log.h>
#include <exceptions/exceptions.h>

#include <boost/noncopyable.hpp><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <boost/shared_ptr.hpp><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <map><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

namespace isc {
namespace d2 {

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

/// @brief Defines a list of transactions.
typedef std::map<TransactionKey, NameChangeTransactionPtr> TransactionList;

/// @brief D2UpdateMgr creates and manages update transactions.
///
/// D2UpdateMgr is the DHCP_DDNS task master, instantiating and then supervising
/// transactions that execute the DNS updates needed to fulfill the requests
/// (NameChangeRequests) received from DHCP_DDNS clients (e.g. DHCP servers).
///
/// D2UpdateMgr uses the services of D2QueueMgr to monitor the queue of
/// NameChangeRequests and select and dequeue requests for processing.
/// When a request is dequeued for processing it is removed from the queue and
/// wrapped in NameChangeTransaction and added to the D2UpdateMgr's list of
/// transactions.
///
/// As part of the process of forming transactions, D2UpdateMgr matches each
/// request with the appropriate list of DNS servers.  This matching is based
/// upon request attributes, primarily the FQDN and update direction (forward
/// or reverse).  D2UpdateMgr uses the services of D2CfgMgr to match requests
/// to DNS server lists.
///
/// Once created, each transaction is responsible for carrying out the steps
/// required to fulfill its specific request.  These steps typically consist of
/// one or more DNS packet exchanges with the appropriate DNS server.  As
/// transactions complete, D2UpdateMgr removes them from the transaction list,
/// replacing them with new transactions.
///
/// D2UpdateMgr carries out each of the above steps, with a method called
/// sweep().  This method is intended to be called as IO events complete.
/// The upper layer(s) are responsible for calling sweep in a timely and cyclic
/// manner.
///
class D2UpdateMgr : public boost::noncopyable {
public:
    /// @brief Maximum number of concurrent transactions
    /// NOTE that 32 is an arbitrary choice picked for the initial
    /// implementation.
    static const size_t MAX_TRANSACTIONS_DEFAULT = 32;

    /// @brief Constructor
    ///
    /// @param queue_mgr reference to the queue manager receiving requests
    /// @param cfg_mgr reference to the configuration manager
    /// @param io_service IO service used by the upper layer(s) to manage
    /// IO events
    /// @param max_transactions the maximum number of concurrent transactions
    ///
    /// @throw D2UpdateMgrError if either the queue manager or configuration
    /// managers are NULL, or max transactions is less than one.
    D2UpdateMgr(D2QueueMgrPtr& queue_mgr, D2CfgMgrPtr& cfg_mgr,
                asiolink::IOServicePtr& io_service,
                const size_t max_transactions = MAX_TRANSACTIONS_DEFAULT);

    /// @brief Destructor
    virtual ~D2UpdateMgr();

    /// @brief Check current transactions; start transactions for new requests.
    ///
    /// This method is the primary public interface used by the upper layer. It
    /// should be called as IO events complete.  During each invocation it does
    /// the following:
    ///
    /// - Removes all completed transactions from the transaction list.
    ///
    /// - If the request queue is not empty and the number of transactions
    /// in the transaction list has not reached maximum allowed, then select
    /// a request from the queue.
    ///
    /// - If a request was selected, start a new transaction for it and
    /// add the transaction to the list of transactions.
    void sweep();

protected:
    /// @brief Performs post-completion cleanup on completed transactions.
    ///
    /// Iterates through the list of transactions and removes any that have
    /// reached completion.  This method may expand in complexity or even
    /// disappear altogether as the implementation matures.
    void checkFinishedTransactions();

    /// @brief Starts a transaction for the next eligible request in the queue.
    ///
    /// This method will scan the request queue for the next request to
    /// dequeue.  The current implementation starts at the front of the queue
    /// and looks for the first request for whose DHCID there is no current
    /// transaction in progress.
    ///
    /// If a request is selected, it is removed from the queue and transaction
    /// is constructed for it.
    ///
    /// It is possible that no such request exists, though this is likely to be
    /// rather rare unless a system is frequently seeing requests for the same
    /// clients in quick succession.
    void pickNextJob();

    /// @brief Create a new transaction for the given request.
    ///
    /// This method will attempt to match the request to suitable DNS servers.
    /// If matching servers are found, it will instantiate a transaction for
    /// the requests, add the transaction to the transaction list, and start
    /// the transaction.
    ///
    /// If updates in a given direction are disabled requests for updates in
    /// that direction will be ignored.  For example: If a request is received
    /// which asks for updates in both directions but only forward updates are
    /// enabled; only the forward update will be attempted.  Effectively, the
    /// request will be treated as if it only asked for forward updates.
    ///
    /// If updates in a given direction are enabled, and a request asks for
    /// updates in that direction, failing to match the request to a list
    /// of servers is an error which will be logged and the request will be
    /// discarded.
    ///
    /// Finally, If conflict resolution is enabled, it will instantiate either
    /// a NameAddTransaction or a NameRemoveTransaction. If disabled it will
    /// instantiate either a SimpleAddTransaction or a SimpleRemoveTransaction.
    ///
    /// @param ncr the NameChangeRequest for which to create a transaction.
    ///
    /// @return True if a transaction was added, false otherwise.
    ///
    /// @throw D2UpdateMgrError if a transaction for this DHCID already
    /// exists. Note this would be programmatic error.
    bool makeTransaction(isc::dhcp_ddns::NameChangeRequestPtr& ncr);

public:
    /// @brief Gets the D2UpdateMgr's IOService.
    ///
    /// @return returns a reference to the IOService
    const asiolink::IOServicePtr& getIOService() {
        return (io_service_);
    }

    /// @brief Returns the maximum number of concurrent transactions.
    size_t getMaxTransactions() const {
        return (max_transactions_);
    }

    /// @brief Sets the maximum number of entries allowed in the queue.
    ///
    /// @param max_transactions is the new maximum number of transactions
    ///
    /// @throw Throws D2QueueMgrError if the new value is less than one or if
    /// the new value is less than the number of entries currently in the
    /// queue.
    void setMaxTransactions(const size_t max_transactions);

    /// @brief Search the transaction list for the given key.
    ///
    /// @param key the transaction key value for which to search.
    ///
    /// @return Iterator pointing to the entry found.  If no entry is
    /// it will point to the list end position.
    TransactionList::iterator findTransaction(const TransactionKey& key);

    /// @brief Returns the transaction list end position.
    TransactionList::iterator transactionListEnd();

    /// @brief Returns the transaction list beg position.
    TransactionList::iterator transactionListBegin();

    /// @brief Convenience method that checks transaction list for the given key
    ///
    /// @param key the transaction key value for which to search.
    ///
    /// @return Returns true if the key is found within the list, false
    /// otherwise.
    bool hasTransaction(const TransactionKey& key);

    /// @brief Removes the entry pointed to by key from the transaction list.
    ///
    /// Removes the entry referred to by key if it exists.  It has no effect
    /// if the entry is not found.
    ///
    /// @param key of the transaction to remove
    void removeTransaction(const TransactionKey& key);

    /// @brief Immediately discards all entries in the transaction list.
    ///
    /// @todo For now this just wipes them out. We might need something
    /// more elegant, that allows a cancel first.
    void clearTransactionList();

    /// @brief Convenience method that returns the number of requests queued.
    size_t getQueueCount() const;

    /// @brief Returns the current number of transactions.
    size_t getTransactionCount() const;

private:
    /// @brief Pointer to the queue manager.
    D2QueueMgrPtr queue_mgr_;

    /// @brief Pointer to the configuration manager.
    D2CfgMgrPtr cfg_mgr_;

    /// @brief Primary IOService instance.
    /// This is the IOService that the upper layer(s) use for IO events, such
    /// as shutdown and configuration commands.  It is the IOService that is
    /// passed into transactions to manager their IO events.
    /// (For future reference, multi-threaded transactions would each use their
    /// own IOService instance.)
    asiolink::IOServicePtr io_service_;

    /// @brief Maximum number of concurrent transactions.
    size_t max_transactions_;

    /// @brief List of transactions.
    TransactionList transaction_list_;
};

/// @brief Defines a pointer to a D2UpdateMgr instance.
typedef boost::shared_ptr<D2UpdateMgr> D2UpdateMgrPtr;


} // namespace isc::d2
} // namespace isc
#endif