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

[2395] Created the initial, bare-bones implementation DHCP-DDNS service

process class, D2Process, and the abstract class from which it derives,
DProcess. This class provides DHCP-DDNS specific event loop and business
logic.

The following new files have been added:

   src/bin/d2/d_process.h - defines the DProcess base class
   src/bin/d2/d2_process.h - defines the D2Process class
   src/bin/d2/d2_process.cc - implements the D2Process class
   src/bin/d2/tests/d2_process_unittests.cc - initial unit tests
parent 6f344e77
// 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)
: DProcess(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);
while (!shut_down_) {
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);
shut_down_ = 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 DProcess interface, which structures it such that it
/// is a managed "application", controlled by a management layer.
class D2Process : public DProcess {
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 DProcessError 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 DProcessError : public isc::Exception {
public:
DProcessError(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
/// @brief Application Process Interface
///
/// DProcess is an abstract class represents the primary "application" level
/// object in a "managed" asyncrhonous 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 DProcess. 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 fo management layer IO,
/// there are methods shutdown, configuration updates, and commands unique
/// to the application.
class DProcess {
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 DProcessError is io_service is NULL.
DProcess(const char* name, IOServicePtr io_service) : name_(name),
io_service_(io_service), shut_down_(false) {
if (!io_service_) {
isc_throw (DProcessError, "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 ~DProcess(){};
protected:
/// @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_;
};
/// @brief Defines a shared pointer to DProcess.
typedef boost::shared_ptr<DProcess> DProcessPtr;
}; // 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.
#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 DProcessPtr 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() {
D2ProcessTest::process_->shutdown();
}
/// @brief Callback that throws an exception.
static void genFatalErrorCallback() {
isc_throw (DProcessError, "simulated fatal error");
}
/// @brief IOService for event processing. Fills in for IOservice
/// supplied by management layer.
IOServicePtr io_service_;
};
/// @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), DProcessError);
// 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.
isc::asiolink::IntervalTimer timer(*io_service_);
timer.setup(genShutdownCallback, 2*1000);
// Record start time, and invoke run().
ptime start = second_clock::universal_time();
int rcode = process_->run();
// Record stop time.
ptime stop = second_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_EQ(2L, elapsed.seconds());
}
/// @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.
isc::asiolink::IntervalTimer timer(*io_service_);
timer.setup(genFatalErrorCallback, 2*1000);
// Record start time, and invoke run().
ptime start = second_clock::universal_time();
int rcode = process_->run();
// Record stop time.
ptime stop = second_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_EQ(2L, elapsed.seconds());
}
} // end of anonymous namespace
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment