Kea  1.9.9-git
ca_process.cc
Go to the documentation of this file.
1 // Copyright (C) 2016-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>
9 #include <agent/ca_process.h>
10 #include <agent/ca_controller.h>
12 #include <agent/ca_log.h>
13 #include <asiolink/io_address.h>
14 #include <asiolink/io_error.h>
15 #include <cc/command_interpreter.h>
16 #include <config/timeouts.h>
17 #include <boost/pointer_cast.hpp>
18 
19 using namespace isc::asiolink;
20 using namespace isc::config;
21 using namespace isc::data;
22 using namespace isc::http;
23 using namespace isc::process;
24 
25 
26 namespace isc {
27 namespace agent {
28 
29 CtrlAgentProcess::CtrlAgentProcess(const char* name,
30  const asiolink::IOServicePtr& io_service)
31  : DProcessBase(name, io_service, DCfgMgrBasePtr(new CtrlAgentCfgMgr())),
32  http_listeners_() {
33 }
34 
36 }
37 
38 void
40 }
41 
42 void
45 
46  try {
47  // Register commands.
48  CtrlAgentControllerPtr controller =
49  boost::dynamic_pointer_cast<CtrlAgentController>(
51  controller->registerCommands();
52 
53  // Let's process incoming data or expiring timers in a loop until
54  // shutdown condition is detected.
55  while (!shouldShutdown()) {
56  // Remove unused listeners within the main loop because new listeners
57  // are created in within a callback method. This avoids removal the
58  // listeners within a callback.
59  garbageCollectListeners(1);
60  runIO();
61  }
62  // Done so removing all listeners.
63  garbageCollectListeners(0);
64  stopIOService();
65  } catch (const std::exception& ex) {
66  LOG_FATAL(agent_logger, CTRL_AGENT_FAILED).arg(ex.what());
67  try {
68  stopIOService();
69  } catch (...) {
70  // Ignore double errors
71  }
73  "Process run method failed: " << ex.what());
74  }
75 
76  try {
77  // Deregister commands.
78  CtrlAgentControllerPtr controller =
79  boost::dynamic_pointer_cast<CtrlAgentController>(
81  controller->deregisterCommands();
82  } catch (const std::exception&) {
83  // What to do? Simply ignore...
84  }
85 
87 }
88 
89 size_t
90 CtrlAgentProcess::runIO() {
91  size_t cnt = getIoService()->get_io_service().poll();
92  if (!cnt) {
93  cnt = getIoService()->get_io_service().run_one();
94  }
95  return (cnt);
96 }
97 
100  setShutdownFlag(true);
101  return (isc::config::createAnswer(0, "Control Agent is shutting down"));
102 }
103 
106  bool check_only) {
107  // System reconfiguration often poses an interesting issue whereby the
108  // configuration parsing is successful, but an attempt to use a new
109  // configuration is not. This will leave us in the inconsistent state
110  // when the configuration is in fact only partially applied and the
111  // system's ability to operate is impaired. The use of C++ lambda is
112  // a way to resolve this problem by injecting the code to the
113  // simpleParseConfig which performs an attempt to open new instance
114  // of the listener (if required). The lambda code will throw an
115  // exception if it fails and cause the simpleParseConfig to rollback
116  // configuration changes and report an error.
117  ConstElementPtr answer = getCfgMgr()->simpleParseConfig(config_set,
118  check_only,
119  [this]() {
120  ConfigPtr base_ctx = getCfgMgr()->getContext();
122  ctx = boost::dynamic_pointer_cast<CtrlAgentCfgContext>(base_ctx);
123 
124  if (!ctx) {
125  isc_throw(Unexpected, "Internal logic error: bad context type");
126  }
127 
129  IOAddress server_address("::");
130  try {
131  server_address = IOAddress(ctx->getHttpHost());
132 
133  } catch (const IOError& e) {
134  isc_throw(BadValue, "Failed to convert " << ctx->getHttpHost()
135  << " to IP address:" << e.what());
136  }
137 
138  uint16_t server_port = ctx->getHttpPort();
139  bool use_https = false;
140 
141  // Only open a new listener if the configuration has changed.
142  if (http_listeners_.empty() ||
143  (http_listeners_.back()->getLocalAddress() != server_address) ||
144  (http_listeners_.back()->getLocalPort() != server_port)) {
145  // Create a TLS context.
146  TlsContextPtr tls_context;
147  // When TLS is enabled configure it.
148  if (!ctx->getCertFile().empty()) {
149  TlsContext::configure(tls_context,
151  ctx->getTrustAnchor(),
152  ctx->getCertFile(),
153  ctx->getKeyFile(),
154  ctx->getCertRequired());
155  use_https = true;
156  }
157 
158  // Create response creator factory first. It will be used to
159  // generate response creators. Each response creator will be
160  // used to generate answer to specific request.
162 
163  // Create http listener. It will open up a TCP socket and be
164  // prepared to accept incoming connection.
165  HttpListenerPtr http_listener
166  (new HttpListener(*getIoService(), server_address,
167  server_port, tls_context, rcf,
170 
171  // Instruct the http listener to actually open socket, install
172  // callback and start listening.
173  http_listener->start();
174 
175  // The new listener is running so add it to the collection of
176  // active listeners. The next step will be to remove all other
177  // active listeners, but we do it inside the main process loop.
178  http_listeners_.push_back(http_listener);
179  }
180 
181  // Ok, seems we're good to go.
182  if (use_https) {
184  .arg(server_address.toText()).arg(server_port);
185  } else {
187  .arg(server_address.toText()).arg(server_port);
188  }
189  });
190 
191  int rcode = 0;
192  config::parseAnswer(rcode, answer);
193  return (answer);
194 }
195 
196 void
197 CtrlAgentProcess::garbageCollectListeners(size_t leaving) {
198  // We expect only one active listener. If there are more (most likely 2),
199  // it means we have just reconfigured the server and need to shut down all
200  // listeners execept the most recently added.
201  if (http_listeners_.size() > leaving) {
202  // Stop no longer used listeners.
203  for (auto l = http_listeners_.begin();
204  l != http_listeners_.end() - leaving;
205  ++l) {
206  (*l)->stop();
207  }
208  // We have stopped listeners but there may be some pending handlers
209  // related to these listeners. Need to invoke these handlers.
210  getIoService()->get_io_service().poll();
211  // Finally, we're ready to remove no longer used listeners.
212  http_listeners_.erase(http_listeners_.begin(),
213  http_listeners_.end() - leaving);
214  }
215 }
216 
217 
220  return (boost::dynamic_pointer_cast<CtrlAgentCfgMgr>(getCfgMgr()));
221 }
222 
225  // Return the most recent listener or null.
226  return (http_listeners_.empty() ? ConstHttpListenerPtr() :
227  http_listeners_.back());
228 }
229 
230 bool
232  // If there are is a listener, we're listening.
233  return (static_cast<bool>(getHttpListener()));
234 }
235 
236 } // namespace isc::agent
237 } // namespace isc
const isc::log::MessageID CTRL_AGENT_STARTED
Definition: ca_messages.h:20
boost::shared_ptr< HttpResponseCreatorFactory > HttpResponseCreatorFactoryPtr
Pointer to the HttpResponseCreatorFactory.
void setShutdownFlag(bool value)
Sets the process shut down flag to the given value.
Definition: d_process.h:166
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
ConstElementPtr createAnswer(const int status_code, const std::string &text, const ConstElementPtr &arg)
boost::shared_ptr< DCfgMgrBase > DCfgMgrBasePtr
Defines a shared pointer to DCfgMgrBase.
Definition: d_cfg_mgr.h:247
boost::shared_ptr< const HttpListener > ConstHttpListenerPtr
Pointer to the const HttpListener.
Definition: listener.h:142
static process::DControllerBasePtr & instance()
Static singleton instance method.
virtual isc::data::ConstElementPtr shutdown(isc::data::ConstElementPtr args)
Initiates the process's shutdown process.
Definition: ca_process.cc:99
boost::shared_ptr< CtrlAgentCfgMgr > CtrlAgentCfgMgrPtr
Defines a shared pointer to CtrlAgentCfgMgr.
Definition: ca_cfg_mgr.h:311
virtual void run()
Implements the process's event loop.
Definition: ca_process.cc:43
isc::log::Logger agent_logger("ctrl-agent")
Control Agent logger.
Definition: ca_log.h:18
bool isListening() const
Checks if the process is listening to the HTTP requests.
Definition: ca_process.cc:231
HTTP response creator factory for Control Agent.
bool shouldShutdown() const
Checks if the process has been instructed to shut down.
Definition: d_process.h:159
const isc::log::MessageID CTRL_AGENT_FAILED
Definition: ca_messages.h:16
HTTP listener.
Definition: listener.h:52
const isc::log::MessageID CTRL_AGENT_HTTPS_SERVICE_STARTED
Definition: ca_messages.h:17
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
void registerCommands()
Register commands.
DCfgMgrBasePtr & getCfgMgr()
Fetches the process's configuration manager.
Definition: d_process.h:195
void deregisterCommands()
Deregister commands.
Idle connection timeout.
Definition: listener.h:67
A generic exception that is thrown when an unexpected error condition occurs.
CtrlAgentCfgMgrPtr getCtrlAgentCfgMgr()
Returns a pointer to the configuration manager.
Definition: ca_process.cc:219
Process Controller for Control Agent Process.
Definition: ca_controller.h:21
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:23
constexpr long TIMEOUT_AGENT_RECEIVE_COMMAND
Timeout for the Control Agent to receive command over the RESTful interface.
Definition: timeouts.h:21
const isc::log::MessageID CTRL_AGENT_RUN_EXIT
Definition: ca_messages.h:19
virtual isc::data::ConstElementPtr configure(isc::data::ConstElementPtr config_set, bool check_only=false)
Processes the given configuration.
Definition: ca_process.cc:105
void stopIOService()
Convenience method for stopping IOservice processing.
Definition: d_process.h:188
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
http::ConstHttpListenerPtr getHttpListener() const
Returns a const pointer to the HTTP listener used by the process.
Definition: ca_process.cc:224
ConstElementPtr parseAnswer(int &rcode, const ConstElementPtr &msg)
Defines the logger used by the top-level component of kea-dhcp-ddns.
const isc::log::MessageID CTRL_AGENT_HTTP_SERVICE_STARTED
Definition: ca_messages.h:18
This file contains several functions and constants that are used for handling commands and responses ...
asiolink::IOServicePtr & getIoService()
Fetches the controller's IOService.
Definition: d_process.h:180
Ctrl Agent Configuration Manager.
Definition: ca_cfg_mgr.h:253
Exception thrown if the process encountered an operational error.
Definition: d_process.h:24
virtual ~CtrlAgentProcess()
Destructor.
Definition: ca_process.cc:35
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
boost::shared_ptr< CtrlAgentController > CtrlAgentControllerPtr
Definition: ca_controller.h:80
Application Process Interface.
Definition: d_process.h:81
#define LOG_FATAL(LOGGER, MESSAGE)
Macro to conveniently test fatal output and log it.
Definition: macros.h:38
boost::shared_ptr< HttpListener > HttpListenerPtr
Pointer to the HttpListener.
Definition: listener.h:139
virtual void init()
Initialize the Control Agent process.
Definition: ca_process.cc:39
boost::shared_ptr< CtrlAgentCfgContext > CtrlAgentCfgContextPtr
Pointer to a configuration context.
Definition: ca_cfg_mgr.h:21
constexpr long TIMEOUT_AGENT_IDLE_CONNECTION_TIMEOUT
Timeout for the idle connection to be closed.
Definition: timeouts.h:24
Control Agent Configuration Context.
Definition: ca_cfg_mgr.h:32
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
HTTP request timeout value.
Definition: listener.h:56
boost::shared_ptr< ConfigBase > ConfigPtr
Non-const pointer to the ConfigBase.
Definition: config_base.h:176