Kea 2.5.8
d2_queue_mgr.cc
Go to the documentation of this file.
1// Copyright (C) 2013-2024 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8#include <d2/d2_queue_mgr.h>
9#include <d2srv/d2_log.h>
10#include <dhcp_ddns/ncr_udp.h>
11
12namespace isc {
13namespace d2 {
14
15// Makes constant visible to Google test macros.
17
18D2QueueMgr::D2QueueMgr(asiolink::IOServicePtr& io_service, const size_t max_queue_size)
19 : io_service_(io_service), max_queue_size_(max_queue_size),
20 mgr_state_(NOT_INITTED), target_stop_state_(NOT_INITTED) {
21 if (!io_service_) {
22 isc_throw(D2QueueMgrError, "IOServicePtr cannot be null");
23 }
24
25 // Use setter to do validation.
26 setMaxQueueSize(max_queue_size);
27}
28
30}
31
32void
35 try {
36 // Note that error conditions must be handled here without throwing
37 // exceptions. Remember this is the application level "link" in the
38 // callback chain. Throwing an exception here will "break" the
39 // io_service "run" we are operating under. With that in mind,
40 // if we hit a problem, we will stop the listener transition to
41 // the appropriate stopped state. Upper layer(s) must monitor our
42 // state as well as our queue size.
43 switch (result) {
45 // Receive was successful, attempt to queue the request.
46 if (getQueueSize() < getMaxQueueSize()) {
47 // There's room on the queue, add to the end
48 enqueue(ncr);
49
50 // Log that we got the request
54 .arg(ncr->getRequestId());
55 return;
56 }
57
58 // Queue is full, stop the listener.
59 // Note that we can move straight to a STOPPED state as there
60 // is no receive in progress.
62 .arg(max_queue_size_);
64 break;
65
67 if (mgr_state_ == STOPPING) {
68 // This is confirmation that the listener has stopped and its
69 // callback will not be called again, unless its restarted.
70 updateStopState();
71 } else {
72 // We should not get a receive complete status of stopped
73 // unless we canceled the read as part of stopping. Therefore
74 // this is unexpected so we will treat it as a receive error.
75 // This is most likely an unforeseen programmatic issue.
77 .arg(mgr_state_);
79 }
80
81 break;
82
83 default:
84 // Receive failed, stop the listener.
85 // Note that we can move straight to a STOPPED state as there
86 // is no receive in progress.
89 break;
90 }
91 } catch (const std::exception& ex) {
92 // On the outside chance a throw occurs, let's log it and swallow it.
94 .arg(ex.what());
95 }
96}
97
98void
100 const uint32_t port,
101 const dhcp_ddns::NameChangeFormat format,
102 const bool reuse_address) {
103
104 if (listener_) {
106 "D2QueueMgr listener is already initialized");
107 }
108
109 // Instantiate a UDP listener and set state to INITTED.
110 // Note UDP listener constructor does not throw.
111 listener_.reset(new dhcp_ddns::NameChangeUDPListener(ip_address, port, format,
112 shared_from_this(), reuse_address));
113 mgr_state_ = INITTED;
114}
115
116void
118 // We can't listen if we haven't initialized the listener yet.
119 if (!listener_) {
120 isc_throw(D2QueueMgrError, "D2QueueMgr "
121 "listener is not initialized, cannot start listening");
122 }
123
124 // If we are already listening, we do not want to "reopen" the listener
125 // and really we shouldn't be trying.
126 if (mgr_state_ == RUNNING) {
127 isc_throw(D2QueueMgrError, "D2QueueMgr "
128 "cannot call startListening from the RUNNING state");
129 }
130
131 // Instruct the listener to start listening and set state accordingly.
132 try {
133 listener_->startListening(io_service_);
134 mgr_state_ = RUNNING;
135 } catch (const isc::Exception& ex) {
136 isc_throw(D2QueueMgrError, "D2QueueMgr listener start failed: "
137 << ex.what());
138 }
139
142}
143
144void
145D2QueueMgr::stopListening(const State target_stop_state) {
146 if (listener_) {
147 // Enforce only valid "stop" states.
148 // This is purely a programmatic error and should never happen.
149 if (target_stop_state != STOPPED &&
150 target_stop_state != STOPPED_QUEUE_FULL &&
151 target_stop_state != STOPPED_RECV_ERROR) {
153 "D2QueueMgr invalid value for stop state: "
154 << target_stop_state);
155 }
156
157 // Remember the state we want to achieve.
158 target_stop_state_ = target_stop_state;
159
160 // Instruct the listener to stop. If the listener reports that it
161 // has IO pending, then we transition to STOPPING to wait for the
162 // cancellation event. Otherwise, we can move directly to the targeted
163 // state.
164 listener_->stopListening();
165 if (listener_->isIoPending()) {
166 mgr_state_ = STOPPING;
167 } else {
168 updateStopState();
169 }
170 }
171}
172
173void
174D2QueueMgr::updateStopState() {
175 mgr_state_ = target_stop_state_;
178}
179
180void
182 // Force our managing layer(s) to stop us properly first.
183 if (mgr_state_ == RUNNING) {
185 "D2QueueMgr cannot delete listener while state is RUNNING");
186 }
187
188 listener_.reset();
189 mgr_state_ = NOT_INITTED;
190}
191
194 if (getQueueSize() == 0) {
196 "D2QueueMgr peek attempted on an empty queue");
197 }
198
199 return (ncr_queue_.front());
200}
201
203D2QueueMgr::peekAt(const size_t index) const {
204 if (index >= getQueueSize()) {
206 "D2QueueMgr peek beyond end of queue attempted"
207 << " index: " << index << " queue size: " << getQueueSize());
208 }
209
210 return (ncr_queue_.at(index));
211}
212
213void
214D2QueueMgr::dequeueAt(const size_t index) {
215 if (index >= getQueueSize()) {
217 "D2QueueMgr dequeue beyond end of queue attempted"
218 << " index: " << index << " queue size: " << getQueueSize());
219 }
220
221 RequestQueue::iterator pos = ncr_queue_.begin() + index;
222 ncr_queue_.erase(pos);
223}
224
225void
227 if (getQueueSize() == 0) {
229 "D2QueueMgr dequeue attempted on an empty queue");
230 }
231
232 ncr_queue_.pop_front();
233}
234
235void
237 ncr_queue_.push_back(ncr);
238}
239
240void
242 ncr_queue_.clear();
243}
244
245void
246D2QueueMgr::setMaxQueueSize(const size_t new_queue_max) {
247 if (new_queue_max < 1) {
249 "D2QueueMgr maximum queue size must be greater than zero");
250 }
251
252 if (new_queue_max < getQueueSize()) {
253 isc_throw(D2QueueMgrError, "D2QueueMgr maximum queue size value cannot"
254 " be less than the current queue size :" << getQueueSize());
255 }
256
257 max_queue_size_ = new_queue_max;
258}
259
260} // namespace isc::d2
261} // namespace isc
This is a base class for exceptions thrown from the DNS library module.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
Thrown if the queue manager encounters a general error.
Definition: d2_queue_mgr.h:28
Thrown if a queue index is beyond the end of the queue.
Definition: d2_queue_mgr.h:59
Thrown if the request queue empty and a read is attempted.
Definition: d2_queue_mgr.h:52
const dhcp_ddns::NameChangeRequestPtr & peek() const
Returns the entry at the front of the queue.
D2QueueMgr(asiolink::IOServicePtr &io_service, const size_t max_queue_size=MAX_QUEUE_DEFAULT)
Constructor.
Definition: d2_queue_mgr.cc:18
virtual ~D2QueueMgr()
Destructor.
Definition: d2_queue_mgr.cc:29
size_t getMaxQueueSize() const
Returns the maximum number of entries allowed in the queue.
Definition: d2_queue_mgr.h:250
static const size_t MAX_QUEUE_DEFAULT
Maximum number of entries allowed in the request queue.
Definition: d2_queue_mgr.h:135
void dequeue()
Removes the entry at the front of the queue.
State
Defines the list of possible states for D2QueueMgr.
Definition: d2_queue_mgr.h:138
const dhcp_ddns::NameChangeRequestPtr & peekAt(const size_t index) const
Returns the entry at a given position in the queue.
void removeListener()
Deletes the current listener.
void enqueue(dhcp_ddns::NameChangeRequestPtr &ncr)
Adds a request to the end of the queue.
void startListening()
Starts actively listening for requests.
void setMaxQueueSize(const size_t max_queue_size)
Sets the maximum number of entries allowed in the queue.
size_t getQueueSize() const
Returns the number of entries in the queue.
Definition: d2_queue_mgr.h:245
virtual void operator()(const dhcp_ddns::NameChangeListener::Result result, dhcp_ddns::NameChangeRequestPtr &ncr)
Function operator implementing the NCR receive callback.
Definition: d2_queue_mgr.cc:33
void clearQueue()
Removes all entries from the queue.
void initUDPListener(const isc::asiolink::IOAddress &ip_address, const uint32_t port, const dhcp_ddns::NameChangeFormat format, const bool reuse_address=false)
Initializes the listener as a UDP listener.
Definition: d2_queue_mgr.cc:99
void dequeueAt(const size_t index)
Removes the entry at a given position in the queue.
void stopListening(const State target_stop_state=STOPPED)
Stops listening for requests.
Result
Defines the outcome of an asynchronous NCR receive.
Definition: ncr_io.h:171
Provides the ability to receive NameChangeRequests via UDP socket.
Definition: ncr_udp.h:317
This file defines the class D2QueueMgr.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RECV_ERROR
Definition: d2_messages.h:60
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_QUEUE_RECEIVE
Definition: d2_messages.h:57
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_UNEXPECTED_HANDLER_ERROR
Definition: d2_messages.h:68
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_UNEXPECTED_STOP
Definition: d2_messages.h:69
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STARTED
Definition: d2_messages.h:63
isc::log::Logger dhcp_to_d2_logger("dhcp-to-d2")
Definition: d2_log.h:19
isc::log::Logger d2_logger("dhcpddns")
Defines the logger used within D2.
Definition: d2_log.h:18
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_QUEUE_FULL
Definition: d2_messages.h:56
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STOPPED
Definition: d2_messages.h:65
NameChangeFormat
Defines the list of data wire formats supported.
Definition: ncr_msg.h:59
boost::shared_ptr< NameChangeRequest > NameChangeRequestPtr
Defines a pointer to a NameChangeRequest.
Definition: ncr_msg.h:241
const int DBGLVL_TRACE_BASIC
Trace basic operations.
Definition: log_dbglevels.h:69
const int DBGLVL_START_SHUT
This is given a value of 0 as that is the level selected if debugging is enabled without giving a lev...
Definition: log_dbglevels.h:50
const int DBGLVL_TRACE_DETAIL_DATA
Trace data associated with detailed operations.
Definition: log_dbglevels.h:78
Defines the logger used by the top-level component of kea-lfc.
This file provides UDP socket based implementation for sending and receiving NameChangeRequests.