|
@@ -5,6 +5,7 @@
|
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
|
|
#include <config.h>
|
|
|
+#include <asiolink/asio_wrapper.h>
|
|
|
#include <agent/ca_process.h>
|
|
|
#include <agent/ca_controller.h>
|
|
|
#include <agent/ca_response_creator_factory.h>
|
|
@@ -12,10 +13,10 @@
|
|
|
#include <asiolink/io_address.h>
|
|
|
#include <asiolink/io_error.h>
|
|
|
#include <cc/command_interpreter.h>
|
|
|
-#include <http/listener.h>
|
|
|
#include <boost/pointer_cast.hpp>
|
|
|
|
|
|
using namespace isc::asiolink;
|
|
|
+using namespace isc::data;
|
|
|
using namespace isc::http;
|
|
|
using namespace isc::process;
|
|
|
|
|
@@ -32,7 +33,8 @@ namespace agent {
|
|
|
|
|
|
CtrlAgentProcess::CtrlAgentProcess(const char* name,
|
|
|
const asiolink::IOServicePtr& io_service)
|
|
|
- : DProcessBase(name, io_service, DCfgMgrBasePtr(new CtrlAgentCfgMgr())) {
|
|
|
+ : DProcessBase(name, io_service, DCfgMgrBasePtr(new CtrlAgentCfgMgr())),
|
|
|
+ http_listeners_() {
|
|
|
}
|
|
|
|
|
|
CtrlAgentProcess::~CtrlAgentProcess() {
|
|
@@ -47,53 +49,19 @@ CtrlAgentProcess::run() {
|
|
|
LOG_INFO(agent_logger, CTRL_AGENT_STARTED).arg(VERSION);
|
|
|
|
|
|
try {
|
|
|
-
|
|
|
// Register commands.
|
|
|
CtrlAgentControllerPtr controller =
|
|
|
boost::dynamic_pointer_cast<CtrlAgentController>(
|
|
|
CtrlAgentController::instance());
|
|
|
controller->registerCommands();
|
|
|
|
|
|
- // Create response creator factory first. It will be used to generate
|
|
|
- // response creators. Each response creator will be used to generate
|
|
|
- // answer to specific request.
|
|
|
- HttpResponseCreatorFactoryPtr rcf(new CtrlAgentResponseCreatorFactory());
|
|
|
-
|
|
|
- DCfgContextBasePtr base_ctx = getCfgMgr()->getContext();
|
|
|
CtrlAgentCfgContextPtr ctx =
|
|
|
boost::dynamic_pointer_cast<CtrlAgentCfgContext>(base_ctx);
|
|
|
- if (!ctx) {
|
|
|
- isc_throw(Unexpected, "Interal logic error: bad context type");
|
|
|
- }
|
|
|
-
|
|
|
- /// @todo: If the parameter is a hostname, we need to resolve it.
|
|
|
- IOAddress server_address("::");
|
|
|
- try {
|
|
|
- server_address = IOAddress(ctx->getHttpHost());
|
|
|
- } catch (const IOError& e) {
|
|
|
- isc_throw(BadValue, "Failed to convert " << ctx->getHttpHost()
|
|
|
- << " to IP address:" << e.what());
|
|
|
- }
|
|
|
-
|
|
|
- uint16_t server_port = ctx->getHttpPort();
|
|
|
-
|
|
|
- // Create http listener. It will open up a TCP socket and be prepared
|
|
|
- // to accept incoming connection.
|
|
|
- HttpListener http_listener(*getIoService(), server_address,
|
|
|
- server_port, rcf, REQUEST_TIMEOUT);
|
|
|
-
|
|
|
- // Instruct the http listener to actually open socket, install callback
|
|
|
- // and start listening.
|
|
|
- http_listener.start();
|
|
|
-
|
|
|
- // Ok, seems we're good to go.
|
|
|
- LOG_INFO(agent_logger, CTRL_AGENT_HTTP_SERVICE_STARTED)
|
|
|
- .arg(server_address.toText()).arg(server_port);
|
|
|
-
|
|
|
// Let's process incoming data or expiring timers in a loop until
|
|
|
// shutdown condition is detected.
|
|
|
while (!shouldShutdown()) {
|
|
|
- getIoService()->run_one();
|
|
|
+ garbageCollectListeners();
|
|
|
+ runIO();
|
|
|
}
|
|
|
stopIOService();
|
|
|
} catch (const std::exception& ex) {
|
|
@@ -120,6 +88,15 @@ CtrlAgentProcess::run() {
|
|
|
LOG_DEBUG(agent_logger, isc::log::DBGLVL_START_SHUT, CTRL_AGENT_RUN_EXIT);
|
|
|
}
|
|
|
|
|
|
+size_t
|
|
|
+CtrlAgentProcess::runIO() {
|
|
|
+ size_t cnt = getIoService()->get_io_service().poll();
|
|
|
+ if (!cnt) {
|
|
|
+ cnt = getIoService()->get_io_service().run_one();
|
|
|
+ }
|
|
|
+ return (cnt);
|
|
|
+}
|
|
|
+
|
|
|
isc::data::ConstElementPtr
|
|
|
CtrlAgentProcess::shutdown(isc::data::ConstElementPtr /*args*/) {
|
|
|
setShutdownFlag(true);
|
|
@@ -129,17 +106,113 @@ CtrlAgentProcess::shutdown(isc::data::ConstElementPtr /*args*/) {
|
|
|
isc::data::ConstElementPtr
|
|
|
CtrlAgentProcess::configure(isc::data::ConstElementPtr config_set,
|
|
|
bool check_only) {
|
|
|
+ // System reconfiguration often poses an interesting issue whereby the
|
|
|
+ // configuration parsing is successful, but an attempt to use a new
|
|
|
+ // configuration is not. This will leave us in the inconsistent state
|
|
|
+ // when the configuration is in fact only partially applied and the
|
|
|
+ // system's ability to operate is impaired. The use of C++ lambda is
|
|
|
+ // a way to resolve this problem by injecting the code to the
|
|
|
+ // simpleParseConfig which performs an attempt to open new instance
|
|
|
+ // of the listener (if required). The lambda code will throw an
|
|
|
+ // exception if it fails and cause the simpleParseConfig to rollback
|
|
|
+ // configuration changes and report an error.
|
|
|
+ ConstElementPtr answer = getCfgMgr()->simpleParseConfig(config_set,
|
|
|
+ check_only,
|
|
|
+ [this]() {
|
|
|
+ DCfgContextBasePtr base_ctx = getCfgMgr()->getContext();
|
|
|
+ CtrlAgentCfgContextPtr
|
|
|
+ ctx = boost::dynamic_pointer_cast<CtrlAgentCfgContext>(base_ctx);
|
|
|
+
|
|
|
+ if (!ctx) {
|
|
|
+ isc_throw(Unexpected, "Interal logic error: bad context type");
|
|
|
+ }
|
|
|
+
|
|
|
+ /// @todo: If the parameter is a hostname, we need to resolve it.
|
|
|
+ IOAddress server_address("::");
|
|
|
+ try {
|
|
|
+ server_address = IOAddress(ctx->getHttpHost());
|
|
|
+
|
|
|
+ } catch (const IOError& e) {
|
|
|
+ isc_throw(BadValue, "Failed to convert " << ctx->getHttpHost()
|
|
|
+ << " to IP address:" << e.what());
|
|
|
+ }
|
|
|
+
|
|
|
+ uint16_t server_port = ctx->getHttpPort();
|
|
|
+
|
|
|
+ // Only open a new listener if the configuration has changed.
|
|
|
+ if (http_listeners_.empty() ||
|
|
|
+ (http_listeners_.back()->getLocalAddress() != server_address) ||
|
|
|
+ (http_listeners_.back()->getLocalPort() != server_port)) {
|
|
|
+ // Create response creator factory first. It will be used to
|
|
|
+ // generate response creators. Each response creator will be
|
|
|
+ // used to generate answer to specific request.
|
|
|
+ HttpResponseCreatorFactoryPtr rcf(new CtrlAgentResponseCreatorFactory());
|
|
|
+
|
|
|
+ // Create http listener. It will open up a TCP socket and be
|
|
|
+ // prepared to accept incoming connection.
|
|
|
+ HttpListenerPtr http_listener(new HttpListener(*getIoService(),
|
|
|
+ server_address,
|
|
|
+ server_port, rcf,
|
|
|
+ REQUEST_TIMEOUT));
|
|
|
+
|
|
|
+ // Instruct the http listener to actually open socket, install
|
|
|
+ // callback and start listening.
|
|
|
+ http_listener->start();
|
|
|
+
|
|
|
+ // The new listener is running so add it to the collection of
|
|
|
+ // active listeners. The next step will be to remove all other
|
|
|
+ // active listeners, but we do it inside the main process loop.
|
|
|
+ http_listeners_.push_back(http_listener);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Ok, seems we're good to go.
|
|
|
+ LOG_INFO(agent_logger, CTRL_AGENT_HTTP_SERVICE_STARTED)
|
|
|
+ .arg(server_address.toText()).arg(server_port);
|
|
|
+
|
|
|
+ });
|
|
|
+
|
|
|
int rcode = 0;
|
|
|
- isc::data::ConstElementPtr answer = getCfgMgr()->simpleParseConfig(config_set,
|
|
|
- check_only);
|
|
|
config::parseAnswer(rcode, answer);
|
|
|
return (answer);
|
|
|
}
|
|
|
|
|
|
+void
|
|
|
+CtrlAgentProcess::garbageCollectListeners() {
|
|
|
+ // We expect only one active listener. If there are more (most likely 2),
|
|
|
+ // it means we have just reconfigured the server and need to shut down all
|
|
|
+ // listeners execept the most recently added.
|
|
|
+ if (http_listeners_.size() > 1) {
|
|
|
+ // Stop no longer used listeners.
|
|
|
+ for (auto l = http_listeners_.begin(); l != http_listeners_.end() - 1;
|
|
|
+ ++l) {
|
|
|
+ (*l)->stop();
|
|
|
+ }
|
|
|
+ // We have stopped listeners but there may be some pending handlers
|
|
|
+ // related to these listeners. Need to invoke these handlers.
|
|
|
+ getIoService()->get_io_service().poll();
|
|
|
+ // Finally, we're ready to remove no longer used listeners.
|
|
|
+ http_listeners_.erase(http_listeners_.cbegin(),
|
|
|
+ http_listeners_.cend() - 1);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
CtrlAgentCfgMgrPtr
|
|
|
CtrlAgentProcess::getCtrlAgentCfgMgr() {
|
|
|
- return(boost::dynamic_pointer_cast<CtrlAgentCfgMgr>(getCfgMgr()));
|
|
|
+ return (boost::dynamic_pointer_cast<CtrlAgentCfgMgr>(getCfgMgr()));
|
|
|
+}
|
|
|
+
|
|
|
+ConstHttpListenerPtr
|
|
|
+CtrlAgentProcess::getHttpListener() const {
|
|
|
+ // Return the most recent listener or null.
|
|
|
+ return (http_listeners_.empty() ? ConstHttpListenerPtr() :
|
|
|
+ http_listeners_.back());
|
|
|
+}
|
|
|
+
|
|
|
+bool
|
|
|
+CtrlAgentProcess::isListening() const {
|
|
|
+ // If there are is a listener, we're listening.
|
|
|
+ return (static_cast<bool>(getHttpListener()));
|
|
|
}
|
|
|
|
|
|
} // namespace isc::agent
|