Kea 2.2.0
d2_process.cc
Go to the documentation of this file.
1// Copyright (C) 2013-2021 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>
10#include <config/command_mgr.h>
11#include <d2/d2_controller.h>
12#include <d2/d2_process.h>
13#include <d2srv/d2_cfg_mgr.h>
14#include <d2srv/d2_log.h>
15#include <d2srv/d2_stats.h>
16#include <d2srv/d2_tsig_key.h>
17#include <hooks/hooks.h>
18#include <hooks/hooks_manager.h>
19
20using namespace isc::hooks;
21using namespace isc::process;
22
23namespace {
24
26struct D2ProcessHooks {
27 int hooks_index_d2_srv_configured_;
28
30 D2ProcessHooks() {
31 hooks_index_d2_srv_configured_ = HooksManager::registerHook("d2_srv_configured");
32 }
33
34};
35
36// Declare a Hooks object. As this is outside any function or method, it
37// will be instantiated (and the constructor run) when the module is loaded.
38// As a result, the hook indexes will be defined before any method in this
39// module is called.
40D2ProcessHooks Hooks;
41
42}
43
44namespace isc {
45namespace d2 {
46
47// Setting to 80% for now. This is an arbitrary choice and should probably
48// be configurable.
49const unsigned int D2Process::QUEUE_RESTART_PERCENT = 80;
50
51D2Process::D2Process(const char* name, const asiolink::IOServicePtr& io_service)
52 : DProcessBase(name, io_service, DCfgMgrBasePtr(new D2CfgMgr())),
53 reconf_queue_flag_(false), shutdown_type_(SD_NORMAL) {
54
55 // Instantiate queue manager. Note that queue manager does not start
56 // listening at this point. That can only occur after configuration has
57 // been received. This means that until we receive the configuration,
58 // D2 will neither receive nor process NameChangeRequests.
59 // Pass in IOService for NCR IO event processing.
60 queue_mgr_.reset(new D2QueueMgr(getIoService()));
61
62 // Instantiate update manager.
63 // Pass in both queue manager and configuration manager.
64 // Pass in IOService for DNS update transaction IO event processing.
66 update_mgr_.reset(new D2UpdateMgr(queue_mgr_, tmp, getIoService()));
67
68 // Initialize stats manager.
70};
71
72void
74 // CommandMgr uses IO service to run asynchronous socket operations.
76};
77
78void
81 D2ControllerPtr controller =
82 boost::dynamic_pointer_cast<D2Controller>(D2Controller::instance());
83 try {
84 // Now logging was initialized so commands can be registered.
85 controller->registerCommands();
86
87 // Loop forever until we are allowed to shutdown.
88 while (!canShutdown()) {
89 // Check on the state of the request queue. Take any
90 // actions necessary regarding it.
92
93 // Give update manager a time slice to queue new jobs and
94 // process finished ones.
95 update_mgr_->sweep();
96
97 // Wait on IO event(s) - block until one or more of the following
98 // has occurred:
99 // a. NCR message has been received
100 // b. Transaction IO has completed
101 // c. Interval timer expired
102 // d. Control channel event
103 // e. Something stopped IO service (runIO returns 0)
104 if (runIO() == 0) {
105 // Pretty sure this amounts to an unexpected stop and we
106 // should bail out now. Normal shutdowns do not utilize
107 // stopping the IOService.
109 "Primary IO service stopped unexpectedly");
110 }
111 }
112 } catch (const std::exception& ex) {
113 LOG_FATAL(d2_logger, DHCP_DDNS_FAILED).arg(ex.what());
114 controller->deregisterCommands();
116 "Process run method failed: " << ex.what());
117 }
118
122
123 controller->deregisterCommands();
124
126
127};
128
129size_t
131 // We want to block until at least one handler is called. We'll use
132 // boost::asio::io_service directly for two reasons. First off
133 // asiolink::IOService::run_one is a void and boost::asio::io_service::stopped
134 // is not present in older versions of boost. We need to know if any
135 // handlers ran or if the io_service was stopped. That latter represents
136 // some form of error and the application cannot proceed with a stopped
137 // service. Secondly, asiolink::IOService does not provide the poll
138 // method. This is a handy method which runs all ready handlers without
139 // blocking.
141 boost::asio::io_service& asio_io_service = io->get_io_service();
142
143 // Poll runs all that are ready. If none are ready it returns immediately
144 // with a count of zero.
145 size_t cnt = asio_io_service.poll();
146 if (!cnt) {
147 // Poll ran no handlers either none are ready or the service has been
148 // stopped. Either way, call run_one to wait for a IO event. If the
149 // service is stopped it will return immediately with a cnt of zero.
150 cnt = asio_io_service.run_one();
151 }
152
153 return (cnt);
154}
155
156bool
158 bool all_clear = false;
159
160 // If we have been told to shutdown, find out if we are ready to do so.
161 if (shouldShutdown()) {
162 switch (shutdown_type_) {
163 case SD_NORMAL:
164 // For a normal shutdown we need to stop the queue manager but
165 // wait until we have finished all the transactions in progress.
166 all_clear = (((queue_mgr_->getMgrState() != D2QueueMgr::RUNNING) &&
167 (queue_mgr_->getMgrState() != D2QueueMgr::STOPPING))
168 && (update_mgr_->getTransactionCount() == 0));
169 break;
170
171 case SD_DRAIN_FIRST:
172 // For a drain first shutdown we need to stop the queue manager but
173 // process all of the requests in the receive queue first.
174 all_clear = (((queue_mgr_->getMgrState() != D2QueueMgr::RUNNING) &&
175 (queue_mgr_->getMgrState() != D2QueueMgr::STOPPING))
176 && (queue_mgr_->getQueueSize() == 0)
177 && (update_mgr_->getTransactionCount() == 0));
178 break;
179
180 case SD_NOW:
181 // Get out right now, no niceties.
182 all_clear = true;
183 break;
184
185 default:
186 // shutdown_type_ is an enum and should only be one of the above.
187 // if its getting through to this, something is whacked.
188 break;
189 }
190
191 if (all_clear) {
194 .arg(getShutdownTypeStr(shutdown_type_));
195 }
196 }
197
198 return (all_clear);
199}
200
205 .arg(args ? args->str() : "(no arguments)");
206
207 // Default shutdown type is normal.
208 std::string type_str(getShutdownTypeStr(SD_NORMAL));
209 shutdown_type_ = SD_NORMAL;
210
211 if (args) {
212 if ((args->getType() == isc::data::Element::map) &&
213 args->contains("type")) {
214 type_str = args->get("type")->stringValue();
215
216 if (type_str == getShutdownTypeStr(SD_NORMAL)) {
217 shutdown_type_ = SD_NORMAL;
218 } else if (type_str == getShutdownTypeStr(SD_DRAIN_FIRST)) {
219 shutdown_type_ = SD_DRAIN_FIRST;
220 } else if (type_str == getShutdownTypeStr(SD_NOW)) {
221 shutdown_type_ = SD_NOW;
222 } else {
223 setShutdownFlag(false);
224 return (isc::config::createAnswer(1, "Invalid Shutdown type: "
225 + type_str));
226 }
227 }
228 }
229
230 // Set the base class's shutdown flag.
231 setShutdownFlag(true);
232 return (isc::config::createAnswer(0, "Shutdown initiated, type is: "
233 + type_str));
234}
235
239 .arg(check_only ? "check" : "update")
240 .arg(getD2CfgMgr()->redactConfig(config_set)->str());
241
243 answer = getCfgMgr()->simpleParseConfig(config_set, check_only,
244 std::bind(&D2Process::reconfigureCommandChannel, this));
245 if (check_only) {
246 return (answer);
247 }
248
249 int rcode = 0;
251 comment = isc::config::parseAnswer(rcode, answer);
252
253 if (rcode) {
254 // Non-zero means we got an invalid configuration, take no further
255 // action. In integrated mode, this will send a failed response back
256 // to the configuration backend.
257 reconf_queue_flag_ = false;
258 return (answer);
259 }
260
261 // Set the reconf_queue_flag to indicate that we need to reconfigure
262 // the queue manager. Reconfiguring the queue manager may be asynchronous
263 // and require one or more events to occur, therefore we set a flag
264 // indicating it needs to be done but we cannot do it here. It must
265 // be done over time, while events are being processed. Remember that
266 // the method we are in now is invoked as part of the configuration event
267 // callback. This means you can't wait for events here, you are already
268 // in one.
272 reconf_queue_flag_ = true;
273
274 // This hook point notifies hooks libraries that the configuration of the
275 // D2 server has completed. It provides the hook library with the pointer
276 // to the common IO service object, new server configuration in the JSON
277 // format and with the pointer to the configuration storage where the
278 // parsed configuration is stored.
279 std::string error("");
280 if (HooksManager::calloutsPresent(Hooks.hooks_index_d2_srv_configured_)) {
281 CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
282
283 callout_handle->setArgument("io_context", getIoService());
284 callout_handle->setArgument("json_config", config_set);
285 callout_handle->setArgument("server_config",
286 getD2CfgMgr()->getD2CfgContext());
287 callout_handle->setArgument("error", error);
288
289 HooksManager::callCallouts(Hooks.hooks_index_d2_srv_configured_,
290 *callout_handle);
291
292 // The config can be rejected by a hook.
293 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
294 callout_handle->getArgument("error", error);
296 .arg(error);
297 reconf_queue_flag_ = false;
298 answer = isc::config::createAnswer(1, error);
299 return (answer);
300 }
301 }
302
303 // If we are here, configuration was valid, at least it parsed correctly
304 // and therefore contained no invalid values.
305 // Return the success answer from above.
306 return (answer);
307}
308
309void
311 switch (queue_mgr_->getMgrState()){
313 if (reconf_queue_flag_ || shouldShutdown()) {
318 try {
321 .arg(reconf_queue_flag_ ? "reconfiguration" : "shutdown");
322 queue_mgr_->stopListening();
323 } catch (const isc::Exception& ex) {
324 // It is very unlikely that we would experience an error
325 // here, but theoretically possible.
327 .arg(ex.what());
328 }
329 }
330 break;
331
336 size_t threshold = (((queue_mgr_->getMaxQueueSize()
337 * QUEUE_RESTART_PERCENT)) / 100);
338 if (queue_mgr_->getQueueSize() <= threshold) {
340 .arg(threshold).arg(queue_mgr_->getMaxQueueSize());
341 try {
342 queue_mgr_->startListening();
343 } catch (const isc::Exception& ex) {
345 .arg(ex.what());
346 }
347 }
348
349 break;
350 }
351
360 if (!shouldShutdown()) {
363 }
364 break;
365
371 break;
372
373 default:
374 // If the reconfigure flag is set, then we are in a state now where
375 // we can do the reconfigure. In other words, we aren't RUNNING or
376 // STOPPING.
377 if (reconf_queue_flag_) {
381 }
382 break;
383 }
384}
385
386void
388 // Set reconfigure flag to false. We are only here because we have
389 // a valid configuration to work with so if we fail below, it will be
390 // an operational issue, such as a busy IP address. That will leave
391 // queue manager in INITTED state, which is fine.
392 // What we don't want is to continually attempt to reconfigure so set
393 // the flag false now.
397 reconf_queue_flag_ = false;
398 try {
399 // Wipe out the current listener.
400 queue_mgr_->removeListener();
401
402 // Get the configuration parameters that affect Queue Manager.
403 const D2ParamsPtr& d2_params = getD2CfgMgr()->getD2Params();
404
407 std::string ip_address = d2_params->getIpAddress().toText();
408 if (ip_address != "127.0.0.1" && ip_address != "::1") {
410 }
411
412 // Instantiate the listener.
413 if (d2_params->getNcrProtocol() == dhcp_ddns::NCR_UDP) {
414 queue_mgr_->initUDPListener(d2_params->getIpAddress(),
415 d2_params->getPort(),
416 d2_params->getNcrFormat(), true);
417 } else {
419 // We should never get this far but if we do deal with it.
420 isc_throw(DProcessBaseError, "Unsupported NCR listener protocol:"
421 << dhcp_ddns::ncrProtocolToString(d2_params->
422 getNcrProtocol()));
423 }
424
425 // Now start it. This assumes that starting is a synchronous,
426 // blocking call that executes quickly.
429 queue_mgr_->startListening();
430 } catch (const isc::Exception& ex) {
431 // Queue manager failed to initialize and therefore not listening.
432 // This is most likely due to an unavailable IP address or port,
433 // which is a configuration issue.
435 }
436}
437
439}
440
443 // The base class gives a base class pointer to our configuration manager.
444 // Since we are D2, and we need D2 specific extensions, we need a pointer
445 // to D2CfgMgr for some things.
446 return (boost::dynamic_pointer_cast<D2CfgMgr>(getCfgMgr()));
447}
448
450 const char* str = "invalid";
451 switch (type) {
452 case SD_NORMAL:
453 str = "normal";
454 break;
455 case SD_DRAIN_FIRST:
456 str = "drain_first";
457 break;
458 case SD_NOW:
459 str = "now";
460 break;
461 default:
462 break;
463 }
464
465 return (str);
466}
467
468void
470 // Get new socket configuration.
471 isc::data::ConstElementPtr sock_cfg = getD2CfgMgr()->getControlSocketInfo();
472
473 // Determine if the socket configuration has changed. It has if
474 // both old and new configuration is specified but respective
475 // data elements aren't equal.
476 bool sock_changed = (sock_cfg && current_control_socket_ &&
477 !sock_cfg->equals(*current_control_socket_));
478
479 // If the previous or new socket configuration doesn't exist or
480 // the new configuration differs from the old configuration we
481 // close the existing socket and open a new socket as appropriate.
482 // Note that closing an existing socket means the client will not
483 // receive the configuration result.
484 if (!sock_cfg || !current_control_socket_ || sock_changed) {
485 // Close the existing socket.
486 if (current_control_socket_) {
488 current_control_socket_.reset();
489 }
490
491 // Open the new socket.
492 if (sock_cfg) {
494 }
495 }
496
497 // Commit the new socket configuration.
498 current_control_socket_ = sock_cfg;
499}
500
501} // namespace isc::d2
502} // namespace isc
CtrlAgentHooks Hooks
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.
void closeCommandSocket()
Shuts down any open control sockets.
Definition: command_mgr.cc:627
static CommandMgr & instance()
CommandMgr is a singleton class.
Definition: command_mgr.cc:650
void setIOService(const asiolink::IOServicePtr &io_service)
Sets IO service to be used by the command manager.
Definition: command_mgr.cc:656
void openCommandSocket(const isc::data::ConstElementPtr &socket_info)
Opens control socket with parameters specified in socket_info.
Definition: command_mgr.cc:623
DHCP-DDNS Configuration Manager.
Definition: d2_cfg_mgr.h:161
static process::DControllerBasePtr & instance()
Static singleton instance method.
D2Process(const char *name, const asiolink::IOServicePtr &io_service)
Constructor.
Definition: d2_process.cc:51
static const unsigned int QUEUE_RESTART_PERCENT
Defines the point at which to resume receiving requests.
Definition: d2_process.h:48
virtual bool canShutdown() const
Indicates whether or not the process can perform a shutdown.
Definition: d2_process.cc:157
virtual void checkQueueStatus()
Monitors current queue manager state, takes action accordingly.
Definition: d2_process.cc:310
virtual ~D2Process()
Destructor.
Definition: d2_process.cc:438
virtual void run()
Implements the process's event loop.
Definition: d2_process.cc:79
virtual void init()
Called after instantiation to perform initialization unique to D2.
Definition: d2_process.cc:73
D2CfgMgrPtr getD2CfgMgr()
Returns a pointer to the configuration manager.
Definition: d2_process.cc:442
virtual isc::data::ConstElementPtr configure(isc::data::ConstElementPtr config_set, bool check_only=false)
Processes the given configuration.
Definition: d2_process.cc:237
void reconfigureCommandChannel()
(Re-)Configure the command channel.
Definition: d2_process.cc:469
virtual void reconfigureQueueMgr()
Initializes then starts the queue manager.
Definition: d2_process.cc:387
ShutdownType
Defines the shutdown types supported by D2Process.
Definition: d2_process.h:36
virtual isc::data::ConstElementPtr shutdown(isc::data::ConstElementPtr args)
Initiates the D2Process shutdown process.
Definition: d2_process.cc:202
static const char * getShutdownTypeStr(const ShutdownType &type)
Returns a text label for the given shutdown type.
Definition: d2_process.cc:449
virtual size_t runIO()
Allows IO processing to run until at least callback is invoked.
Definition: d2_process.cc:130
D2QueueMgr creates and manages a queue of DNS update requests.
Definition: d2_queue_mgr.h:132
static void init()
Initialize D2 statistics.
Definition: d2_stats.cc:46
D2UpdateMgr creates and manages update transactions.
Definition: d2_update_mgr.h:65
Exception thrown if the process encountered an operational error.
Definition: d_process.h:24
Application Process Interface.
Definition: d_process.h:81
void setShutdownFlag(bool value)
Sets the process shut down flag to the given value.
Definition: d_process.h:166
asiolink::IOServicePtr & getIoService()
Fetches the controller's IOService.
Definition: d_process.h:180
bool shouldShutdown() const
Checks if the process has been instructed to shut down.
Definition: d_process.h:159
DCfgMgrBasePtr & getCfgMgr()
Fetches the process's configuration manager.
Definition: d_process.h:195
This file contains several functions and constants that are used for handling commands and responses ...
#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_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition: macros.h:26
#define LOG_FATAL(LOGGER, MESSAGE)
Macro to conveniently test fatal output and log it.
Definition: macros.h:38
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
ConstElementPtr createAnswer(const int status_code, const std::string &text, const ConstElementPtr &arg)
ConstElementPtr parseAnswer(int &rcode, const ConstElementPtr &msg)
boost::shared_ptr< D2CfgMgr > D2CfgMgrPtr
Defines a shared pointer to D2CfgMgr.
Definition: d2_cfg_mgr.h:334
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RECOVERING
Definition: d2_messages.h:54
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STOP_ERROR
Definition: d2_messages.h:62
const isc::log::MessageID DHCP_DDNS_FAILED
Definition: d2_messages.h:22
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_START_ERROR
Definition: d2_messages.h:59
const isc::log::MessageID DHCP_DDNS_SHUTDOWN_COMMAND
Definition: d2_messages.h:80
const isc::log::MessageID DHCP_DDNS_CONFIGURE
Definition: d2_messages.h:17
const isc::log::MessageID DHCP_DDNS_CLEARED_FOR_SHUTDOWN
Definition: d2_messages.h:15
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RECONFIGURING
Definition: d2_messages.h:53
const isc::log::MessageID DHCP_DDNS_RUN_EXIT
Definition: d2_messages.h:79
const isc::log::MessageID DHCP_DDNS_CONFIGURED_CALLOUT_DROP
Definition: d2_messages.h:18
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RESUME_ERROR
Definition: d2_messages.h:56
const isc::log::MessageID DHCP_DDNS_STARTED
Definition: d2_messages.h:81
boost::shared_ptr< D2Controller > D2ControllerPtr
Pointer to a process controller.
Definition: d2_controller.h:15
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_RESUMING
Definition: d2_messages.h:57
boost::shared_ptr< D2Params > D2ParamsPtr
Defines a pointer for D2Params instances.
Definition: d2_config.h:257
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STOPPING
Definition: d2_messages.h:61
const isc::log::MessageID DHCP_DDNS_NOT_ON_LOOPBACK
Definition: d2_messages.h:45
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:27
@ error
Definition: db_log.h:115
std::string ncrProtocolToString(NameChangeProtocol protocol)
Function which converts NameChangeProtocol enums to text labels.
Definition: ncr_io.cc:36
boost::shared_ptr< CalloutHandle > CalloutHandlePtr
A shared pointer to a CalloutHandle object.
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
boost::shared_ptr< DCfgMgrBase > DCfgMgrBasePtr
Defines a shared pointer to DCfgMgrBase.
Definition: d_cfg_mgr.h:247
ConstElementPtr redactConfig(ConstElementPtr const &element, list< string > const &json_path)
Redact a configuration.
Defines the logger used by the top-level component of kea-lfc.