Kea 2.7.0
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#include <stats/stats_mgr.h>
12
13using namespace isc::stats;
14
15namespace isc {
16namespace d2 {
17
18// Makes constant visible to Google test macros.
20
21D2QueueMgr::D2QueueMgr(asiolink::IOServicePtr& io_service, const size_t max_queue_size)
22 : io_service_(io_service), max_queue_size_(max_queue_size),
23 mgr_state_(NOT_INITTED), target_stop_state_(NOT_INITTED) {
24 if (!io_service_) {
25 isc_throw(D2QueueMgrError, "IOServicePtr cannot be null");
26 }
27
28 // Use setter to do validation.
29 setMaxQueueSize(max_queue_size);
30}
31
34
35void
38 try {
39 // Note that error conditions must be handled here without throwing
40 // exceptions. Remember this is the application level "link" in the
41 // callback chain. Throwing an exception here will "break" the
42 // io_service "run" we are operating under. With that in mind,
43 // if we hit a problem, we will stop the listener transition to
44 // the appropriate stopped state. Upper layer(s) must monitor our
45 // state as well as our queue size.
46 switch (result) {
48 // Receive was successful, attempt to queue the request.
49 if (getQueueSize() < getMaxQueueSize()) {
50 // There's room on the queue, add to the end
51 enqueue(ncr);
52
53 // Log that we got the request
57 .arg(ncr->getRequestId());
58 return;
59 }
60
61 // Queue is full, stop the listener.
62 // Note that we can move straight to a STOPPED state as there
63 // is no receive in progress.
65 .arg(max_queue_size_);
66 StatsMgr::instance().addValue("queue-mgr-queue-full", static_cast<int64_t>(1));
68 break;
69
71 if (mgr_state_ == STOPPING) {
72 // This is confirmation that the listener has stopped and its
73 // callback will not be called again, unless its restarted.
74 updateStopState();
75 } else {
76 // We should not get a receive complete status of stopped
77 // unless we canceled the read as part of stopping. Therefore
78 // this is unexpected so we will treat it as a receive error.
79 // This is most likely an unforeseen programmatic issue.
81 .arg(mgr_state_);
83 }
84
85 break;
86
87 default:
88 // Receive failed, stop the listener.
89 // Note that we can move straight to a STOPPED state as there
90 // is no receive in progress.
93 break;
94 }
95 } catch (const std::exception& ex) {
96 // On the outside chance a throw occurs, let's log it and swallow it.
98 .arg(ex.what());
99 }
100}
101
102void
104 const uint32_t port,
105 const dhcp_ddns::NameChangeFormat format,
106 const bool reuse_address) {
107
108 if (listener_) {
110 "D2QueueMgr listener is already initialized");
111 }
112
113 // Instantiate a UDP listener and set state to INITTED.
114 // Note UDP listener constructor does not throw.
115 listener_.reset(new dhcp_ddns::NameChangeUDPListener(ip_address, port, format,
116 shared_from_this(), reuse_address));
117 mgr_state_ = INITTED;
118}
119
120void
122 // We can't listen if we haven't initialized the listener yet.
123 if (!listener_) {
124 isc_throw(D2QueueMgrError, "D2QueueMgr "
125 "listener is not initialized, cannot start listening");
126 }
127
128 // If we are already listening, we do not want to "reopen" the listener
129 // and really we shouldn't be trying.
130 if (mgr_state_ == RUNNING) {
131 isc_throw(D2QueueMgrError, "D2QueueMgr "
132 "cannot call startListening from the RUNNING state");
133 }
134
135 // Instruct the listener to start listening and set state accordingly.
136 try {
137 listener_->startListening(io_service_);
138 mgr_state_ = RUNNING;
139 } catch (const isc::Exception& ex) {
140 isc_throw(D2QueueMgrError, "D2QueueMgr listener start failed: "
141 << ex.what());
142 }
143
146}
147
148void
149D2QueueMgr::stopListening(const State target_stop_state) {
150 if (listener_) {
151 // Enforce only valid "stop" states.
152 // This is purely a programmatic error and should never happen.
153 if (target_stop_state != STOPPED &&
154 target_stop_state != STOPPED_QUEUE_FULL &&
155 target_stop_state != STOPPED_RECV_ERROR) {
157 "D2QueueMgr invalid value for stop state: "
158 << target_stop_state);
159 }
160
161 // Remember the state we want to achieve.
162 target_stop_state_ = target_stop_state;
163
164 // Instruct the listener to stop. If the listener reports that it
165 // has IO pending, then we transition to STOPPING to wait for the
166 // cancellation event. Otherwise, we can move directly to the targeted
167 // state.
168 listener_->stopListening();
169 if (listener_->isIoPending()) {
170 mgr_state_ = STOPPING;
171 } else {
172 updateStopState();
173 }
174 }
175}
176
177void
178D2QueueMgr::updateStopState() {
179 mgr_state_ = target_stop_state_;
182}
183
184void
186 // Force our managing layer(s) to stop us properly first.
187 if (mgr_state_ == RUNNING) {
189 "D2QueueMgr cannot delete listener while state is RUNNING");
190 }
191
192 listener_.reset();
193 mgr_state_ = NOT_INITTED;
194}
195
198 if (getQueueSize() == 0) {
200 "D2QueueMgr peek attempted on an empty queue");
201 }
202
203 return (ncr_queue_.front());
204}
205
207D2QueueMgr::peekAt(const size_t index) const {
208 if (index >= getQueueSize()) {
210 "D2QueueMgr peek beyond end of queue attempted"
211 << " index: " << index << " queue size: " << getQueueSize());
212 }
213
214 return (ncr_queue_.at(index));
215}
216
217void
218D2QueueMgr::dequeueAt(const size_t index) {
219 if (index >= getQueueSize()) {
221 "D2QueueMgr dequeue beyond end of queue attempted"
222 << " index: " << index << " queue size: " << getQueueSize());
223 }
224
225 RequestQueue::iterator pos = ncr_queue_.begin() + index;
226 ncr_queue_.erase(pos);
227}
228
229void
231 if (getQueueSize() == 0) {
233 "D2QueueMgr dequeue attempted on an empty queue");
234 }
235
236 ncr_queue_.pop_front();
237}
238
239void
241 ncr_queue_.push_back(ncr);
242}
243
244void
246 ncr_queue_.clear();
247}
248
249void
250D2QueueMgr::setMaxQueueSize(const size_t new_queue_max) {
251 if (new_queue_max < 1) {
253 "D2QueueMgr maximum queue size must be greater than zero");
254 }
255
256 if (new_queue_max < getQueueSize()) {
257 isc_throw(D2QueueMgrError, "D2QueueMgr maximum queue size value cannot"
258 " be less than the current queue size :" << getQueueSize());
259 }
260
261 max_queue_size_ = new_queue_max;
262}
263
264} // namespace isc::d2
265} // 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.
Thrown if a queue index is beyond the end of the queue.
Thrown if the request queue empty and a read is attempted.
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.
virtual ~D2QueueMgr()
Destructor.
size_t getMaxQueueSize() const
Returns the maximum number of entries allowed in the queue.
static const size_t MAX_QUEUE_DEFAULT
Maximum number of entries allowed in the request queue.
void dequeue()
Removes the entry at the front of the queue.
State
Defines the list of possible states for D2QueueMgr.
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.
virtual void operator()(const dhcp_ddns::NameChangeListener::Result result, dhcp_ddns::NameChangeRequestPtr &ncr)
Function operator implementing the NCR receive callback.
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.
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
static StatsMgr & instance()
Statistics Manager accessor method.
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.
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...
const int DBGLVL_TRACE_DETAIL_DATA
Trace data associated with detailed operations.
Defines the logger used by the top-level component of kea-lfc.
This file provides UDP socket based implementation for sending and receiving NameChangeRequests.