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

[master] Merge branch 2955 which adds D2Process and DProcessBase

 to bin/src/D2.
parents 72b428b6 ece829ff
......@@ -48,12 +48,17 @@ 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
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/asiolink/libb10-asiolink.la
b10_d2_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
b10_d2_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
b10_d2dir = $(pkgdatadir)
b10_d2_DATA = d2.spec
......@@ -14,15 +14,35 @@
$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.
// 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 <config/ccsession.h>
#include <d2/d2_log.h>
#include <d2/d2_process.h>
using namespace asio;
namespace isc {
namespace d2 {
D2Process::D2Process(const char* name, IOServicePtr io_service)
: DProcessBase(name, io_service) {
};
void
D2Process::init() {
};
int
D2Process::run() {
// Until shut down or an fatal error occurs, wait for and
// execute a single callback. This is a preliminary implementation
// that is likely to evolve as development progresses.
// To use run(), the "managing" layer must issue an io_service::stop
// or the call to run will continue to block, and shutdown will not
// occur.
LOG_DEBUG(d2_logger, DBGLVL_START_SHUT, D2PRC_RUN_ENTER);
IOServicePtr& io_service = getIoService();
while (!shouldShutdown()) {
try {
io_service->run_one();
} catch (const std::exception& ex) {
LOG_FATAL(d2_logger, D2PRC_FAILED).arg(ex.what());
return (EXIT_FAILURE);
}
}
LOG_DEBUG(d2_logger, DBGLVL_START_SHUT, D2PRC_RUN_EXIT);
return (EXIT_SUCCESS);
};
int
D2Process::shutdown() {
LOG_DEBUG(d2_logger, DBGLVL_START_SHUT, D2PRC_SHUTDOWN);
setShutdownFlag(true);
return (0);
}
isc::data::ConstElementPtr
D2Process::configure(isc::data::ConstElementPtr config_set) {
// @TODO This is the initial implementation which simply accepts
// any content in config_set as valid. This is sufficient to
// allow participation as a BIND10 module, while D2 configuration support
// is being developed.
LOG_DEBUG(d2_logger, DBGLVL_TRACE_BASIC,
D2PRC_CONFIGURE).arg(config_set->str());
return (isc::config::createAnswer(0, "Configuration accepted."));
}
isc::data::ConstElementPtr
D2Process::command(const std::string& command, isc::data::ConstElementPtr args){
// @TODO This is the initial implementation. If and when D2 is extended
// to support its own commands, this implementation must change. Otherwise
// it should reject all commands as it does now.
LOG_DEBUG(d2_logger, DBGLVL_TRACE_BASIC,
D2PRC_COMMAND).arg(command).arg(args->str());
return (isc::config::createAnswer(1, "Unrecognized command:" + command));
}
D2Process::~D2Process() {
};
}; // namespace isc::d2
}; // 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_PROCESS_H
#define D2_PROCESS_H
#include <d2/d_process.h>
namespace isc {
namespace d2 {
/// @brief @TODO DHCP-DDNS Application Process
///
/// D2Process provides the top level application logic for DHCP-driven DDNS
/// update processing. It provides the asynchronous event processing required
/// to receive DNS mapping change requests and carry them out.
/// It implements the DProcessBase interface, which structures it such that it
/// is a managed "application", controlled by a management layer.
class D2Process : public DProcessBase {
public:
/// @brief Constructor
///
/// @param name name is a text label for the process. Generally used
/// in log statements, but otherwise arbitrary.
/// @param io_service is the io_service used by the caller for
/// asynchronous event handling.
///
/// @throw DProcessBaseError is io_service is NULL.
D2Process(const char* name, IOServicePtr io_service);
/// @brief Will be used after instantiation to perform initialization
/// unique to D2. This will likely include interactions with QueueMgr and
/// UpdateMgr, to prepare for request receipt and processing.
virtual void init();
/// @brief Implements the process's event loop.
/// The initial implementation is quite basic, surrounding calls to
/// io_service->runOne() with a test of the shutdown flag.
/// Once invoked, the method will continue until the process itself is
/// exiting due to a request to shutdown or some anomaly forces an exit.
/// @return returns 0 upon a successful, "normal" termination, non
/// zero to indicate an abnormal termination.
virtual int run();
// @TODO need brief
virtual int shutdown();
// @TODO need brief
/// @brief Processes the given configuration.
///
/// This method may be called multiple times during the process lifetime.
/// Certainly once during process startup, and possibly later if the user
/// alters configuration. This method must not throw, it should catch any
/// processing errors and return a success or failure answer as described
/// below.
///
/// @param config_set a new configuration (JSON) for the process
/// @return an Element that contains the results of configuration composed
/// of an integer status value (0 means successful, non-zero means failure),
/// and a string explanation of the outcome.
virtual isc::data::ConstElementPtr configure(isc::data::ConstElementPtr
config_set);
// @TODO need brief
/// @brief Processes the given command.
///
/// This method is called to execute any custom commands supported by the
/// process. This method must not throw, it should catch any processing
/// errors and return a success or failure answer as described below.
///
/// @param command is a string label representing the command to execute.
/// @param args is a set of arguments (if any) required for the given
/// command.
/// @return an Element that contains the results of command composed
/// of an integer status value (0 means successful, non-zero means failure),
/// and a string explanation of the outcome.
virtual isc::data::ConstElementPtr command(const std::string& command,
isc::data::ConstElementPtr args);
// @TODO need brief
virtual ~D2Process();
};
}; // namespace isc::d2
}; // namespace isc
#endif
// 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 D_PROCESS_H
#define D_PROCESS_H
#include <asiolink/asiolink.h>
#include <cc/data.h>
#include <boost/shared_ptr.hpp>
#include <exceptions/exceptions.h>
typedef boost::shared_ptr<isc::asiolink::IOService> IOServicePtr;
namespace isc {
namespace d2 {
/// @brief Exception thrown if the process encountered an operational error.
class DProcessBaseError : public isc::Exception {
public:
DProcessBaseError(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
/// @brief Application Process Interface
///
/// DProcessBase is an abstract class represents the primary "application"
/// level object in a "managed" asynchronous application. It provides a uniform
/// interface such that a managing layer can construct, intialize, and start
/// the application's event loop. The event processing is centered around the
/// use of isc::asiolink::io_service. The io_service is shared between the
/// the managing layer and the DProcessBase. This allows management layer IO
/// such as directives to be sensed and handled, as well as processing IO
/// activity specific to the application. In terms of management layer IO,
/// there are methods shutdown, configuration updates, and commands unique
/// to the application.
class DProcessBase {
public:
/// @brief Constructor
///
/// @param name name is a text label for the process. Generally used
/// in log statements, but otherwise arbitrary.
/// @param io_service is the io_service used by the caller for
/// asynchronous event handling.
///
/// @throw DProcessBaseError is io_service is NULL.
DProcessBase(const char* name, IOServicePtr io_service) : name_(name),
io_service_(io_service), shut_down_flag_(false) {
if (!io_service_) {
isc_throw (DProcessBaseError, "IO Service cannot be null");
}
};
/// @brief May be used after instantiation to perform initialization unique
/// to application. It must be invoked prior to invoking run. This would
/// likely include the creation of additional IO sources and their
/// integration into the io_service.
virtual void init() = 0;
/// @brief Implements the process's event loop. In its simplest form it
/// would an invocation io_service_->run(). This method should not exit
/// until the process itself is exiting due to a request to shutdown or
/// some anomaly is forcing an exit.
/// @return returns EXIT_SUCCESS upon a successful, normal termination,
/// and EXIT_FAILURE to indicate an abnormal termination.
virtual int run() = 0;
/// @brief Implements the process's shutdown processing. When invoked, it
/// should ensure that the process gracefully exits the run method.
virtual int shutdown() = 0;
/// @brief Processes the given configuration.
///
/// This method may be called multiple times during the process lifetime.
/// Certainly once during process startup, and possibly later if the user
/// alters configuration. This method must not throw, it should catch any
/// processing errors and return a success or failure answer as described
/// below.
///
/// @param config_set a new configuration (JSON) for the process
/// @return an Element that contains the results of configuration composed
/// of an integer status value (0 means successful, non-zero means failure),
/// and a string explanation of the outcome.
virtual isc::data::ConstElementPtr configure(isc::data::ConstElementPtr
config_set) = 0;
/// @brief Processes the given command.
///
/// This method is called to execute any custom commands supported by the
/// process. This method must not throw, it should catch any processing
/// errors and return a success or failure answer as described below.
///
/// @param command is a string label representing the command to execute.
/// @param args is a set of arguments (if any) required for the given
/// command.
/// @return an Element that contains the results of command composed
/// of an integer status value (0 means successful, non-zero means failure),
/// and a string explanation of the outcome.
virtual isc::data::ConstElementPtr command(
const std::string& command, isc::data::ConstElementPtr args) = 0;
/// @brief Destructor
virtual ~DProcessBase(){};
bool shouldShutdown() {
return (shut_down_flag_);
}
void setShutdownFlag(bool value) {
shut_down_flag_ = value;
}
const std::string& getName() const {
return (name_);
}
IOServicePtr& getIoService() {
return (io_service_);
}
private:
/// @brief Text label for the process. Generally used in log statements,
/// but otherwise can be arbitrary.
std::string name_;
/// @brief The IOService to be used for asynchronous event handling.
IOServicePtr io_service_;
/// @brief Boolean flag set when shutdown has been requested.
bool shut_down_flag_;
};
/// @brief Defines a shared pointer to DProcessBase.
typedef boost::shared_ptr<DProcessBase> DProcessBasePtr;
}; // namespace isc::d2
}; // namespace isc
#endif
......@@ -82,16 +82,14 @@ main(int argc, char* argv[]) {
((verbose_mode && stand_alone)
? isc::log::DEBUG : isc::log::INFO),
isc::log::MAX_DEBUG_LEVEL, NULL, !stand_alone);
LOG_INFO(d2_logger, D2_STARTING);
LOG_DEBUG(d2_logger, DBGLVL_START_SHUT, D2_START_INFO)
.arg(getpid()).arg(verbose_mode ? "yes" : "no")
.arg(stand_alone ? "yes" : "no" );
LOG_INFO(d2_logger, D2CTL_STARTING);
// For now we will sleep awhile to simulate doing something.
// Without at least a sleep, the process will start, exit and be
// restarted by Bind10/Init endlessley in a rapid succession.
sleep(1000);
LOG_INFO(d2_logger, D2_SHUTDOWN);
LOG_INFO(d2_logger, D2CTL_STOPPING);
return (EXIT_SUCCESS);
}
......@@ -52,7 +52,10 @@ if HAVE_GTEST
TESTS += d2_unittests
d2_unittests_SOURCES = ../d2_log.h ../d2_log.cc
d2_unittests_SOURCES += ../d_process.h
d2_unittests_SOURCES += ../d2_process.cc ../d2_process.h
d2_unittests_SOURCES += d2_unittests.cc
d2_unittests_SOURCES += d2_process_unittests.cc
nodist_d2_unittests_SOURCES = ../d2_messages.h ../d2_messages.cc
d2_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
......@@ -60,6 +63,9 @@ d2_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
d2_unittests_LDADD = $(GTEST_LDADD)
d2_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
d2_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
d2_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
d2_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
d2_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
endif
noinst_PROGRAMS = $(TESTS)
// 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 <config/ccsession.h>
#include <d2/d2_process.h>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <gtest/gtest.h>
#include <config.h>
#include <sstream>
using namespace std;
using namespace isc;
using namespace isc::config;
using namespace isc::d2;
using namespace boost::posix_time;
namespace {
/// @brief D2Process test fixture class
class D2ProcessTest : public ::testing::Test {
public:
/// @brief Static instance accessible via test callbacks.
static DProcessBasePtr process_;
/// @brief Constructor
D2ProcessTest() {
io_service_.reset(new isc::asiolink::IOService());
process_.reset(new D2Process("TestProcess", io_service_));
}
/// @brief Destructor
~D2ProcessTest() {
io_service_.reset();
process_.reset();
}
/// @brief Callback that will invoke shutdown method.
static void genShutdownCallback() {
process_->shutdown();
}
/// @brief Callback that throws an exception.
static void genFatalErrorCallback() {
isc_throw (DProcessBaseError, "simulated fatal error");
}
/// @brief IOService for event processing. Fills in for IOservice
/// supplied by management layer.
IOServicePtr io_service_;
};
// Define the static process instance
DProcessBasePtr D2ProcessTest::process_;
/// @brief Verifies D2Process constructor behavior.
/// 1. Verifies that constructor fails with an invalid IOService
/// 2. Verifies that constructor succeeds with a valid IOService
TEST(D2Process, construction) {
// Verify that the constructor will fail if given an empty
// io service.
IOServicePtr lcl_io_service;
EXPECT_THROW (D2Process("TestProcess", lcl_io_service), DProcessBaseError);
// Verify that the constructor succeeds with a valid io_service
lcl_io_service.reset(new isc::asiolink::IOService());
ASSERT_NO_THROW (D2Process("TestProcess", lcl_io_service));
}
/// @brief Verifies basic configure method behavior.
// @TODO This test is simplistic and will need to be augmented
// as configuration ability is implemented.
TEST_F(D2ProcessTest, configure) {
// Verify that given a configuration "set", configure returns
// a successful response.
int rcode = -1;
string config = "{ \"test-value\": 1000 } ";
isc::data::ElementPtr json = isc::data::Element::fromJSON(config);
isc::data::ConstElementPtr answer = process_->configure(json);
isc::config::parseAnswer(rcode, answer);
EXPECT_EQ(0, rcode);
}
/// @brief Verifies basic command method behavior.
// @TODO IF the D2Process is extended to support extra commands
// this testing will need to augmented accordingly.
TEST_F(D2ProcessTest, command) {
// Verfiy that the process will process unsupported command and
// return a failure response.
int rcode = -1;
string args = "{ \"arg1\": 77 } ";
isc::data::ElementPtr json = isc::data::Element::fromJSON(args);
isc::data::ConstElementPtr answer =
process_->command("bogus_command", json);
parseAnswer(rcode, answer);
EXPECT_EQ(1, rcode);
}
/// @brief Verifies that an "external" call to shutdown causes
/// the run method to exit gracefully with a return value of EXIT_SUCCESS.
TEST_F(D2ProcessTest, normalShutdown) {
// Use an asiolink IntervalTimer and callback to generate the
// shutdown invocation. (Note IntervalTimer setup is in milliseconds).
isc::asiolink::IntervalTimer timer(*io_service_);
timer.setup(genShutdownCallback, 2 * 1000);
// Record start time, and invoke run().
ptime start = microsec_clock::universal_time();
int rcode = process_->run();
// Record stop time.
ptime stop = microsec_clock::universal_time();
// Verify normal shutdown status.
EXPECT_EQ(EXIT_SUCCESS, rcode);
// Verify that duration of the run invocation is the same as the
// timer duration. This demonstrates that the shutdown was driven
// by an io_service event and callback.
time_duration elapsed = stop - start;
EXPECT_TRUE(elapsed.total_milliseconds() >= 1900 &&
elapsed.total_milliseconds() <= 2100);
}
/// @brief Verifies that an "uncaught" exception thrown during event loop
/// processing is treated as a fatal error.
TEST_F(D2ProcessTest, fatalErrorShutdown) {
// Use an asiolink IntervalTimer and callback to generate the
// the exception. (Note IntervalTimer setup is in milliseconds).
isc::asiolink::IntervalTimer timer(*io_service_);
timer.setup(genFatalErrorCallback, 2 * 1000);
// Record start time, and invoke run().
ptime start = microsec_clock::universal_time();
int rcode = process_->run();
// Record stop time.
ptime stop = microsec_clock::universal_time();
// Verify failure status.
EXPECT_EQ(EXIT_FAILURE, rcode);
// Verify that duration of the run invocation is the same as the
// timer duration. This demonstrates that the anomaly occurred
// during io callback processing.
time_duration elapsed = stop - start;
EXPECT_TRUE(elapsed.total_milliseconds() >= 1900 &&
elapsed.total_milliseconds() <= 2100);
}
} // end of anonymous namespace
......@@ -161,7 +161,7 @@ class TestD2Daemon(unittest.TestCase):
# soon enough to catch it.
(returncode, output, error) = self.runCommand(["../b10-d2", "-s"])
output_text = str(output) + str(error)
self.assertEqual(output_text.count("D2_STARTING"), 1)
self.assertEqual(output_text.count("D2CTL_STARTING"), 1)
if __name__ == '__main__':
unittest.main()
Markdown is supported
0% or .