Commit b1c20ec7 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[5107] Implemented CtrlAgentResponseCreator and other classes.

parent 7c882edc
......@@ -44,10 +44,13 @@ noinst_LTLIBRARIES = libagent.la
libagent_la_SOURCES = ctrl_agent_cfg_mgr.cc ctrl_agent_cfg_mgr.h
libagent_la_SOURCES += ctrl_agent_controller.cc ctrl_agent_controller.h
libagent_la_SOURCES += ctrl_agent_command_mgr.cc ctrl_agent_command_mgr.h
libagent_la_SOURCES += ctrl_agent_log.cc ctrl_agent_log.h
libagent_la_SOURCES += ctrl_agent_process.cc ctrl_agent_process.h
libagent_la_SOURCES += agent_parser.cc agent_parser.h
libagent_la_SOURCES += parser_context.cc parser_context.h parser_context_decl.h
libagent_la_SOURCES += ctrl_agent_response_creator.cc ctrl_agent_response_creator.h
libagent_la_SOURCES += ctrl_agent_response_creator_factory.h
libagent_la_SOURCES += agent_lexer.ll
nodist_libagent_la_SOURCES = ctrl_agent_messages.h ctrl_agent_messages.cc
......@@ -66,6 +69,7 @@ kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la
kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la
kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la
kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/http/libkea-http.la
kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la
......
// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <agent/ctrl_agent_command_mgr.h>
#include <cc/data.h>
#include <boost/pointer_cast.hpp>
using namespace isc::data;
namespace isc {
namespace agent {
CtrlAgentCommandMgr&
CtrlAgentCommandMgr::instance() {
static CtrlAgentCommandMgr command_mgr;
return (command_mgr);
}
CtrlAgentCommandMgr::CtrlAgentCommandMgr()
: HookedCommandMgr() {
}
ConstElementPtr
CtrlAgentCommandMgr::handleCommand(const std::string& cmd_name,
const isc::data::ConstElementPtr& params) {
ElementPtr answer_list = Element::createList();
answer_list->add(boost::const_pointer_cast<
Element>(HookedCommandMgr::handleCommand(cmd_name, params)));
return (answer_list);
}
} // end of namespace isc::agent
} // end of namespace isc
// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef CTRL_AGENT_COMMAND_MGR_H
#define CTRL_AGENT_COMMAND_MGR_H
#include <config/hooked_command_mgr.h>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
namespace isc {
namespace agent {
/// @brief Command Manager for Control Agent.
///
/// This is an implementation of the Command Manager within Control Agent.
/// In addition to the standard capabilities of the @ref HookedCommandMgr
/// it is also intended to forward commands to the respective Kea servers
/// when the command is not supported directly by the Control Agent.
///
/// @todo This Command Manager doesn't yet support forwarding commands.
///
/// The @ref CtrlAgentCommandMgr is implemented as a singleton. The commands
/// are registered using @c CtrlAgentCommandMgr::instance().registerCommand().
/// The @ref CtrlAgentResponseCreator uses the sole instance of the Command
/// Manager to handle incoming commands.
class CtrlAgentCommandMgr : public config::HookedCommandMgr,
public boost::noncopyable {
public:
/// @brief Returns sole instance of the Command Manager.
static CtrlAgentCommandMgr& instance();
private:
/// @brief Handles the command having a given name and arguments.
///
/// This method extends the base implementation with the ability to forward
/// commands to Kea servers if the Control Agent failed to handle it itself.
///
/// @todo Currently this method only wraps an answer within a list Element.
/// This will be later used to include multiple answers within this list.
/// For now it is just a single answer from the Control Agent.
///
/// @param cmd_name Command name.
/// @param params Command arguments.
///
/// @return Pointer to the const data element representing response
/// to a command.
virtual isc::data::ConstElementPtr
handleCommand(const std::string& cmd_name,
const isc::data::ConstElementPtr& params);
/// @brief Private constructor.
///
/// The instance should be created using @ref CtrlAgentCommandMgr::instance,
/// thus the constructor is private.
CtrlAgentCommandMgr();
};
} // end of namespace isc::agent
} // end of namespace isc
#endif
......@@ -6,12 +6,26 @@
#include <config.h>
#include <agent/ctrl_agent_process.h>
#include <agent/ctrl_agent_response_creator_factory.h>
#include <agent/ctrl_agent_log.h>
#include <asiolink/io_address.h>
#include <cc/command_interpreter.h>
#include <http/listener.h>
#include <boost/pointer_cast.hpp>
using namespace isc::asiolink;
using namespace isc::http;
using namespace isc::process;
// Temporarily hardcoded configuration.
namespace {
const IOAddress SERVER_ADDRESS("127.0.0.1");
const unsigned short SERVER_PORT = 8081;
const long REQUEST_TIMEOUT = 10000;
}
namespace isc {
namespace agent {
......@@ -32,6 +46,12 @@ CtrlAgentProcess::run() {
LOG_INFO(agent_logger, CTRL_AGENT_STARTED).arg(VERSION);
try {
HttpResponseCreatorFactoryPtr rcf(new CtrlAgentResponseCreatorFactory());
HttpListener http_listener(*getIoService(), SERVER_ADDRESS,
SERVER_PORT, rcf, REQUEST_TIMEOUT);
http_listener.start();
while (!shouldShutdown()) {
getIoService()->run_one();
}
......
// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <agent/ctrl_agent_command_mgr.h>
#include <agent/ctrl_agent_response_creator.h>
#include <cc/data.h>
#include <http/post_request_json.h>
#include <http/response_json.h>
#include <boost/pointer_cast.hpp>
using namespace isc::data;
using namespace isc::http;
namespace isc {
namespace agent {
HttpRequestPtr
CtrlAgentResponseCreator::createNewHttpRequest() const {
return (HttpRequestPtr(new PostHttpRequestJson()));
}
HttpResponsePtr
CtrlAgentResponseCreator::
createStockHttpResponse(const ConstHttpRequestPtr& request,
const HttpStatusCode& status_code) const {
// The request hasn't been finalized so the request object
// doesn't contain any information about the HTTP version number
// used. But, the context should have this data (assuming the
// HTTP version is parsed ok).
HttpVersion http_version(request->context()->http_version_major_,
request->context()->http_version_minor_);
// We only accept HTTP version 1.0 or 1.1. If other version number is found
// we fall back to HTTP/1.0.
if ((http_version < HttpVersion(1, 0)) || (HttpVersion(1, 1) < http_version)) {
http_version.major_ = 1;
http_version.minor_ = 0;
}
// This will generate the response holding JSON content.
HttpResponsePtr response(new HttpResponseJson(http_version, status_code));
return (response);
}
HttpResponsePtr
CtrlAgentResponseCreator::
createDynamicHttpResponse(const ConstHttpRequestPtr& request) {
// The request is always non-null, because this is verified by the
// createHttpResponse method. Let's try to convert it to the
// ConstPostHttpRequestJson type as this is the type generated by the
// createNewHttpRequest. If the conversion result is null it means that
// the caller did not use createNewHttpRequest method to create this
// instance. This is considered an error in the server logic.
ConstPostHttpRequestJsonPtr request_json = boost::dynamic_pointer_cast<
const PostHttpRequestJson>(request);
if (!request_json) {
// Notify the client that we have a problem with our server.
return (createStockHttpResponse(request, HttpStatusCode::INTERNAL_SERVER_ERROR));
}
// We have already checked that the request is finalized so the call
// to getBodyAsJson must not trigger an exception.
ConstElementPtr command = request_json->getBodyAsJson();
// Process command doesn't generate exceptions but can possibly return
// null response, if the handler is not implemented properly. This is
// again an internal server issue.
ConstElementPtr response = CtrlAgentCommandMgr::instance().processCommand(command);
if (!response) {
// Notify the client that we have a problem with our server.
return (createStockHttpResponse(request, HttpStatusCode::INTERNAL_SERVER_ERROR));
}
// The response is ok, so let's create new HTTP response with the status OK.
HttpResponseJsonPtr http_response = boost::dynamic_pointer_cast<
HttpResponseJson>(createStockHttpResponse(request, HttpStatusCode::OK));
http_response->setBodyAsJson(response);
return (http_response);
}
} // end of namespace isc::agent
} // end of namespace isc
// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef CTRL_AGENT_RESPONSE_CREATOR_H
#define CTRL_AGENT_RESPONSE_CREATOR_H
#include <agent/ctrl_agent_command_mgr.h>
#include <http/response_creator.h>
#include <boost/shared_ptr.hpp>
namespace isc {
namespace agent {
class CtrlAgentResponseCreator;
/// @brief Pointer to the @ref CtrlAgentResponseCreator.
typedef boost::shared_ptr<CtrlAgentResponseCreator> CtrlAgentResponseCreatorPtr;
/// @brief Concrete implementation of the HTTP response crator used
/// by the Control Agent.
///
/// See the documentation of the @ref HttpResponseCreator for the basic
/// information how HTTP response creators are utilized by the libkea-http
/// library to generate HTTP responses.
///
/// This creator expects that received requests are encapsulated in the
/// @ref PostHttpRequestJson objects. The generated responses are
/// encapsulated in the HttpResponseJson objects.
///
/// This class uses @ref CtrlAgentCommandMgr singleton to process commands
/// conveyed in the HTTP body. The JSON responses returned by the manager
/// are placed in the body of the generated HTTP responses.
class CtrlAgentResponseCreator : public http::HttpResponseCreator {
public:
/// @brief Create a new request.
///
/// This method creates a bare instance of the @ref PostHttpRequestJson.
///
/// @return Pointer to the new instance of the @ref PostHttpRequestJson.
virtual http::HttpRequestPtr createNewHttpRequest() const;
/// @brief Creates stock HTTP response.
///
/// @param request Pointer to an object representing HTTP request.
/// @param status_code Status code of the response.
/// @return Pointer to an @ref HttpResponseJson object representing stock
/// HTTP response.
virtual http::HttpResponsePtr
createStockHttpResponse(const http::ConstHttpRequestPtr& request,
const http::HttpStatusCode& status_code) const;
private:
/// @brief Creates implementation specific HTTP response.
///
/// @param request Pointer to an object representing HTTP request.
/// @return Pointer to an object representing HTTP response.
virtual http::HttpResponsePtr
createDynamicHttpResponse(const http::ConstHttpRequestPtr& request);
};
} // end of namespace isc::agent
} // end of namespace isc
#endif
// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef CTRL_AGENT_RESPONSE_CREATOR_FACTORY_H
#define CTRL_AGENT_RESPONSE_CREATOR_FACTORY_H
#include <agent/ctrl_agent_response_creator.h>
#include <http/response_creator_factory.h>
namespace isc {
namespace agent {
/// @brief HTTP response creator factory for Control Agent.
///
/// See the documentation of the @ref HttpResponseCreatorFactory for
/// the details how the response factory object is used by the
/// @ref HttpListener.
///
/// This class always returns the same instance of the
/// @ref CtrlAgentResponseCreator which @ref HttpListener and
/// @ref HttpConnection classes use to generate HTTP response messages
/// which comply with the formats required by the Control Agent.
class CtrlAgentResponseCreatorFactory : public http::HttpResponseCreatorFactory {
public:
/// @brief Constructor.
///
/// Creates sole instance of the @ref CtrlAgentResponseCreator object
/// returned by the @ref CtrlAgentResponseCreatorFactory::create.
CtrlAgentResponseCreatorFactory()
: sole_creator_(new CtrlAgentResponseCreator()) {
}
/// @brief Returns an instance of the @ref CtrlAgentResponseCreator which
/// is used by HTTP server to generate responses to commands.
///
/// @return Pointer to the @ref CtrlAgentResponseCreator object.
virtual http::HttpResponseCreatorPtr create() const {
return (sole_creator_);
}
private:
/// @brief Instance of the @ref CtrlAgentResponseCreator returned.
http::HttpResponseCreatorPtr sole_creator_;
};
} // end of namespace isc::agent
} // end of namespace isc
#endif
......@@ -44,9 +44,12 @@ TESTS += ctrl_agent_unittest
ctrl_agent_unittest_SOURCES = ctrl_agent_cfg_mgr_unittest.cc
ctrl_agent_unittest_SOURCES += ctrl_agent_controller_unittest.cc
ctrl_agent_unittests_SOURCES += ctrl_agent_command_mgr_unittests.cc
ctrl_agent_unittest_SOURCES += parser_unittest.cc
ctrl_agent_unittest_SOURCES += ctrl_agent_process_unittest.cc
ctrl_agent_unittests_SOURCES += ctrl_agent_response_creator_factory_unittests.cc
ctrl_agent_unittest_SOURCES += ctrl_agent_unittest.cc
ctrl_agent_unittests_SOURCES += ctrl_agent_response_creator_unittests.cc
ctrl_agent_unittest_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
ctrl_agent_unittest_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS)
......@@ -61,6 +64,7 @@ ctrl_agent_unittest_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.
ctrl_agent_unittest_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la
ctrl_agent_unittest_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la
ctrl_agent_unittest_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
ctrl_agent_unittests_LDADD += $(top_builddir)/src/lib/http/libkea-http.la
ctrl_agent_unittest_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
ctrl_agent_unittest_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
ctrl_agent_unittest_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la
......
// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
#include <agent/ctrl_agent_command_mgr.h>
#include <gtest/gtest.h>
using namespace isc::agent;
namespace {
/// @brief Test fixture class for @ref CtrlAgentCommandMgr.
///
/// @todo Add tests for various commands, including the cases when the
/// commands are forwarded to other servers via unix sockets.
/// Meanwhile, this is just a placeholder for the tests.
class CtrlAgentCommandMgrTest : public ::testing::Test {
public:
/// @brief Constructor.
///
/// Deregisters all commands except 'list-commands'.
CtrlAgentCommandMgrTest() {
CtrlAgentCommandMgr::instance().deregisterAll();
}
/// @brief Destructor.
///
/// Deregisters all commands except 'list-commands'.
virtual ~CtrlAgentCommandMgrTest() {
CtrlAgentCommandMgr::instance().deregisterAll();
}
};
}
// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
#include <agent/ctrl_agent_response_creator.h>
#include <agent/ctrl_agent_response_creator_factory.h>
#include <boost/pointer_cast.hpp>
#include <gtest/gtest.h>
using namespace isc::agent;
namespace {
// This test verifies that create() method always returns the same
// instance of the CtrlAgentResponseCreator object.
TEST(CtrlAgentResponseCreatorFactory, create) {
CtrlAgentResponseCreatorFactory factory;
// Invoke twice.
CtrlAgentResponseCreatorPtr response1;
CtrlAgentResponseCreatorPtr response2;
ASSERT_NO_THROW(response1 = boost::dynamic_pointer_cast<
CtrlAgentResponseCreator>(factory.create()));
ASSERT_NO_THROW(response2 = boost::dynamic_pointer_cast<
CtrlAgentResponseCreator>(factory.create()));
// It must always return non-null object.
ASSERT_TRUE(response1);
ASSERT_TRUE(response2);
// And it must always return the same object.
EXPECT_TRUE(response1 == response2);
}
}
// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
#include <agent/ctrl_agent_command_mgr.h>
#include <agent/ctrl_agent_response_creator.h>
#include <cc/command_interpreter.h>
#include <http/post_request.h>
#include <http/post_request_json.h>
#include <http/response_json.h>
#include <gtest/gtest.h>
#include <boost/bind.hpp>
#include <boost/pointer_cast.hpp>
using namespace isc::agent;
using namespace isc::config;
using namespace isc::data;
using namespace isc::http;
namespace {
/// @brief Test fixture class for @ref CtrlAgentResponseCreator.
class CtrlAgentResponseCreatorTest : public ::testing::Test {
public:
/// @brief Constructor.
///
/// Creates instance of the response creator and uses this instance to
/// create "empty" request. It also removes registered commands from the
/// command manager.
CtrlAgentResponseCreatorTest()
: response_creator_(),
request_(response_creator_.createNewHttpRequest()) {
// Deregisters commands.
CtrlAgentCommandMgr::instance().deregisterAll();
CtrlAgentCommandMgr::instance().
registerCommand("foo", boost::bind(&CtrlAgentResponseCreatorTest::
fooCommandHandler,
this, _1, _2));
// Make sure that the request has been initialized properly.
if (!request_) {
ADD_FAILURE() << "CtrlAgentResponseCreator::createNewHttpRequest"
" returns NULL!";
}
}
/// @brief Destructor.
///
/// Removes registered commands from the command manager.
virtual ~CtrlAgentResponseCreatorTest() {
CtrlAgentCommandMgr::instance().deregisterAll();
}
/// @brief Fills request context with required data to create new request.
///
/// @param request Request which context should be configured.
void setBasicContext(const HttpRequestPtr& request) {
request->context()->method_ = "POST";
request->context()->http_version_major_ = 1;
request->context()->http_version_minor_ = 1;
request->context()->uri_ = "/foo";
// Content-Type
HttpHeaderContext content_type;
content_type.name_ = "Content-Type";
content_type.value_ = "application/json";
request->context()->headers_.push_back(content_type);
// Content-Length
HttpHeaderContext content_length;
content_length.name_ = "Content-Length";
content_length.value_ = "0";
request->context()->headers_.push_back(content_length);
}
/// @brief Test creation of stock response.
///
/// @param status_code Status code to be included in the response.
/// @param must_contain Text that must be present in the textual
/// representation of the generated response.
void testStockResponse(const HttpStatusCode& status_code,
const std::string& must_contain) {
HttpResponsePtr response;
ASSERT_NO_THROW(
response = response_creator_.createStockHttpResponse(request_,
status_code)
);
ASSERT_TRUE(response);
HttpResponseJsonPtr response_json = boost::dynamic_pointer_cast<
HttpResponseJson>(response);
ASSERT_TRUE(response_json);
// Make sure the response contains the string specified as argument.
EXPECT_TRUE(response_json->toString().find(must_contain) != std::string::npos);
}
/// @brief Handler for the 'foo' test command.
///
/// @param command_name Command name, i.e. 'foo'.
/// @param command_arguments Command arguments (empty).
///
/// @return Returns response with a single string "bar".
ConstElementPtr fooCommandHandler(const std::string& command_name,
const ConstElementPtr& command_arguments) {
ElementPtr arguments = Element::createList();
arguments->add(Element::create("bar"));
return (createAnswer(CONTROL_RESULT_SUCCESS, arguments));
}
/// @brief Instance of the response crator.
CtrlAgentResponseCreator response_creator_;
/// @brief Instance of the "empty" request.
///
/// The context belonging to this request may be modified by the unit
/// tests to verify various scenarios of response creation.
HttpRequestPtr request_;
};