pkglibexec_PROGRAMS = b10-dhcp-ddns
b10_dhcp_ddns_SOURCES = main.cc
+b10_dhcp_ddns_SOURCES += d_process.h
+b10_dhcp_ddns_SOURCES += d_controller.cc d_controller.h
+b10_dhcp_ddns_SOURCES += d_cfg_mgr.cc d_cfg_mgr.h
b10_dhcp_ddns_SOURCES += d2_asio.h
b10_dhcp_ddns_SOURCES += d2_log.cc d2_log.h
b10_dhcp_ddns_SOURCES += d2_process.cc d2_process.h
-b10_dhcp_ddns_SOURCES += d_controller.cc d_controller.h
-b10_dhcp_ddns_SOURCES += d2_controller.cc d2_controller.h
-b10_dhcp_ddns_SOURCES += d_cfg_mgr.cc d_cfg_mgr.h
b10_dhcp_ddns_SOURCES += d2_config.cc d2_config.h
b10_dhcp_ddns_SOURCES += d2_cfg_mgr.cc d2_cfg_mgr.h
b10_dhcp_ddns_SOURCES += d2_queue_mgr.cc d2_queue_mgr.h
b10_dhcp_ddns_SOURCES += nc_trans.cc nc_trans.h
b10_dhcp_ddns_SOURCES += state_model.cc state_model.h
+if CONFIG_BACKEND_BUNDY
+b10_dhcp_ddns_SOURCES += bundy_d2_controller.cc bundy_d2_controller.h
+else
+if CONFIG_BACKEND_JSON
+b10_dhcp_ddns_SOURCES += d2_controller.cc d2_controller.h
+endif
+endif
+
nodist_b10_dhcp_ddns_SOURCES = d2_messages.h d2_messages.cc
EXTRA_DIST += d2_messages.mes
--- /dev/null
+// Copyright (C) 2014 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/bundy_d2_controller.h>
+#include <d2/d2_process.h>
+#include <d2/spec_config.h>
+
+#include <sstream>
+
+namespace isc {
+namespace d2 {
+
+/// @brief Defines the application name, this is passed into base class
+/// it may be used to locate configuration data and appears in log statement.
+const char* D2Controller::d2_app_name_ = "DHCP-DDNS";
+
+/// @brief Defines the executable name. This is passed into the base class
+const char* D2Controller::d2_bin_name_ = "b10-dhcp-ddns";
+
+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 have access to the singleton in order to use it within BUNDY
+ // static function callbacks.
+ if (!getController()) {
+ DControllerBasePtr controller_ptr(new D2Controller());
+ setController(controller_ptr);
+ }
+
+ 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(getAppName().c_str(), getIOService()));
+}
+
+D2Controller::D2Controller()
+ : DControllerBase(d2_app_name_, d2_bin_name_) {
+ // set the 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/dhcp-ddns.spec");
+ } else {
+ setSpecFileName(D2_SPECFILE_LOCATION);
+ }
+}
+
+D2Controller::~D2Controller() {
+}
+
+void
+D2Controller::launch(int argc, char* argv[], const bool test_mode) {
+ // Step 1 is to parse the command line arguments.
+ try {
+ parseArgs(argc, argv);
+ } catch (const InvalidUsage& ex) {
+ usage(ex.what());
+ throw; // rethrow it
+ }
+
+ // Do not initialize logger here if we are running unit tests. It would
+ // replace an instance of unit test specific logger.
+ if (!test_mode) {
+ // 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(getBinName(),
+ (isVerbose() ? isc::log::DEBUG : isc::log::INFO),
+ isc::log::MAX_DEBUG_LEVEL, NULL, true);
+ }
+
+ LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT, DCTL_STARTING)
+ .arg(getAppName()).arg(getpid());
+ try {
+ // Step 2 is to create and initialize the application process object.
+ initProcess();
+ } catch (const std::exception& ex) {
+ LOG_FATAL(dctl_logger, DCTL_INIT_PROCESS_FAIL)
+ .arg(getAppName()).arg(ex.what());
+ isc_throw (ProcessInitError,
+ "Application Process initialization failed: " << ex.what());
+ }
+
+ // Next we connect to Bundy.
+ try {
+ establishSession();
+ } catch (const std::exception& ex) {
+ LOG_FATAL(dctl_logger, DCTL_SESSION_FAIL).arg(ex.what());
+ isc_throw (SessionStartError,
+ "Session start up failed: " << ex.what());
+ }
+
+ // Everything is clear for launch, so start the application's
+ // event loop.
+ try {
+ runProcess();
+ } catch (const std::exception& ex) {
+ LOG_FATAL(dctl_logger, DCTL_PROCESS_FAILED)
+ .arg(getAppName()).arg(ex.what());
+ isc_throw (ProcessRunError,
+ "Application process event loop failed: " << ex.what());
+ }
+
+ // Disconnect from Bundy.
+ try {
+ disconnectSession();
+ } catch (const std::exception& ex) {
+ LOG_ERROR(dctl_logger, DCTL_DISCONNECT_FAIL)
+ .arg(getAppName()).arg(ex.what());
+ isc_throw (SessionEndError, "Session end failed: " << ex.what());
+ }
+
+ // All done, so bail out.
+ LOG_INFO(dctl_logger, DCTL_STOPPING).arg(getAppName());
+}
+
+void
+D2Controller::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(":v" + getCustomOpts());
+ while ((ch = getopt(argc, argv, opts.c_str())) != -1) {
+ switch (ch) {
+ case 'v':
+ // Enables verbose logging.
+ setVerbose(true);
+ break;
+
+ case '?': {
+ // We hit an invalid option.
+ isc_throw(InvalidUsage, "unsupported option: ["
+ << static_cast<char>(optopt) << "] "
+ << (!optarg ? "" : optarg));
+
+ break;
+ }
+
+ default:
+ // We hit a valid custom option
+ if (!customOption(ch, optarg)) {
+ // This would be a programmatic error.
+ isc_throw(InvalidUsage, " Option listed but implemented?: ["
+ << static_cast<char>(ch) << "] "
+ << (!optarg ? "" : optarg));
+ }
+ break;
+ }
+ }
+
+ // There was too much information on the command line.
+ if (argc > optind) {
+ isc_throw(InvalidUsage, "extraneous command line information");
+ }
+}
+
+void
+D2Controller::establishSession() {
+ LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT, DCTL_CCSESSION_STARTING)
+ .arg(getAppName()).arg(getSpecFileName());
+
+ // Create the BIND10 command control session with the our IOService.
+ cc_session_ = SessionPtr(new isc::cc::Session(
+ getIOService()->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(
+ getSpecFileName(), *cc_session_,
+ dummyConfigHandler,
+ DControllerBase::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(DControllerBase::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(dctl_logger, DCTL_CONFIG_LOAD_FAIL)
+ .arg(getAppName()).arg(comment->str());
+ }
+}
+
+void D2Controller::disconnectSession() {
+ LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT, DCTL_CCSESSION_ENDING)
+ .arg(getAppName());
+
+ // 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
+D2Controller::dummyConfigHandler(isc::data::ConstElementPtr) {
+ LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT, DCTL_CONFIG_STUB)
+ .arg(getController()->getAppName());
+ return (isc::config::createAnswer(0, "Configuration accepted."));
+}
+
+isc::data::ConstElementPtr
+D2Controller::updateConfig(isc::data::ConstElementPtr new_config) {
+ if (!config_session_) {
+ // That should never happen as we install config_handler
+ // after we instantiate the server.
+ isc::data::ConstElementPtr answer =
+ isc::config::createAnswer(1, "Configuration rejected,"
+ " Session has not started.");
+ return (answer);
+ }
+
+ // Let's get the existing configuration.
+ isc::data::ConstElementPtr full_config = config_session_->getFullConfig();
+
+ // The configuration passed to this handler function is partial.
+ // In other words, it just includes the values being modified.
+ // In the same time, there may be dependencies between various
+ // configuration parsers. For example: the option value can
+ // be set if the definition of this option is set. If someone removes
+ // an existing option definition then the partial configuration that
+ // removes that definition is triggered while a relevant option value
+ // may remain configured. This eventually results in the
+ // configuration being in the inconsistent state.
+ // In order to work around this problem we need to merge the new
+ // configuration with the existing (full) configuration.
+
+ // Let's create a new object that will hold the merged configuration.
+ boost::shared_ptr<isc::data::MapElement>
+ merged_config(new isc::data::MapElement());
+
+ // Merge an existing and new configuration.
+ merged_config->setValue(full_config->mapValue());
+ isc::data::merge(merged_config, new_config);
+
+ // Send the merged configuration to the application.
+ return (getProcess()->configure(merged_config));
+}
+
+
+void
+D2Controller::usage(const std::string & text)
+{
+ if (text != "") {
+ std::cerr << "Usage error: " << text << std::endl;
+ }
+
+ std::cerr << "Usage: " << getBinName() << std::endl;
+ std::cerr << " -v: verbose output" << std::endl;
+ std::cerr << getUsageText() << std::endl;
+}
+
+
+}; // namespace isc::d2
+}; // namespace isc
--- /dev/null
+// Copyright (C) 2014 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 BUNDY_D2_CONTROLLER_H
+#define BUNDY_D2_CONTROLLER_H
+
+#include <cc/data.h>
+#include <cc/session.h>
+#include <config/ccsession.h>
+#include <d2/d2_asio.h>
+#include <d2/d2_log.h>
+#include <d2/d_controller.h>
+#include <d2/d_process.h>
+#include <exceptions/exceptions.h>
+#include <log/logger_support.h>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/noncopyable.hpp>
+
+
+namespace isc {
+namespace d2 {
+
+/// @brief Exception thrown when the session start up fails.
+class SessionStartError: public isc::Exception {
+public:
+ SessionStartError (const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+/// @brief Exception thrown when the session end fails.
+class SessionEndError: public isc::Exception {
+public:
+ SessionEndError (const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+/// @brief Defines a shared pointer to a Session.
+typedef boost::shared_ptr<isc::cc::Session> SessionPtr;
+
+/// @brief Defines a shared pointer to a ModuleCCSession.
+typedef boost::shared_ptr<isc::config::ModuleCCSession> ModuleCCSessionPtr;
+
+/// @brief Bundy-integrated Process Controller for D2 Process
+/// This class is the DHCP-DDNS specific derivation of DControllerBase which
+/// is managed by BUNDY. It creates and manages an instance of the DHCP-DDNS
+/// application process, D2Process.
+///
+/// D2 will be constructed with this class if the project is configured with
+/// --with-kea-config=BUNDY
+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 pointer reference to the singleton instance.
+ static DControllerBasePtr& instance();
+
+ /// @brief Destructor.
+ virtual ~D2Controller();
+
+ /// @brief Defines the application name, this is passed into base class
+ /// and appears in log statements.
+ static const char* d2_app_name_;
+
+ /// @brief Defines the executable name. This is passed into the base class
+ /// by convention this should match the BUNDY module name.
+ static const char* d2_bin_name_;
+
+ /// @brief Acts as the primary entry point into the controller execution
+ /// and provides the outermost application control logic:
+ ///
+ /// 1. parse command line arguments
+ /// 2. instantiate and initialize the application process
+ /// 3. establish BUNDY session(s) if in integrated mode
+ /// 4. start and wait on the application process event loop
+ /// 5. upon event loop completion, disconnect from BUNDY (if needed)
+ /// 6. exit to the caller
+ ///
+ /// It is intended to be called from main() and be given the command line
+ /// arguments. Note this method is deliberately not virtual to ensure the
+ /// proper sequence of events occur.
+ ///
+ /// This function can be run in the test mode. It prevents initialization
+ /// of D2 module logger. This is used in unit tests which initialize logger
+ /// in their main function. Such logger uses environmental variables to
+ /// control severity, verbosity etc. Reinitialization of logger by this
+ /// function would replace unit tests specific logger configuration with
+ /// this suitable for D2 running as a bind10 module.
+ ///
+ /// @param argc is the number of command line arguments supplied
+ /// @param argv is the array of string (char *) command line arguments
+ /// @param test_mode is a bool value which indicates if
+ /// @c DControllerBase::launch should be run in the test mode (if true).
+ /// This parameter doesn't have default value to force test implementers to
+ /// enable test mode explicitly.
+ ///
+ /// @throw throws one of the following exceptions:
+ /// InvalidUsage - Indicates invalid command line.
+ /// ProcessInitError - Failed to create and initialize application
+ /// process object.
+ /// SessionStartError - Could not connect to BUNDY (integrated mode only).
+ /// ProcessRunError - A fatal error occurred while in the application
+ /// process event loop.
+ /// SessionEndError - Could not disconnect from BUNDY (integrated mode
+ /// only).
+ virtual void launch(int argc, char* argv[], const bool test_mode);
+
+ /// @brief A dummy configuration handler that always returns success.
+ ///
+ /// This configuration handler does not perform configuration
+ /// parsing and always returns success. A dummy handler should
+ /// be installed using \ref isc::config::ModuleCCSession ctor
+ /// to get the initial configuration. This initial configuration
+ /// comprises values for only those elements that were modified
+ /// the previous session. The D2 configuration parsing can't be
+ /// used to parse the initial configuration because it may need the
+ /// full configuration to satisfy dependencies between the
+ /// various configuration values. Installing the dummy handler
+ /// that guarantees to return success causes initial configuration
+ /// to be stored for the session being created and that it can
+ /// be later accessed with \ref isc::config::ConfigData::getFullConfig.
+ ///
+ /// @param new_config new configuration.
+ ///
+ /// @return success configuration status.
+ static isc::data::ConstElementPtr
+ dummyConfigHandler(isc::data::ConstElementPtr new_config);
+
+ /// @brief Instance method invoked by the configuration event handler and
+ /// which processes the actual configuration update. Provides behavioral
+ /// path for both integrated and stand-alone modes. The current
+ /// implementation will merge the configuration update into the existing
+ /// configuration and then invoke the application process' configure method.
+ ///
+ /// @todo This implementation is will evolve as the D2 configuration
+ /// management task is implemented (trac #2957).
+ ///
+ /// @param new_config is the new configuration
+ ///
+ /// @return returns an Element that contains the results of configuration
+ /// update composed of an integer status value (0 means successful,
+ /// non-zero means failure), and a string explanation of the outcome.
+ virtual isc::data::ConstElementPtr
+ updateConfig(isc::data::ConstElementPtr new_config);
+
+protected:
+ /// @brief Processes the command line arguments. It is the first step
+ /// taken after the controller has been launched. It combines the stock
+ /// list of options with those returned by getCustomOpts(), and uses
+ /// cstdlib's getopt to loop through the command line. The stock options
+ /// It handles stock options directly, and passes any custom options into
+ /// the customOption method. Currently there are only two stock options
+ /// -s for stand alone mode, and -v for verbose logging.
+ ///
+ /// @param argc is the number of command line arguments supplied
+ /// @param argv is the array of string (char *) command line arguments
+ ///
+ /// @throw throws InvalidUsage when there are usage errors.
+ void parseArgs(int argc, char* argv[]);
+
+ /// @brief Establishes connectivity with BUNDY. This method is used
+ /// invoked during launch, if running in integrated mode, following
+ /// successful process initialization. It is responsible for establishing
+ /// the BUNDY control and config sessions. During the session creation,
+ /// it passes in the controller's IOService and the callbacks for command
+ /// directives and config events. Lastly, it will invoke the onConnect
+ /// method providing the derivation an opportunity to execute any custom
+ /// logic associated with session establishment.
+ ///
+ /// @throw the BUNDY framework may throw std::exceptions.
+ void establishSession();
+
+ /// @brief Terminates connectivity with BUNDY. This method is invoked
+ /// in integrated mode after the application event loop has exited. It
+ /// first calls the onDisconnect method providing the derivation an
+ /// opportunity to execute custom logic if needed, and then terminates the
+ /// BUNDY config and control sessions.
+ ///
+ /// @throw the BUNDY framework may throw std:exceptions.
+ void disconnectSession();
+
+ /// @brief Prints the program usage text to std error.
+ ///
+ /// @param text is a string message which will preceded the usage text.
+ /// This is intended to be used for specific usage violation messages.
+ void usage(const std::string& text);
+
+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 application 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 Helper session object that represents raw connection to msgq.
+ SessionPtr cc_session_;
+
+ /// @brief Session that receives configuration and commands.
+ ModuleCCSessionPtr config_session_;
+
+ /// @brief Constructor is declared private to maintain the integrity of
+ /// the singleton instance.
+ D2Controller();
+
+ // DControllerTest is named a friend class to facilitate unit testing while
+ // leaving the intended member scopes intact.
+ friend class DControllerTest;
+};
+
+}; // namespace isc::d2
+}; // namespace isc
+
+#endif
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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
namespace d2 {
/// @brief Defines the application name, this is passed into base class
-/// and appears in log statements.
-const char* D2Controller::d2_app_name_ = "DHCP-DDNS";
+/// it may be used to locate configuration data and appears in log statement.
+const char* D2Controller::d2_app_name_ = "DhcpDdns";
/// @brief Defines the executable name. This is passed into the base class
-/// by convention this should match the BIND10 module name.
const char* D2Controller::d2_bin_name_ = "b10-dhcp-ddns";
DControllerBasePtr&
D2Controller::D2Controller()
: DControllerBase(d2_app_name_, d2_bin_name_) {
- // set the BIND10 spec file either from the environment or
+ // set the spec file either from the environment or
// use the production value.
if (getenv("B10_FROM_BUILD")) {
setSpecFileName(std::string(getenv("B10_FROM_BUILD")) +
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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
static const char* d2_app_name_;
/// @brief Defines the executable name. This is passed into the base class
- /// by convention this should match the BIND10 module name.
+ /// by convention this should match the executable name.
static const char* d2_bin_name_;
private:
configuration has failed. The service will start, but will not
process requests until the configuration has been corrected.
+% DCTL_CONFIG_FILE_LOAD_FAIL %1 configuration could not be loaded from file: %2
+This fatal error message indicates that the application attempted to load its
+initial configuration from file and has failed. The service will exit.
+
% DCTL_CONFIG_START parsing new configuration: %1
A debug message indicating that the application process has received an
updated configuration and has passed it to its configuration manager
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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
#include <d2/d2_log.h>
+#include <config/ccsession.h>
#include <d2/d_controller.h>
#include <exceptions/exceptions.h>
#include <log/logger_support.h>
// Note that the constructor instantiates the controller's primary IOService.
DControllerBase::DControllerBase(const char* app_name, const char* bin_name)
- : app_name_(app_name), bin_name_(bin_name), stand_alone_(false),
+ : app_name_(app_name), bin_name_(bin_name),
verbose_(false), spec_file_name_(""),
io_service_(new isc::asiolink::IOService()){
}
-
void
DControllerBase::setController(const DControllerBasePtr& controller) {
if (controller_) {
void
DControllerBase::launch(int argc, char* argv[], const bool test_mode) {
+
// Step 1 is to parse the command line arguments.
try {
parseArgs(argc, argv);
// replace an instance of unit test specific logger.
if (!test_mode) {
// 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(bin_name_,
- ((verbose_ && stand_alone_)
- ? isc::log::DEBUG : isc::log::INFO),
- isc::log::MAX_DEBUG_LEVEL, NULL, !stand_alone_);
+ Daemon::loggerInit(bin_name_.c_str(), verbose_);
}
LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT, DCTL_STARTING)
"Application Process initialization failed: " << ex.what());
}
- // Next we connect if we are running integrated.
- if (stand_alone_) {
- LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT, DCTL_STANDALONE)
- .arg(app_name_);
- } else {
- try {
- establishSession();
- } catch (const std::exception& ex) {
- LOG_FATAL(dctl_logger, DCTL_SESSION_FAIL).arg(ex.what());
- isc_throw (SessionStartError,
- "Session start up failed: " << ex.what());
- }
+ LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT, DCTL_STANDALONE).arg(app_name_);
+
+ // Step 3 is to load configuration from file.
+ int rcode;
+ isc::data::ConstElementPtr comment
+ = isc::config::parseAnswer(rcode, configFromFile());
+ if (rcode != 0) {
+ LOG_FATAL(dctl_logger, DCTL_CONFIG_FILE_LOAD_FAIL)
+ .arg(app_name_).arg(comment->stringValue());
+ isc_throw (ProcessInitError, "Could Not load configration file: "
+ << comment->stringValue());
}
// Everything is clear for launch, so start the application's
"Application process event loop failed: " << ex.what());
}
- // If running integrated, disconnect.
- if (!stand_alone_) {
- try {
- disconnectSession();
- } catch (const std::exception& ex) {
- LOG_ERROR(dctl_logger, DCTL_DISCONNECT_FAIL)
- .arg(app_name_).arg(ex.what());
- isc_throw (SessionEndError, "Session end failed: " << ex.what());
- }
- }
-
// All done, so bail out.
LOG_INFO(dctl_logger, DCTL_STOPPING).arg(app_name_);
}
-
void
DControllerBase::parseArgs(int argc, char* argv[])
{
int ch;
opterr = 0;
optind = 1;
- std::string opts(":vs" + getCustomOpts());
+ std::string opts("vc:" + getCustomOpts());
while ((ch = getopt(argc, argv, opts.c_str())) != -1) {
switch (ch) {
case 'v':
verbose_ = true;
break;
- case 's':
- // Enables stand alone or "BINDLESS" operation.
- stand_alone_ = true;
+ case 'c':
+ // config file name
+ if (optarg == NULL) {
+ isc_throw(InvalidUsage, "configuration file name missing");
+ }
+
+ Daemon::init(optarg);
break;
case '?': {
}
}
+isc::data::ConstElementPtr
+DControllerBase::configHandler(isc::data::ConstElementPtr new_config) {
+ LOG_DEBUG(dctl_logger, DBGLVL_COMMAND, DCTL_CONFIG_UPDATE)
+ .arg(controller_->getAppName()).arg(new_config->str());
+
+ // 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(dctl_logger, DBGLVL_COMMAND, DCTL_COMMAND_RECEIVED)
+ .arg(controller_->getAppName()).arg(command)
+ .arg(args ? args->str() : "(no args)");
+
+ // Invoke the instance method on the controller singleton.
+ return (controller_->executeCommand(command, args));
+}
+
bool
DControllerBase::customOption(int /* option */, char* /*optarg*/)
{
process_->init();
}
-void
-DControllerBase::establishSession() {
- LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT, DCTL_CCSESSION_STARTING)
- .arg(app_name_).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(dctl_logger, DCTL_CONFIG_LOAD_FAIL)
- .arg(app_name_).arg(comment->str());
+isc::data::ConstElementPtr
+DControllerBase::configFromFile() {
+ isc::data::ConstElementPtr module_config;
+
+ try {
+ std::string config_file = getConfigFileName();
+ if (config_file.empty()) {
+ // Basic sanity check: file name must not be empty.
+ isc_throw(BadValue, "JSON configuration file not specified. Please "
+ "use -c command line option.");
+ }
+
+ // Read contents of the file and parse it as JSON
+ isc::data::ConstElementPtr whole_config =
+ isc::data::Element::fromJSONFile(config_file, true);
+
+ // Extract derivation-specific portion of the configuration.
+ module_config = whole_config->get(getAppName());
+ if (!module_config) {
+ isc_throw(BadValue, "Config file " << config_file <<
+ " does not include '" <<
+ getAppName() << "' entry.");
+ }
+ } catch (const std::exception& ex) {
+ // build an error result
+ isc::data::ConstElementPtr error =
+ isc::config::createAnswer(1,
+ std::string("Configuration parsing failed: ") + ex.what());
+ return (error);
}
- // Lastly, call onConnect. This allows deriving class to execute custom
- // logic predicated by session connect.
- onSessionConnect();
+ return (updateConfig(module_config));
}
+
void
DControllerBase::runProcess() {
LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT, DCTL_RUN_PROCESS).arg(app_name_);
process_->run();
}
-void DControllerBase::disconnectSession() {
- LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT, DCTL_CCSESSION_ENDING)
- .arg(app_name_);
-
- // 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(dctl_logger, DBGLVL_START_SHUT, DCTL_CONFIG_STUB)
- .arg(controller_->getAppName());
- return (isc::config::createAnswer(0, "Configuration accepted."));
-}
-
-isc::data::ConstElementPtr
-DControllerBase::configHandler(isc::data::ConstElementPtr new_config) {
-
- LOG_DEBUG(dctl_logger, DBGLVL_COMMAND, DCTL_CONFIG_UPDATE)
- .arg(controller_->getAppName()).arg(new_config->str());
-
- // 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(dctl_logger, DBGLVL_COMMAND, DCTL_COMMAND_RECEIVED)
- .arg(controller_->getAppName()).arg(command)
- .arg(args ? args->str() : "(no args)");
-
- // Invoke the instance method on the controller singleton.
- return (controller_->executeCommand(command, args));
-}
-
+// Instance method for handling new config
isc::data::ConstElementPtr
DControllerBase::updateConfig(isc::data::ConstElementPtr new_config) {
- isc::data::ConstElementPtr full_config;
- if (stand_alone_) {
- // @todo Until there is a configuration manager to provide retrieval
- // we'll just assume the incoming config is the full configuration set.
- // It may also make more sense to isolate the controller from the
- // configuration manager entirely. We could do something like
- // process_->getFullConfig() here for stand-alone mode?
- full_config = new_config;
- } else {
- if (!config_session_) {
- // That should never happen as we install config_handler
- // after we instantiate the server.
- isc::data::ConstElementPtr answer =
- isc::config::createAnswer(1, "Configuration rejected,"
- " Session has not started.");
- return (answer);
- }
-
- // Let's get the existing configuration.
- full_config = config_session_->getFullConfig();
- }
-
- // The configuration passed to this handler function is partial.
- // In other words, it just includes the values being modified.
- // In the same time, there may be dependencies between various
- // configuration parsers. For example: the option value can
- // be set if the definition of this option is set. If someone removes
- // an existing option definition then the partial configuration that
- // removes that definition is triggered while a relevant option value
- // may remain configured. This eventually results in the
- // configuration being in the inconsistent state.
- // In order to work around this problem we need to merge the new
- // configuration with the existing (full) configuration.
-
- // Let's create a new object that will hold the merged configuration.
- boost::shared_ptr<isc::data::MapElement>
- merged_config(new isc::data::MapElement());
-
- // Merge an existing and new configuration.
- merged_config->setValue(full_config->mapValue());
- isc::data::merge(merged_config, new_config);
-
- // Send the merged configuration to the application.
- return (process_->configure(merged_config));
+ return (process_->configure(new_config));
}
+// Instance method for executing commands
isc::data::ConstElementPtr
DControllerBase::executeCommand(const std::string& command,
isc::data::ConstElementPtr args) {
// as it may be supported there.
isc::data::ConstElementPtr answer;
if (command.compare(SHUT_DOWN_COMMAND) == 0) {
- answer = shutdown(args);
+ answer = shutdownProcess(args);
} else {
// It wasn't shutdown, so may be a custom controller command.
int rcode = 0;
}
isc::data::ConstElementPtr
-DControllerBase::shutdown(isc::data::ConstElementPtr args) {
+DControllerBase::shutdownProcess(isc::data::ConstElementPtr args) {
if (process_) {
return (process_->shutdown(args));
- }
+ }
// Not really a failure, but this condition is worth noting. In reality
// it should be pretty hard to cause this.
std::cerr << "Usage error: " << text << std::endl;
}
- std::cerr << "Usage: " << bin_name_ << std::endl;
- std::cerr << " -v: verbose output" << std::endl;
- std::cerr << " -s: stand-alone mode (don't connect to BIND10)"
- << std::endl;
+ std::cerr << "Usage: " << bin_name_ << std::endl
+ << " -c <config file name> : mandatory,"
+ << " specifies name of configuration file " << std::endl
+ << " -v: optional, verbose output " << std::endl;
+ // add any derivation specific usage
std::cerr << getUsageText() << std::endl;
}
DControllerBase::~DControllerBase() {
}
+std::string
+DControllerBase::getConfigFileName() {
+ return (Daemon::getConfigFile());
+}
+
}; // namespace isc::d2
+
+// Provide an implementation until we figure out a better way to do this.
+void
+dhcp::Daemon::loggerInit(const char* log_name, bool verbose) {
+ isc::log::initLogger(log_name,
+ (verbose ? isc::log::DEBUG : isc::log::INFO),
+ isc::log::MAX_DEBUG_LEVEL, NULL, true);
+}
+
}; // namespace isc
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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
#define D_CONTROLLER_H
#include <cc/data.h>
-#include <cc/session.h>
-#include <config/ccsession.h>
#include <d2/d2_asio.h>
#include <d2/d2_log.h>
#include <d2/d_process.h>
+#include <dhcpsrv/daemon.h>
#include <exceptions/exceptions.h>
#include <log/logger_support.h>
isc::Exception(file, line, what) { };
};
-/// @brief Exception thrown when the session start up fails.
-class SessionStartError: public isc::Exception {
-public:
- SessionStartError (const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) { };
-};
-
/// @brief Exception thrown when the application process encounters an
/// operation in its event loop (i.e. run method).
class ProcessRunError: public isc::Exception {
isc::Exception(file, line, what) { };
};
-/// @brief Exception thrown when the session end fails.
-class SessionEndError: public isc::Exception {
-public:
- SessionEndError (const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) { };
-};
-
-
/// @brief Exception thrown when the controller encounters an operational error.
class DControllerBaseError : public isc::Exception {
public:
class DControllerBase;
typedef boost::shared_ptr<DControllerBase> DControllerBasePtr;
-/// @brief Defines a shared pointer to a Session.
-typedef boost::shared_ptr<isc::cc::Session> SessionPtr;
-
-/// @brief Defines a shared pointer to a ModuleCCSession.
-typedef boost::shared_ptr<isc::config::ModuleCCSession> ModuleCCSessionPtr;
-
-
/// @brief Application Controller
///
/// DControllerBase is an abstract singleton which provides the framework and
/// services for managing an application process that implements the
-/// DProcessBase interface. It allows the process to run either in
-/// integrated mode as a BIND10 module or stand-alone. It coordinates command
-/// line argument parsing, process instantiation and initialization, and runtime
-/// control through external command and configuration event handling.
+/// DProcessBase interface. It runs the process like a stand-alone, command
+/// line driven executable, which must be supplied a configuration file at
+/// startup. It coordinates command line argument parsing, process
+/// instantiation and initialization, and runtime control through external
+/// command and configuration event handling.
/// It creates the IOService instance which is used for runtime control
/// events and passes the IOService into the application process at process
-/// creation. In integrated mode it is responsible for establishing BIND10
-/// session(s) and passes this IOService into the session creation method(s).
-/// It also provides the callback handlers for command and configuration events
-/// received from the external framework (aka BIND10). For example, when
-/// running in integrated mode and a user alters the configuration with the
-/// bindctl tool, BIND10 will emit a configuration message which is sensed by
-/// the controller's IOService. The IOService in turn invokes the configuration
-/// callback, DControllerBase::configHandler(). If the user issues a command
-/// such as shutdown via bindctl, BIND10 will emit a command message, which is
-/// sensed by controller's IOService which invokes the command callback,
-/// DControllerBase::commandHandler().
+/// creation.
+/// It provides the callback handlers for command and configuration events
+/// which could be triggered by an external source. Such sources are intended
+/// to be registed with and monitored by the controller's IOService such that
+/// the appropriate handler can be invoked.
///
/// NOTE: Derivations must supply their own static singleton instance method(s)
/// for creating and fetching the instance. The base class declares the instance
-/// member in order for it to be available for BIND10 callback functions. This
-/// would not be required if BIND10 supported instance method callbacks.
-class DControllerBase : public boost::noncopyable {
+/// member in order for it to be available for static callback functions.
+class DControllerBase : public dhcp::Daemon {
public:
/// @brief Constructor
///
/// @param app_name is display name of the application under control. This
/// name appears in log statements.
- /// @param bin_name is the name of the application executable. Typically
- /// this matches the BIND10 module name.
+ /// @param bin_name is the name of the application executable.
DControllerBase(const char* app_name, const char* bin_name);
/// @brief Destructor
///
/// 1. parse command line arguments
/// 2. instantiate and initialize the application process
- /// 3. establish BIND10 session(s) if in integrated mode
+ /// 3. load the configuration file
/// 4. start and wait on the application process event loop
- /// 5. upon event loop completion, disconnect from BIND10 (if needed)
- /// 6. exit to the caller
+ /// 5. exit to the caller
///
/// It is intended to be called from main() and be given the command line
- /// arguments. Note this method is deliberately not virtual to ensure the
- /// proper sequence of events occur.
+ /// arguments.
///
- /// This function can be run in the test mode. It prevents initialization
+ /// This function can be run in "test mode". It prevents initialization
/// of D2 module logger. This is used in unit tests which initialize logger
- /// in their main function. Such logger uses environmental variables to
- /// control severity, verbosity etc. Reinitialization of logger by this
- /// function would replace unit tests specific logger configuration with
- /// this suitable for D2 running as a bind10 module.
+ /// in their main function. Such a logger uses environmental variables to
+ /// control severity, verbosity etc.
///
/// @param argc is the number of command line arguments supplied
/// @param argv is the array of string (char *) command line arguments
/// InvalidUsage - Indicates invalid command line.
/// ProcessInitError - Failed to create and initialize application
/// process object.
- /// SessionStartError - Could not connect to BIND10 (integrated mode only).
/// ProcessRunError - A fatal error occurred while in the application
/// process event loop.
- /// SessionEndError - Could not disconnect from BIND10 (integrated mode
- /// only).
- void launch(int argc, char* argv[], const bool test_mode);
-
- /// @brief A dummy configuration handler that always returns success.
- ///
- /// This configuration handler does not perform configuration
- /// parsing and always returns success. A dummy handler should
- /// be installed using \ref isc::config::ModuleCCSession ctor
- /// to get the initial configuration. This initial configuration
- /// comprises values for only those elements that were modified
- /// the previous session. The D2 configuration parsing can't be
- /// used to parse the initial configuration because it may need the
- /// full configuration to satisfy dependencies between the
- /// various configuration values. Installing the dummy handler
- /// that guarantees to return success causes initial configuration
- /// to be stored for the session being created and that it can
- /// be later accessed with \ref isc::config::ConfigData::getFullConfig.
- ///
- /// @param new_config new configuration.
- ///
- /// @return success configuration status.
- static isc::data::ConstElementPtr
- dummyConfigHandler(isc::data::ConstElementPtr new_config);
-
- /// @brief A callback for handling all incoming configuration updates.
- ///
- /// As a pointer to this method is used as a callback in ASIO for
- /// ModuleCCSession, it has to be static. It acts as a wrapper around
- /// the virtual instance method, updateConfig.
- ///
- /// @param new_config textual representation of the new configuration
- ///
- /// @return status of the config update
- static isc::data::ConstElementPtr
- configHandler(isc::data::ConstElementPtr new_config);
-
- /// @brief A callback for handling all incoming commands.
- ///
- /// As a pointer to this method is used as a callback in ASIO for
- /// ModuleCCSession, it has to be static. It acts as a wrapper around
- /// the virtual instance method, executeCommand.
- ///
- /// @param command textual representation of the command
- /// @param args parameters of the command. It can be NULL pointer if no
- /// arguments exist for a particular command.
- ///
- /// @return status of the processed command
- static isc::data::ConstElementPtr
- commandHandler(const std::string& command, isc::data::ConstElementPtr args);
+ virtual void launch(int argc, char* argv[], const bool test_mode);
/// @brief Instance method invoked by the configuration event handler and
/// which processes the actual configuration update. Provides behavioral
/// implementation will merge the configuration update into the existing
/// configuration and then invoke the application process' configure method.
///
- /// @todo This implementation is will evolve as the D2 configuration
- /// management task is implemented (trac #2957).
- ///
/// @param new_config is the new configuration
///
/// @return returns an Element that contains the results of configuration
/// update composed of an integer status value (0 means successful,
/// non-zero means failure), and a string explanation of the outcome.
- virtual isc::data::ConstElementPtr
- updateConfig(isc::data::ConstElementPtr new_config);
+ virtual isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr
+ new_config);
+ /// @brief Reconfigures the process from a configuration file
+ ///
+ /// By default the file is assumed to be a JSON text file whose contents
+ /// include at least:
+ ///
+ /// @code
+ /// { "<module-name>": {<module-config>} }
+ ///
+ /// where:
+ /// module-name : is a label which uniquely identifies the
+ /// configuration data for this controller's application
+ ///
+ /// module-config: a set of zero or more JSON elements which comprise
+ /// the application'ss configuration values
+ /// @endcode
+ ///
+ /// The method extracts the set of configuration elements for the
+ /// module-name which matches the controller's app_name_ and passes that
+ /// set into @c udpateConfig().
+ ///
+ /// The file may contain an arbitrary number of other modules.
+ ///
+ /// @return returns an Element that contains the results of configuration
+ /// update composed of an integer status value (0 means successful,
+ /// non-zero means failure), and a string explanation of the outcome.
+ virtual isc::data::ConstElementPtr configFromFile();
/// @brief Instance method invoked by the command event handler and which
/// processes the actual command directive.
/// failure.
/// D2::COMMAND_INVALID - Command is not recognized as valid be either
/// the controller or the application process.
- virtual isc::data::ConstElementPtr
- executeCommand(const std::string& command, isc::data::ConstElementPtr args);
+ virtual isc::data::ConstElementPtr executeCommand(const std::string&
+ command,
+ isc::data::
+ ConstElementPtr args);
+
+ /// @brief A callback for handling all incoming configuration updates.
+ ///
+ /// Provides a static callback that can be used to handle asynchronsouly
+ /// received configurations. It acts as a wrapper around the singleton's
+ /// virtual instance method, updateConfig.
+ ///
+ /// @param new_config textual representation of the new configuration
+ ///
+ /// @return status of the config update
+ static isc::data::ConstElementPtr configHandler(isc::data::ConstElementPtr
+ new_config);
+
+ /// @brief A callback for handling all incoming commands.
+ ///
+ /// Provides a static callback that can be used to handle asynchronsouly
+ /// received commands. It acts as a wrapper around the singleton's virtual
+ /// instance method, executeCommand.
+ ///
+ /// @param command textual representation of the command
+ /// @param args parameters of the command. It can be NULL pointer if no
+ /// arguments exist for a particular command.
+ ///
+ /// @return status of the processed command
+ static isc::data::ConstElementPtr commandHandler(const std::string& command,
+ isc::data::ConstElementPtr
+ args);
+
+ /// @brief Fetches the name of the application under control.
+ ///
+ /// @return returns the controller service name string
+ const std::string getAppName() const {
+ return (app_name_);
+ }
+
+ /// @brief Fetches the name of the application executable.
+ ///
+ /// @return returns the controller logger name string
+ const std::string getBinName() const {
+ return (bin_name_);
+ }
protected:
/// @brief Virtual method that provides derivations the opportunity to
virtual isc::data::ConstElementPtr customControllerCommand(
const std::string& command, isc::data::ConstElementPtr args);
- /// @brief Virtual method which is invoked after the controller successfully
- /// establishes BIND10 connectivity. It provides an opportunity for the
- /// derivation to execute any custom behavior associated with session
- /// establishment.
- ///
- /// Note, it is not called when running stand-alone.
- ///
- /// @throw should throw a DControllerBaseError if it fails.
- virtual void onSessionConnect(){};
-
- /// @brief Virtual method which is invoked as the first action taken when
- /// the controller is terminating the session(s) with BIND10. It provides
- /// an opportunity for the derivation to execute any custom behavior
- /// associated with session termination.
- ///
- /// Note, it is not called when running stand-alone.
- ///
- /// @throw should throw a DControllerBaseError if it fails.
- virtual void onSessionDisconnect(){};
-
/// @brief Virtual method which can be used to contribute derivation
/// specific usage text. It is invoked by the usage() method under
/// invalid usage conditions.
/// @brief Virtual method which returns a string containing the option
/// letters for any custom command line options supported by the derivation.
- /// These are added to the stock options of "s" and "v" during command
+ /// These are added to the stock options of "c" and "v" during command
/// line interpretation.
///
/// @return returns a string containing the custom option letters.
return ("");
}
- /// @brief Fetches the name of the application under control.
- ///
- /// @return returns the controller service name string
- const std::string getAppName() const {
- return (app_name_);
- }
-
- /// @brief Fetches the name of the application executable.
- ///
- /// @return returns the controller logger name string
- const std::string getBinName() const {
- return (bin_name_);
- }
-
- /// @brief Supplies whether or not the controller is in stand alone mode.
- ///
- /// @return returns true if in stand alone mode, false otherwise
- bool isStandAlone() const {
- return (stand_alone_);
- }
-
- /// @brief Method for enabling or disabling stand alone mode.
- ///
- /// @param value is the new value to assign the flag.
- void setStandAlone(bool value) {
- stand_alone_ = value;
- }
-
/// @brief Supplies whether or not verbose logging is enabled.
///
/// @return returns true if verbose logging is enabled.
return (io_service_);
}
- /// @brief Getter for fetching the name of the controller's BIND10 spec
+ /// @brief Getter for fetching the name of the controller's config spec
/// file.
///
/// @return returns the file name string.
return (spec_file_name_);
}
- /// @brief Setter for setting the name of the controller's BIND10 spec file.
+ /// @brief Setter for setting the name of the controller's config spec file.
///
/// @param spec_file_name the file name string.
void setSpecFileName(const std::string& spec_file_name) {
/// instance a second time.
static void setController(const DControllerBasePtr& controller);
-private:
/// @brief Processes the command line arguments. It is the first step
/// taken after the controller has been launched. It combines the stock
/// list of options with those returned by getCustomOpts(), and uses
/// cstdlib's getopt to loop through the command line. The stock options
/// It handles stock options directly, and passes any custom options into
/// the customOption method. Currently there are only two stock options
- /// -s for stand alone mode, and -v for verbose logging.
+ /// -c for specifying the configuration file, and -v for verbose logging.
///
/// @param argc is the number of command line arguments supplied
/// @param argv is the array of string (char *) command line arguments
/// if there is a failure creating or initializing the application process.
void initProcess();
- /// @brief Establishes connectivity with BIND10. This method is used
- /// invoked during launch, if running in integrated mode, following
- /// successful process initialization. It is responsible for establishing
- /// the BIND10 control and config sessions. During the session creation,
- /// it passes in the controller's IOService and the callbacks for command
- /// directives and config events. Lastly, it will invoke the onConnect
- /// method providing the derivation an opportunity to execute any custom
- /// logic associated with session establishment.
- ///
- /// @throw the BIND10 framework may throw std::exceptions.
- void establishSession();
-
/// @brief Invokes the application process's event loop,(DBaseProcess::run).
/// It is called during launch only after successfully completing the
/// requested setup: command line parsing, application initialization,
// @throw throws DControllerBaseError or indirectly DProcessBaseError
void runProcess();
- /// @brief Terminates connectivity with BIND10. This method is invoked
- /// in integrated mode after the application event loop has exited. It
- /// first calls the onDisconnect method providing the derivation an
- /// opportunity to execute custom logic if needed, and then terminates the
- /// BIND10 config and control sessions.
- ///
- /// @throw the BIND10 framework may throw std:exceptions.
- void disconnectSession();
-
/// @brief Initiates shutdown procedure. This method is invoked
/// by executeCommand in response to the shutdown command. It will invoke
/// the application process's shutdown method which causes the process to
/// until the process has halted. Rather it is used to convey the
/// need to shutdown. A successful return indicates that the shutdown
/// has successfully commenced, but does not indicate that the process
- /// has actually exited.
+ /// has actually exited.
///
/// @return returns an Element that contains the results of shutdown
/// command composed of an integer status value (0 means successful,
/// non-zero means failure), and a string explanation of the outcome.
- isc::data::ConstElementPtr shutdown(isc::data::ConstElementPtr args);
+ isc::data::ConstElementPtr shutdownProcess(isc::data::ConstElementPtr args);
+
+ /// @brief Fetches the name of the configuration file
+ ///
+ /// @return the configuration file name as a string
+ virtual std::string getConfigFileName();
+
+ /// @brief Fetches the current process
+ ///
+ /// @return the a pointer to the current process instance.
+ DProcessBasePtr getProcess() {
+ return (process_);
+ }
/// @brief Prints the program usage text to std error.
///
void usage(const std::string& text);
private:
- /// @brief Display name of the service under control. This name
- /// appears in log statements.
+ /// @brief Name of the service under control.
+ /// This name is used as the configuration module name and appears in log
+ /// statements.
std::string app_name_;
- /// @brief Name of the service executable. By convention this matches
- /// the BIND10 module name. It is also used to establish the logger
- /// name.
+ /// @brief Name of the service executable.
+ /// By convention this matches the executable nam. It is also used to
+ /// establish the logger name.
std::string bin_name_;
- /// @brief Indicates if the controller stand alone mode is enabled. When
- /// enabled, the controller will not establish connectivity with BIND10.
- bool stand_alone_;
-
/// @brief Indicates if the verbose logging mode is enabled.
bool verbose_;
- /// @brief The absolute file name of the BIND10 spec file.
+ /// @brief The absolute file name of the JSON spec file.
std::string spec_file_name_;
/// @brief Pointer to the instance of the process.
/// @brief Shared pointer to an IOService object, used for ASIO operations.
IOServicePtr io_service_;
- /// @brief Helper session object that represents raw connection to msgq.
- SessionPtr cc_session_;
-
- /// @brief Session that receives configuration and commands.
- ModuleCCSessionPtr config_session_;
-
/// @brief Singleton instance value.
static DControllerBasePtr controller_;
d2_unittests_SOURCES = ../d2_asio.h
d2_unittests_SOURCES += ../d2_log.h ../d2_log.cc
d2_unittests_SOURCES += ../d_process.h
-d2_unittests_SOURCES += ../d_controller.cc ../d2_controller.h
-d2_unittests_SOURCES += ../d2_process.cc ../d2_process.h
-d2_unittests_SOURCES += ../d2_controller.cc ../d2_controller.h
+d2_unittests_SOURCES += ../d_controller.cc ../d_controller.h
d2_unittests_SOURCES += ../d_cfg_mgr.cc ../d_cfg_mgr.h
+d2_unittests_SOURCES += ../d2_process.cc ../d2_process.h
d2_unittests_SOURCES += ../d2_config.cc ../d2_config.h
d2_unittests_SOURCES += ../d2_cfg_mgr.cc ../d2_cfg_mgr.h
d2_unittests_SOURCES += ../d2_queue_mgr.cc ../d2_queue_mgr.h
d2_unittests_SOURCES += d_test_stubs.cc d_test_stubs.h
d2_unittests_SOURCES += d2_unittests.cc
d2_unittests_SOURCES += d2_process_unittests.cc
-d2_unittests_SOURCES += d_controller_unittests.cc
-d2_unittests_SOURCES += d2_controller_unittests.cc
d2_unittests_SOURCES += d_cfg_mgr_unittests.cc
d2_unittests_SOURCES += d2_cfg_mgr_unittests.cc
d2_unittests_SOURCES += d2_queue_mgr_unittests.cc
d2_unittests_SOURCES += state_model_unittests.cc
nodist_d2_unittests_SOURCES = ../d2_messages.h ../d2_messages.cc
+if CONFIG_BACKEND_BUNDY
+d2_unittests_SOURCES += ../bundy_d2_controller.cc ../bundy_d2_controller.h
+d2_unittests_SOURCES += bundy_d2_controller_unittests.cc
+else
+if CONFIG_BACKEND_JSON
+d2_unittests_SOURCES += ../d2_controller.cc ../d2_controller.h
+d2_unittests_SOURCES += d2_controller_unittests.cc
+d2_unittests_SOURCES += d_controller_unittests.cc
+endif
+endif
+
d2_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
d2_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
d2_unittests_LDADD = $(GTEST_LDADD)
--- /dev/null
+// Copyright (C) 2014 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 <config/ccsession.h>
+#include <d_test_stubs.h>
+#include <d2/bundy_d2_controller.h>
+#include <d2/spec_config.h>
+
+#include <boost/pointer_cast.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <gtest/gtest.h>
+
+#include <config.h>
+#include <sstream>
+
+using namespace boost::posix_time;
+
+namespace isc {
+namespace d2 {
+
+/// @brief Test fixture class for testing D2Controller class. This class
+/// derives from DControllerTest and wraps a D2Controller. Much of the
+/// underlying functionality is in the DControllerBase class which has an
+/// extensive set of unit tests that are independent of DHCP-DDNS.
+/// @TODO Currently These tests are relatively light and duplicate some of
+/// the testing done on the base class. These tests are sufficient to ensure
+/// that D2Controller properly derives from its base class and to test the
+/// logic that is unique to D2Controller. These tests will be augmented and
+/// or new tests added as additional functionality evolves.
+/// Unlike the stub testing, there is no use of SimFailure to induce error
+/// conditions as this is production code.
+class BundyD2ControllerTest : public DControllerTest {
+public:
+ /// @brief Constructor
+ /// Note the constructor passes in the static D2Controller instance
+ /// method.
+ BundyD2ControllerTest() : DControllerTest(D2Controller::instance) {
+ }
+
+ /// @brief Destructor
+ ~BundyD2ControllerTest() {
+ }
+};
+
+/// @brief Basic Controller instantiation testing.
+/// Verifies that the controller singleton gets created and that the
+/// basic derivation from the base class is intact.
+TEST_F(BundyD2ControllerTest, basicInstanceTesting) {
+ // Verify the we can the singleton instance can be fetched and that
+ // it is the correct type.
+ DControllerBasePtr& controller = DControllerTest::getController();
+ ASSERT_TRUE(controller);
+ ASSERT_NO_THROW(boost::dynamic_pointer_cast<D2Controller>(controller));
+
+ // Verify that controller's app name is correct.
+ EXPECT_TRUE(checkAppName(D2Controller::d2_app_name_));
+
+ // Verify that controller's bin name is correct.
+ EXPECT_TRUE(checkBinName(D2Controller::d2_bin_name_));
+
+ // Verify that controller's spec file name is correct.
+ EXPECT_TRUE(checkSpecFileName(D2_SPECFILE_LOCATION));
+
+ // Verify that controller's IOService exists.
+ EXPECT_TRUE(checkIOService());
+
+ // Verify that the Process does NOT exist.
+ EXPECT_FALSE(checkProcess());
+}
+
+/// @brief Tests basic command line processing.
+/// Verifies that:
+/// 1. Standard command line options are supported.
+/// 2. Invalid options are detected.
+TEST_F(BundyD2ControllerTest, commandLineArgs) {
+ char* argv[] = { const_cast<char*>("progName"),
+ const_cast<char*>("-v") };
+ int argc = 2;
+
+ // Verify that verbose flag is false initially.
+ EXPECT_TRUE(checkVerbose(false));
+
+ // Verify that standard options can be parsed without error.
+ EXPECT_NO_THROW(parseArgs(argc, argv));
+
+ // Verify that verbose flag is now true.
+ EXPECT_TRUE(checkVerbose(true));
+
+ // Verify that an unknown option is detected.
+ char* argv2[] = { const_cast<char*>("progName"),
+ const_cast<char*>("-x") };
+ argc = 2;
+ EXPECT_THROW(parseArgs(argc, argv2), InvalidUsage);
+}
+
+/// @brief Tests application process creation and initialization.
+/// Verifies that the process can be successfully created and initialized.
+TEST_F(BundyD2ControllerTest, initProcessTesting) {
+ ASSERT_NO_THROW(initProcess());
+ EXPECT_TRUE(checkProcess());
+}
+
+/// @brief Configuration update event testing.
+/// This really tests just the ability of the handlers to invoke the necessary
+/// chain of methods and handle error conditions. Configuration parsing and
+/// retrieval should be tested as part of the d2 configuration management
+/// implementation. Note that this testing calls the configuration update event
+/// callback, configHandler, directly.
+/// This test verifies that:
+/// 1. Configuration will be rejected in integrated mode when there is no
+/// session established. (This is a very contrived situation).
+/// 2. In stand-alone mode a configuration update results in successful
+/// status return.
+/// 3. That an application process error in configuration updating is handled
+/// properly.
+TEST_F(BundyD2ControllerTest, configUpdateTests) {
+ int rcode = -1;
+ isc::data::ConstElementPtr answer;
+
+ // Initialize the application process.
+ ASSERT_NO_THROW(initProcess());
+ EXPECT_TRUE(checkProcess());
+
+ // Create a configuration set using a small, valid D2 configuration.
+ isc::data::ElementPtr config_set =
+ isc::data::Element::fromJSON(valid_d2_config);
+
+ // Configuration should be rejected as there is no session. This is a
+ // pretty contrived situation that shouldn't be possible other than the
+ // handler being called directly (like this does).
+ answer = DControllerBase::configHandler(config_set);
+ isc::config::parseAnswer(rcode, answer);
+ EXPECT_EQ(1, rcode);
+}
+
+/// @brief Command execution tests.
+/// This really tests just the ability of the handler to invoke the necessary
+/// chain of methods and to handle error conditions. Note that this testing
+/// calls the command callback, commandHandler, directly.
+/// This test verifies that:
+/// 1. That an unrecognized command is detected and returns a status of
+/// d2::COMMAND_INVALID.
+/// 2. Shutdown command is recognized and returns a d2::COMMAND_SUCCESS status.
+TEST_F(BundyD2ControllerTest, executeCommandTests) {
+ int rcode = -1;
+ isc::data::ConstElementPtr answer;
+ isc::data::ElementPtr arg_set;
+
+ // Initialize the application process.
+ ASSERT_NO_THROW(initProcess());
+ EXPECT_TRUE(checkProcess());
+
+ // Verify that an unknown command returns an COMMAND_INVALID response.
+ std::string bogus_command("bogus");
+ answer = DControllerBase::commandHandler(bogus_command, arg_set);
+ isc::config::parseAnswer(rcode, answer);
+ EXPECT_EQ(COMMAND_INVALID, rcode);
+
+ // Verify that shutdown command returns COMMAND_SUCCESS response.
+ //answer = executeCommand(SHUT_DOWN_COMMAND, isc::data::ElementPtr());
+ answer = DControllerBase::commandHandler(SHUT_DOWN_COMMAND, arg_set);
+ isc::config::parseAnswer(rcode, answer);
+ EXPECT_EQ(COMMAND_SUCCESS, rcode);
+}
+
+/// @brief Tests launch with a session establishment failure.
+/// This test launches with a valid command line for integrated mode and no.
+/// Attempting to connect to BIND10 should fail, even if BIND10 is running
+/// UNLESS the test is run as root. Launch should throw SessionStartError.
+TEST_F(BundyD2ControllerTest, launchSessionFailure) {
+ // Command line to run integrated
+ char* argv[] = { (char*)"progName" };
+ int argc = 1;
+
+ // Launch the controller in integrated mode.
+ EXPECT_THROW(launch(argc, argv), SessionStartError);
+}
+
+}; // end of isc::d2 namespace
+}; // end of isc namespace
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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
/// 2. Invalid options are detected.
TEST_F(D2ControllerTest, commandLineArgs) {
char* argv[] = { const_cast<char*>("progName"),
- const_cast<char*>("-s"),
+ const_cast<char*>("-c"),
+ const_cast<char*>(DControllerTest::CFG_TEST_FILE),
const_cast<char*>("-v") };
- int argc = 3;
+ int argc = 4;
- // Verify that both flags are false initially.
- EXPECT_TRUE(checkStandAlone(false));
+ // Verify that verbose flag is false initially.
EXPECT_TRUE(checkVerbose(false));
// Verify that standard options can be parsed without error.
EXPECT_NO_THROW(parseArgs(argc, argv));
- // Verify that flags are now true.
- EXPECT_TRUE(checkStandAlone(true));
+ // Verify that verbose flag is true.
EXPECT_TRUE(checkVerbose(true));
+ // Verify configuration file name is correct.
+ EXPECT_TRUE(checkConfigFileName(DControllerTest::CFG_TEST_FILE));
+
// Verify that an unknown option is detected.
char* argv2[] = { const_cast<char*>("progName"),
const_cast<char*>("-x") };
TEST_F(D2ControllerTest, launchNormalShutdown) {
// command line to run standalone
char* argv[] = { const_cast<char*>("progName"),
- const_cast<char*>("-s"),
+ const_cast<char*>("-c"),
+ const_cast<char*>(DControllerTest::CFG_TEST_FILE),
const_cast<char*>("-v") };
- int argc = 3;
+ int argc = 4;
+
+ // Create a valid D2 configuration file.
+ writeFile(valid_d2_config);
// Use an asiolink IntervalTimer and callback to generate the
// shutdown invocation. (Note IntervalTimer setup is in milliseconds).
/// implementation. Note that this testing calls the configuration update event
/// callback, configHandler, directly.
/// This test verifies that:
-/// 1. Configuration will be rejected in integrated mode when there is no
-/// session established. (This is a very contrived situation).
-/// 2. In stand-alone mode a configuration update results in successful
-/// status return.
-/// 3. That an application process error in configuration updating is handled
+/// 1. A valid configuration yields a successful parse result.
+/// 2. That an application process error in configuration updating is handled
/// properly.
TEST_F(D2ControllerTest, configUpdateTests) {
int rcode = -1;
isc::data::ElementPtr config_set =
isc::data::Element::fromJSON(valid_d2_config);
- // We are not stand-alone, so configuration should be rejected as there is
- // no session. This is a pretty contrived situation that shouldn't be
- // possible other than the handler being called directly (like this does).
- answer = DControllerBase::configHandler(config_set);
- isc::config::parseAnswer(rcode, answer);
- EXPECT_EQ(1, rcode);
-
- // Verify that in stand alone we get a successful update result.
- setStandAlone(true);
+ // Verify that given a valid config we get a successful update result.
answer = DControllerBase::configHandler(config_set);
isc::config::parseAnswer(rcode, answer);
EXPECT_EQ(0, rcode);
/// @brief Command execution tests.
/// This really tests just the ability of the handler to invoke the necessary
/// chain of methods and to handle error conditions. Note that this testing
-/// calls the command callback, commandHandler, directly.
+/// calls the executeCommand method directly.
/// This test verifies that:
/// 1. That an unrecognized command is detected and returns a status of
/// d2::COMMAND_INVALID.
-# Copyright (C) 2013 Internet Systems Consortium.
+# Copyright (C) 2013-2014 Internet Systems Consortium.
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
print("Note: Simple test to verify that D2 server can be started.")
# note that "-s" for stand alone is necessary in order to flush the log output
# soon enough to catch it.
- (returncode, output, error) = self.runCommand(["../b10-dhcp-ddns",
- "-s", "-v"])
+ (returncode, output, error) = self.runCommand(["../b10-dhcp-ddns", "-v"])
output_text = str(output) + str(error)
self.assertEqual(output_text.count("DCTL_STARTING"), 1)
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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
/// 4. Extraneous command line information is detected.
TEST_F(DStubControllerTest, commandLineArgs) {
- // Verify that both flags are false initially.
- EXPECT_TRUE(checkStandAlone(false));
+ // Verify that verbose flag is false initially.
EXPECT_TRUE(checkVerbose(false));
// Verify that standard options can be parsed without error.
char* argv[] = { const_cast<char*>("progName"),
- const_cast<char*>("-s"),
+ const_cast<char*>("-c"),
+ const_cast<char*>("cfgName"),
const_cast<char*>("-v") };
- int argc = 3;
+ int argc = 4;
EXPECT_NO_THROW(parseArgs(argc, argv));
- // Verify that flags are now true.
- EXPECT_TRUE(checkStandAlone(true));
+ // Verify that verbose is true.
EXPECT_TRUE(checkVerbose(true));
+ // Verify configuration file name is correct
+ EXPECT_TRUE(checkConfigFileName("cfgName"));
+
// Verify that the custom command line option is parsed without error.
char xopt[3] = "- ";
xopt[1] = *DStubController::stub_option_x_;
TEST_F(DStubControllerTest, launchProcessInitError) {
// Command line to run integrated
char* argv[] = { const_cast<char*>("progName"),
- const_cast<char*>("-s"),
+ const_cast<char*>("-c"),
+ const_cast<char*>(DControllerTest::CFG_TEST_FILE),
const_cast<char*>("-v") };
- int argc = 3;
+ int argc = 4;
// Launch the controller in stand alone mode.
SimFailure::set(SimFailure::ftCreateProcessException);
/// @brief Tests launch and normal shutdown (stand alone mode).
/// This creates an interval timer to generate a normal shutdown and then
-/// launches with a valid, stand-alone command line and no simulated errors.
+/// launches with a valid, command line, with a valid configuration file
+/// and no simulated errors.
TEST_F(DStubControllerTest, launchNormalShutdown) {
// command line to run standalone
char* argv[] = { const_cast<char*>("progName"),
- const_cast<char*>("-s"),
+ const_cast<char*>("-c"),
+ const_cast<char*>(DControllerTest::CFG_TEST_FILE),
const_cast<char*>("-v") };
- int argc = 3;
+ int argc = 4;
+
+ // Create a non-empty, config file. writeFile will wrap the contents
+ // with the module name for us.
+ writeFile("{}");
// Use an asiolink IntervalTimer and callback to generate the
// shutdown invocation. (Note IntervalTimer setup is in milliseconds).
elapsed.total_milliseconds() <= 2200);
}
+/// @brief Tests launch with an nonexistant configuration file.
+TEST_F(DStubControllerTest, nonexistantConfigFile) {
+ // command line to run standalone
+ char* argv[] = { const_cast<char*>("progName"),
+ const_cast<char*>("-c"),
+ const_cast<char*>("bogus-file"),
+ const_cast<char*>("-v") };
+ int argc = 4;
+
+ // Record start time, and invoke launch().
+ EXPECT_THROW(launch(argc, argv), ProcessInitError);
+}
+
+/// @brief Tests launch with configuration file argument but no file name
+TEST_F(DStubControllerTest, missingConfigFileName) {
+ // command line to run standalone
+ char* argv[] = { const_cast<char*>("progName"),
+ const_cast<char*>("-c"),
+ const_cast<char*>("-v") };
+ int argc = 3;
+
+ // Record start time, and invoke launch().
+ EXPECT_THROW(launch(argc, argv), ProcessInitError);
+}
+
+/// @brief Tests launch with no configuration file argument
+TEST_F(DStubControllerTest, missingConfigFileArgument) {
+ // command line to run standalone
+ char* argv[] = { const_cast<char*>("progName"),
+ const_cast<char*>("-v") };
+ int argc = 2;
+
+ // Record start time, and invoke launch().
+ EXPECT_THROW(launch(argc, argv), ProcessInitError);
+}
+
/// @brief Tests launch with an operational error during application execution.
/// This test creates an interval timer to generate a runtime exception during
/// the process event loop. It launches wih a valid, stand-alone command line
TEST_F(DStubControllerTest, launchRuntimeError) {
// command line to run standalone
char* argv[] = { const_cast<char*>("progName"),
- const_cast<char*>("-s"),
+ const_cast<char*>("-c"),
+ const_cast<char*>(DControllerTest::CFG_TEST_FILE),
const_cast<char*>("-v") };
- int argc = 3;
+ int argc = 4;
+
+ // Create a non-empty, config file. writeFile will wrap the contents
+ // with the module name for us.
+ writeFile("{}");
// Use an asiolink IntervalTimer and callback to generate the
// shutdown invocation. (Note IntervalTimer setup is in milliseconds).
elapsed.total_milliseconds() <= 2200);
}
-/// @brief Tests launch with a session establishment failure.
-/// This test launches with a valid command line for integrated mode and no.
-/// Attempting to connect to BIND10 should fail, even if BIND10 is running
-/// UNLESS the test is run as root. Launch should throw SessionStartError.
-TEST_F(DStubControllerTest, launchSessionFailure) {
- // Command line to run integrated
- char* argv[] = { (char*)"progName" };
- int argc = 1;
-
- // Launch the controller in integrated mode.
- EXPECT_THROW(launch(argc, argv), SessionStartError);
-}
-
/// @brief Configuration update event testing.
/// This really tests just the ability of the handlers to invoke the necessary
/// chain of methods and handle error conditions. Configuration parsing and
/// implementation. Note that this testing calls the configuration update event
/// callback, configHandler, directly.
/// This test verifies that:
-/// 1. Configuration will be rejected in integrated mode when there is no
-/// session established. (This is a very contrived situation).
-/// 2. In stand-alone mode a configuration update results in successful
-/// status return.
-/// 3. That an application process error in configuration updating is handled
+/// 1. That a valid configuration update results in successful status return.
+/// 2. That an application process error in configuration updating is handled
/// properly.
TEST_F(DStubControllerTest, configUpdateTests) {
int rcode = -1;
std::string config = "{ \"test-value\": 1000 } ";
isc::data::ElementPtr config_set = isc::data::Element::fromJSON(config);
- // We are not stand-alone, so configuration should be rejected as there is
- // no session. This is a pretty contrived situation that shouldn't be
- // possible other than the handler being called directly (like this does).
- answer = DControllerBase::configHandler(config_set);
- isc::config::parseAnswer(rcode, answer);
- EXPECT_EQ(1, rcode);
-
- // Verify that in stand alone we get a successful update result.
- setStandAlone(true);
+ // Verify that a valid config gets a successful update result.
answer = DControllerBase::configHandler(config_set);
isc::config::parseAnswer(rcode, answer);
EXPECT_EQ(0, rcode);
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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
: DProcessBase(name, io_service, DCfgMgrBasePtr(new DStubCfgMgr())) {
};
+
void
DStubProcess::init() {
if (SimFailure::shouldFailOn(SimFailure::ftProcessInit)) {
/// @brief Defines the bin name used to construct the controller
const char* DStubController::stub_bin_name_ = "TestBin";
+
DControllerBasePtr&
DStubController::instance() {
// If the singleton hasn't been created, do it now.
// Initialize controller wrapper's static instance getter member.
DControllerTest::InstanceGetter DControllerTest::instanceGetter_ = NULL;
+/// @brief Defines the name of the configuration file to use
+const char* DControllerTest::CFG_TEST_FILE = "d2-test-config.json";
+
//************************** ObjectParser *************************
ObjectParser::ObjectParser(const std::string& param_name,
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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
#include <gtest/gtest.h>
+#include <fstream>
+#include <iostream>
+#include <sstream>
+
namespace isc {
namespace d2 {
/// a clean start between tests.
virtual ~DControllerTest() {
getController().reset();
+ static_cast<void>(unlink(CFG_TEST_FILE));
}
/// @brief Convenience method that destructs and then recreates the
return (getController()->io_service_);
}
- /// @brief Compares stand alone flag with the given value.
+ /// @brief Compares verbose flag with the given value.
///
/// @param value
///
- /// @return returns true if the stand alone flag is equal to the given
- /// value.
- bool checkStandAlone(bool value) {
- return (getController()->isStandAlone() == value);
- }
-
- /// @brief Sets the controller's stand alone flag to the given value.
- ///
- /// @param value is the new value to assign.
- ///
- void setStandAlone(bool value) {
- getController()->setStandAlone(value);
+ /// @return returns true if the verbose flag is equal to the given value.
+ bool checkVerbose(bool value) {
+ return (getController()->isVerbose() == value);
}
- /// @brief Compares verbose flag with the given value.
+ /// @brief Compares configuration file name with the given value.
///
- /// @param value
+ /// @param value file name to compare against
///
/// @return returns true if the verbose flag is equal to the given value.
- bool checkVerbose(bool value) {
- return (getController()->isVerbose() == value);
+ bool checkConfigFileName(const std::string& value) {
+ return (getController()->getConfigFileName() == value);
}
/// @Wrapper to invoke the Controller's parseArgs method. Please refer to
getController()->initProcess();
}
- /// @Wrapper to invoke the Controller's establishSession method. Please
- /// refer to DControllerBase::establishSession for details.
- void establishSession() {
- getController()->establishSession();
- }
-
/// @Wrapper to invoke the Controller's launch method. Please refer to
/// DControllerBase::launch for details.
void launch(int argc, char* argv[]) {
getController()->launch(argc, argv, true);
}
- /// @Wrapper to invoke the Controller's disconnectSession method. Please
- /// refer to DControllerBase::disconnectSession for details.
- void disconnectSession() {
- getController()->disconnectSession();
- }
-
/// @Wrapper to invoke the Controller's updateConfig method. Please
/// refer to DControllerBase::updateConfig for details.
isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr
static void genFatalErrorCallback() {
isc_throw (DProcessBaseError, "simulated fatal error");
}
+
+ /// @brief writes specified content to a well known file
+ ///
+ /// Writes given JSON content to CFG_TEST_FILE. It will wrap the
+ /// content within a JSON element whose name is equal to the controller's
+ /// app name or the given module name if not blank:
+ ///
+ /// @code
+ /// { "<app_name>" : <content> }
+ /// @endcod
+ ///
+ /// suffix the content within a JSON element with the given module
+ /// name or wrapped by a JSON
+ /// element . Tests will
+ /// attempt to read that file.
+ ///
+ /// @param content JSON text to be written to file
+ /// @param module_name content content to be written to file
+ void writeFile(const std::string& content,
+ const std::string module_name = "") {
+ std::ofstream out(CFG_TEST_FILE, std::ios::trunc);
+ ASSERT_TRUE(out.is_open());
+
+ out << "{ \"" << (!module_name.empty() ? module_name :
+ getController()->getAppName())
+ << "\": " << std::endl;
+
+ out << content;
+ out << " } " << std::endl;
+ out.close();
+ }
+
+ /// Name of a config file used during tests
+ static const char* CFG_TEST_FILE;
};
/// @brief a collection of elements that store uint32 values