Commit fd911f47 authored by Thomas Markwalder's avatar Thomas Markwalder
Browse files

[2956] Interrim checkin to allow merge with 2955. Note a subsequent commit

will be required to make d2 build.
Modified files:
    Makefile.am
    d2_log.cc
    d2_log.h
    d2_messages.mes
    d2.spec
    main.cc
    tests/Makefile.am
    tests/d2_test.py
New files:
    d2_controller.cc
    d2_controller.h
    d_controller.cc
    d_controller.h
    spec_config.h
    tests/d2_controller_unittests.cc
    tests/d_controller_unittests.cc
    tests/d_test_stubs.cc
    tests/d_test_stubs.h
parent 7f8feaab
......@@ -16,7 +16,7 @@ endif
pkglibexecdir = $(libexecdir)/@PACKAGE@
CLEANFILES = *.gcno *.gcda spec_config.h d2_srv_messages.h d2_srv_messages.cc
CLEANFILES = *.gcno *.gcda spec_config.h d2_messages.h d2_messages.cc
man_MANS = b10-d2.8
DISTCLEANFILES = $(man_MANS)
......@@ -48,12 +48,19 @@ pkglibexec_PROGRAMS = b10-d2
b10_d2_SOURCES = main.cc
b10_d2_SOURCES += d2_log.cc d2_log.h
b10_d2_SOURCES += d_process.h
b10_d2_SOURCES += d2_process.cc d2_process.h
b10_d2_SOURCES += d_controller.cc d_controller.h
b10_d2_SOURCES += d2_controller.cc d2_controller.h
nodist_b10_d2_SOURCES = d2_messages.h d2_messages.cc
EXTRA_DIST += d2_messages.mes
b10_d2_LDADD = $(top_builddir)/src/lib/log/libb10-log.la
b10_d2_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
b10_d2_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
b10_d2_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
b10_d2_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
b10_d2dir = $(pkgdatadir)
b10_d2_DATA = d2.spec
{
"module_spec": {
"module_name": "D2",
"module_description": "DHCP-DDNS process",
"module_description": "DHPC-DDNS Service",
"config_data": [
],
"commands": [
{
"command_name": "shutdown",
"command_description": "Shuts down the D2 process.",
"command_args": [
{
"item_name": "pid",
"item_type": "integer",
"item_optional": true
}
]
}
{
"command_name": "shutdown",
"command_description": "Shut down the stats httpd",
"command_args": [
{
"item_name": "pid",
"item_type": "integer",
"item_optional": true
}
]
}
]
}
}
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <d2/d2_controller.h>
#include <d2/d2_process.h>
#include <d2/spec_config.h>
namespace isc {
namespace d2 {
DControllerBasePtr&
D2Controller::instance() {
// If the instance hasn't been created yet, create it. Note this method
// must use the base class singleton instance methods. The base class
// must own the singleton in order to use it within BIND10 static function
// callbacks.
if (!getController()) {
setController(new D2Controller());
}
return (getController());
}
DProcessBase* D2Controller::createProcess() {
// Instantiate and return an instance of the D2 application process. Note
// that the process is passed the controller's io_service.
return (new D2Process(getName().c_str(), getIOService()));
}
D2Controller::D2Controller()
: DControllerBase(D2_MODULE_NAME) {
// set the BIND10 spec file either from the environment or
// use the production value.
if (getenv("B10_FROM_BUILD")) {
setSpecFileName(std::string(getenv("B10_FROM_BUILD")) +
"/src/bin/d2/d2.spec");
} else {
setSpecFileName(D2_SPECFILE_LOCATION);
}
}
D2Controller::~D2Controller() {
}
}; // end namespace isc::d2
}; // end namespace isc
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef D2_CONTROLLER_H
#define D2_CONTROLLER_H
#include <d2/d_controller.h>
namespace isc {
namespace d2 {
/// @brief Process Controller for D2 Process
/// This class is the DHCP-DDNS specific derivation of DControllerBase. It
/// creates and manages an instance of the DCHP-DDNS application process,
/// D2Process.
/// @TODO Currently, this class provides only the minimum required specialized
/// behavior to run the DHCP-DDNS service. It may very well expand as the
/// service implementation evolves. Some thought was given to making
/// DControllerBase a templated class but the labor savings versus the
/// potential number of virtual methods which may be overridden didn't seem
/// worth the clutter at this point.
class D2Controller : public DControllerBase {
public:
/// @brief Static singleton instance method. This method returns the
/// base class singleton instance member. It instantiates the singleton
/// and sets the base class instance member upon first invocation.
///
/// @return returns the a pointer reference to the singleton instance.
static DControllerBasePtr& instance();
/// @brief Destructor.
virtual ~D2Controller();
private:
/// @brief Creates an instance of the DHCP-DDNS specific application
/// process. This method is invoked during the process initialization
/// step of the controller launch.
///
/// @return returns a DProcessBase* to the applicatio process created.
/// Note the caller is responsible for destructing the process. This
/// is handled by the base class, which wraps this pointer with a smart
/// pointer.
virtual DProcessBase* createProcess();
/// @brief Constructor is declared private to maintain the integrity of
/// the singleton instance.
D2Controller();
};
}; // namespace isc::d2
}; // namespace isc
#endif
......@@ -19,7 +19,8 @@
namespace isc {
namespace d2 {
isc::log::Logger d2_logger("d2");
const char* const D2_MODULE_NAME = "b10-d2";
isc::log::Logger d2_logger(D2_MODULE_NAME);
} // namespace d2
} // namespace isc
......
......@@ -22,12 +22,16 @@
namespace isc {
namespace d2 {
/// @TODO need brief
extern const char* const D2_MODULE_NAME;
/// Define the logger for the "d2" module part of b10-d2. We could define
/// a logger in each file, but we would want to define a common name to avoid
/// spelling mistakes, so it is just one small step from there to define a
/// module-common logger.
extern isc::log::Logger d2_logger;
} // namespace d2
} // namespace isc
......
......@@ -14,15 +14,90 @@
$NAMESPACE isc::d2
% D2_STARTING : process starting
This is a debug message issued during a D2 process startup.
% D2CTL_STARTING DHCP-DDNS controller starting, pid: %1
This is an informational message issued when controller for DHCP-DDNS
service first starts.
% D2_START_INFO pid: %1, verbose: %2, standalone: %3
This is a debug message issued during the D2 process startup.
It lists some information about the parameters with which the
process is running.
% D2CTL_STOPPING DHCP-DDNS controller is exiting
This is an informational message issued when the controller is exiting
following a shut down (normal or otherwise) of the DDHCP-DDNS process.
% D2_SHUTDOWN : process is performing a normal shutting down
This is a debug message issued when a D2 process shuts down
normally in response to command to stop.
% D2PRC_SHUTDOWN DHCP-DDNS process is performing a normal shut down
This is a debug message issued when the service process has been instructed
to shut down by the controller.
% D2PRC_RUN_ENTER process has entered the event loop
This is a debug message issued when the D2 process enters it's
run method.
% D2PRC_RUN_EXIT process is exiting the event loop
This is a debug message issued when the D2 process exits the
in event loop.
% D2PRC_FAILED process experienced a fatal error: %1
This is a debug message issued when the D2 process encounters an
unrecoverable error from within the event loop.
% D2PRC_CONFIGURE new configuration received: %1
This is a debug message issued when the D2 process configure method
has been invoked.
% D2PRC_COMMAND command directive received, command: %1 - args: %2
This is a debug message issued when the D2 process command method
has been invoked.
% D2CTL_INIT_PROCESS initializing application proces
This debug message is issued just before the controller attempts
to create and initialize it's process instance.
% D2CTL_SESSION_FAIL failed to establish BIND 10 session: %1
The controller has failed to establish communication with the rest of BIND
10 and will exit.
% D2CTL_DISCONNECT_FAIL failed to disconnect from BIND 10 session: %1
The controller has failed to terminate communication with the rest of BIND
10.
% D2CTL_STANDALONE skipping message queue, running standalone
This is a debug message indicating that the controller is running in the
process in standalone mode. This means it will not connected to the BIND10
message queue. Standalone mode is only useful during program development,
and should not be used in a production environment.
% D2CTL_RUN_PROCESS starting application proces event loop
This debug message is issued just before the controller invokes
the application process run method.
% D2CTL_FAILED process failed: %1
The controller has encountered a fatal error and is terminating.
The reason for the failure is included in the message.
% D2CTL_CCSESSION_STARTING starting control channel session, specfile: %1
This debug message is issued just before the controller attempts
to establish a session with the BIND 10 control channel.
% D2CTL_CCSESSION_ENDING ending control channel session
This debug message is issued just before the controller attempts
to disconnect from its session with the BIND 10 control channel.
% D2CTL_CONFIG_STUB configuration stub handler called
This debug message is issued when the dummy handler for configuration
events is called. This only happens during intial startup.
% D2CTL_CONFIG_LOAD_FAIL failed to load configuration: %1
This critical error message indicates that the initial process
configuration has failed. The service will start, but will not
process requests until the configuration has been corrected.
% D2CTL_COMMAND_RECEIVED received command %1, arguments: %2
A debug message listing the command (and possible arguments) received
from the BIND 10 control system by the controller.
% D2CTL_NOT_RUNNING The application process instance is not running
A warning message is issued when an attempt is made to shut down the
the process when it is not running.
% D2CTL_CONFIG_UPDATE updated configuration received: %1
A debug message indicating that the controller has received an
updated configuration from the BIND 10 configuration system.
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <d2/d2_log.h>
#include <d2/d_controller.h>
#include <exceptions/exceptions.h>
#include <log/logger_support.h>
#include <sstream>
namespace isc {
namespace d2 {
DControllerBasePtr DControllerBase::controller_;
// Note that the constructor instantiates the controller's primary IOService.
DControllerBase::DControllerBase(const char* name)
: name_(name), stand_alone_(false), verbose_(false),
spec_file_name_(""), io_service_(new isc::asiolink::IOService()){
}
void
DControllerBase::setController(DControllerBase* controller) {
if (controller_) {
// This shouldn't happen, but let's make sure it can't be done.
// It represents a programmatic error.
isc_throw (DControllerBaseError,
"Multiple controller instances attempted.");
}
controller_ = DControllerBasePtr(controller);
}
int
DControllerBase::launch(int argc, char* argv[]) {
int ret = EXIT_SUCCESS;
// Step 1 is to parse the command line arguments.
try {
parseArgs(argc, argv);
} catch (const InvalidUsage& ex) {
usage(ex.what());
return (EXIT_FAILURE);
}
#if 1
//@TODO During initial development default to max log, no buffer
isc::log::initLogger(name_, isc::log::DEBUG,
isc::log::MAX_DEBUG_LEVEL, NULL, false);
#else
// Now that we know what the mode flags are, we can init logging.
// If standalone is enabled, do not buffer initial log messages
isc::log::initLogger(name_,
((verbose_ && stand_alone_)
? isc::log::DEBUG : isc::log::INFO),
isc::log::MAX_DEBUG_LEVEL, NULL, !stand_alone_);
#endif
LOG_DEBUG(d2_logger, DBGLVL_START_SHUT, D2CTL_STARTING).arg(getpid());
try {
// Step 2 is to create and init the application process.
initProcess();
// Next we connect if we are running integrated.
if (!stand_alone_) {
try {
establishSession();
} catch (const std::exception& ex) {
LOG_ERROR(d2_logger, D2CTL_SESSION_FAIL).arg(ex.what());
return (EXIT_FAILURE);
}
} else {
LOG_DEBUG(d2_logger, DBGLVL_START_SHUT, D2CTL_STANDALONE);
}
// Everything is clear for launch, so start the application's
// event loop.
runProcess();
} catch (const std::exception& ex) {
LOG_FATAL(d2_logger, D2CTL_FAILED).arg(ex.what());
ret = EXIT_FAILURE;
}
// If running integrated, always try to disconnect.
if (!stand_alone_) {
try {
disconnectSession();
} catch (const std::exception& ex) {
LOG_ERROR(d2_logger, D2CTL_DISCONNECT_FAIL).arg(ex.what());
ret = EXIT_FAILURE;
}
}
// All done, so bail out.
LOG_INFO(d2_logger, D2CTL_STOPPING);
return (ret);
}
void
DControllerBase::parseArgs(int argc, char* argv[])
{
// Iterate over the given command line options. If its a stock option
// ("s" or "v") handle it here. If its a valid custom option, then
// invoke customOption.
int ch;
opterr = 0;
optind = 1;
std::string opts(":vs" + getCustomOpts());
while ((ch = getopt(argc, argv, opts.c_str())) != -1) {
switch (ch) {
case 'v':
// Enables verbose logging.
verbose_ = true;
break;
case 's':
// Enables stand alone or "BINDLESS" operation.
stand_alone_ = true;
break;
case '?': {
// We hit an invalid option.
std::stringstream tmp;
tmp << " unsupported option: [" << (char)optopt << "] "
<< (!optarg ? "" : optarg);
isc_throw(InvalidUsage,tmp.str());
break;
}
default:
// We hit a valid custom option
if (!customOption(ch, optarg)) {
// this would be a programmatic err
std::stringstream tmp;
tmp << " Option listed but implemented?: [" <<
(char)ch << "] " << (!optarg ? "" : optarg);
isc_throw(InvalidUsage,tmp.str());
}
break;
}
}
// There was too much information on the command line.
if (argc > optind) {
std::stringstream tmp;
tmp << "extraneous command line information";
isc_throw(InvalidUsage,tmp.str());
}
}
bool
DControllerBase::customOption(int /* option */, char* /*optarg*/)
{
// Default implementation returns false.
return (false);
}
void
DControllerBase::initProcess() {
LOG_DEBUG(d2_logger, DBGLVL_START_SHUT, D2CTL_INIT_PROCESS);
// Invoke virtual method to instantiate the application process.
try {
process_.reset(createProcess());
} catch (const std::exception& ex) {
isc_throw (DControllerBaseError, std::string("createProcess failed:")
+ ex.what());
}
// This is pretty unlikely, but will test for it just to be safe..
if (!process_) {
isc_throw (DControllerBaseError, "createProcess returned NULL");
}
// Invoke application's init method
// @TODO This call may throw DProcessError
process_->init();
}
void
DControllerBase::establishSession() {
LOG_DEBUG(d2_logger, DBGLVL_START_SHUT, D2CTL_CCSESSION_STARTING)
.arg(spec_file_name_);
// Create the BIND10 command control session with the our IOService.
cc_session_ = SessionPtr(new isc::cc::Session(
io_service_->get_io_service()));
// Create the BIND10 config session with the stub configuration handler.
// This handler is internally invoked by the constructor and on success
// the constructor updates the current session with the configuration that
// had been committed in the previous session. If we do not install
// the dummy handler, the previous configuration would be lost.
config_session_ = ModuleCCSessionPtr(new isc::config::ModuleCCSession(
spec_file_name_, *cc_session_,
dummyConfigHandler, commandHandler,
false));
// Enable configuration even processing.
config_session_->start();
// We initially create ModuleCCSession() with a dummy configHandler, as
// the session module is too eager to send partial configuration.
// Replace the dummy config handler with the real handler.
config_session_->setConfigHandler(configHandler);
// Call the real configHandler with the full configuration retrieved
// from the config session.
isc::data::ConstElementPtr answer = configHandler(
config_session_->getFullConfig());
// Parse the answer returned from the configHandler. Log the error but
// keep running. This provides an opportunity for the user to correct
// the configuration dynamically.
int ret = 0;
isc::data::ConstElementPtr comment = isc::config::parseAnswer(ret, answer);
if (ret) {
LOG_ERROR(d2_logger, D2CTL_CONFIG_LOAD_FAIL).arg(comment->str());
}
// Lastly, call onConnect. This allows deriving class to execute custom
// logic predicated by session connect.
onSessionConnect();
}
void
DControllerBase::runProcess() {
LOG_DEBUG(d2_logger, DBGLVL_START_SHUT, D2CTL_RUN_PROCESS);
if (!process_) {
// This should not be possible.
isc_throw(DControllerBaseError, "Process not initialized");
}
// Invoke the applicatio process's run method. This may throw DProcessError
process_->run();
}
void DControllerBase::disconnectSession() {
LOG_DEBUG(d2_logger, DBGLVL_START_SHUT, D2CTL_CCSESSION_ENDING);
// Call virtual onDisconnect. Allows deriving class to execute custom
// logic prior to session loss.
onSessionDisconnect();
// Destroy the BIND10 config session.
if (config_session_) {
config_session_.reset();
}
// Destroy the BIND10 command and control session.
if (cc_session_) {
cc_session_->disconnect();
cc_session_.reset();
}
}
isc::data::ConstElementPtr
DControllerBase::dummyConfigHandler(isc::data::ConstElementPtr) {
LOG_DEBUG(d2_logger, DBGLVL_START_SHUT, D2CTL_CONFIG_STUB);
return (isc::config::createAnswer(0, "Configuration accepted."));
}
isc::data::ConstElementPtr
DControllerBase::configHandler(isc::data::ConstElementPtr new_config) {
LOG_DEBUG(d2_logger, DBGLVL_COMMAND, D2CTL_CONFIG_UPDATE)
.arg(new_config->str());
if (!controller_) {
// This should never happen as we install the handler after we
// instantiate the server.
isc::data::ConstElementPtr answer =
isc::config::createAnswer(1, "Configuration rejected,"
" Controller has not been initialized.");
return (answer);
}
// Invoke the instance method on the controller singleton.
return (controller_->updateConfig(new_config));
}
// Static callback which invokes non-static handler on singleton
isc::data::ConstElementPtr
DControllerBase::commandHandler(const std::string& command,
isc::data::ConstElementPtr args) {
LOG_DEBUG(d2_logger, DBGLVL_COMMAND, D2CTL_COMMAND_RECEIVED)
.arg(command).arg(args->str());
if (!controller_ ) {
// This should never happen as we install the handler after we
// instantiate the server.
isc::data::ConstElementPtr answer =
isc::config::createAnswer(1, "Command rejected,"
" Controller has not been initialized.");
return (answer);
}
// Invoke the instance method on the controller singleton.
return (controller_->executeCommand(command, args));
}