From: Marcin Siodelski Date: Tue, 3 Jan 2017 09:58:32 +0000 (+0100) Subject: [5100] Extracted command management into BaseCommandMgr class. X-Git-Tag: trac5099_base~1^2~6 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=8c118181a39686f0f4e7d484f9905a8bc42faf97;p=thirdparty%2Fkea.git [5100] Extracted command management into BaseCommandMgr class. --- diff --git a/src/lib/config/Makefile.am b/src/lib/config/Makefile.am index 6025276d59..d01a6b8f64 100644 --- a/src/lib/config/Makefile.am +++ b/src/lib/config/Makefile.am @@ -15,6 +15,7 @@ BUILT_SOURCES = config_messages.h config_messages.cc lib_LTLIBRARIES = libkea-cfgclient.la libkea_cfgclient_la_SOURCES = config_data.h config_data.cc libkea_cfgclient_la_SOURCES += module_spec.h module_spec.cc +libkea_cfgclient_la_SOURCES += base_command_mgr.cc base_command_mgr.h libkea_cfgclient_la_SOURCES += command_mgr.cc command_mgr.h libkea_cfgclient_la_SOURCES += command_socket.cc command_socket.h libkea_cfgclient_la_SOURCES += command_socket_factory.cc command_socket_factory.h diff --git a/src/lib/config/base_command_mgr.cc b/src/lib/config/base_command_mgr.cc new file mode 100644 index 0000000000..7a1a8f66a0 --- /dev/null +++ b/src/lib/config/base_command_mgr.cc @@ -0,0 +1,111 @@ +// 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 +#include +#include +#include + +using namespace isc::data; + +namespace isc { +namespace config { + +BaseCommandMgr::BaseCommandMgr() { + registerCommand("list-commands", boost::bind(&BaseCommandMgr::listCommandsHandler, + this, _1, _2)); +} + +void +BaseCommandMgr::registerCommand(const std::string& cmd, CommandHandler handler) { + if (!handler) { + isc_throw(InvalidCommandHandler, "Specified command handler is NULL"); + } + + HandlerContainer::const_iterator it = handlers_.find(cmd); + if (it != handlers_.end()) { + isc_throw(InvalidCommandName, "Handler for command '" << cmd + << "' is already installed."); + } + + handlers_.insert(make_pair(cmd, handler)); + + LOG_DEBUG(command_logger, DBG_COMMAND, COMMAND_REGISTERED).arg(cmd); +} + +void +BaseCommandMgr::deregisterCommand(const std::string& cmd) { + if (cmd == "list-commands") { + isc_throw(InvalidCommandName, + "Can't uninstall internal command 'list-commands'"); + } + + HandlerContainer::iterator it = handlers_.find(cmd); + if (it == handlers_.end()) { + isc_throw(InvalidCommandName, "Handler for command '" << cmd + << "' not found."); + } + handlers_.erase(it); + + LOG_DEBUG(command_logger, DBG_COMMAND, COMMAND_DEREGISTERED).arg(cmd); +} + +void +BaseCommandMgr::deregisterAll() { + + // No need to log anything here. deregisterAll is not used in production + // code, just in tests. + handlers_.clear(); + registerCommand("list-commands", + boost::bind(&BaseCommandMgr::listCommandsHandler, this, _1, _2)); +} + +isc::data::ConstElementPtr +BaseCommandMgr::processCommand(const isc::data::ConstElementPtr& cmd) { + if (!cmd) { + return (createAnswer(CONTROL_RESULT_ERROR, + "Command processing failed: NULL command parameter")); + } + + try { + ConstElementPtr arg; + std::string name = parseCommand(arg, cmd); + + LOG_INFO(command_logger, COMMAND_RECEIVED).arg(name); + + HandlerContainer::const_iterator it = handlers_.find(name); + if (it == handlers_.end()) { + // Ok, there's no such command. + return (createAnswer(CONTROL_RESULT_ERROR, + "'" + name + "' command not supported.")); + } + + // Call the actual handler and return whatever it returned + return (it->second(name, arg)); + + } catch (const Exception& e) { + LOG_WARN(command_logger, COMMAND_PROCESS_ERROR2).arg(e.what()); + return (createAnswer(CONTROL_RESULT_ERROR, + std::string("Error during command processing:") + + e.what())); + } +} + +isc::data::ConstElementPtr +BaseCommandMgr::listCommandsHandler(const std::string& name, + const isc::data::ConstElementPtr& params) { + using namespace isc::data; + ElementPtr commands = Element::createList(); + for (HandlerContainer::const_iterator it = handlers_.begin(); + it != handlers_.end(); ++it) { + commands->add(Element::create(it->first)); + } + return (createAnswer(CONTROL_RESULT_SUCCESS, commands)); +} + + +} // namespace isc::config +} // namespace isc diff --git a/src/lib/config/base_command_mgr.h b/src/lib/config/base_command_mgr.h new file mode 100644 index 0000000000..389027f112 --- /dev/null +++ b/src/lib/config/base_command_mgr.h @@ -0,0 +1,147 @@ +// 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 BASE_COMMAND_MGR_H +#define BASE_COMMAND_MGR_H + +#include +#include +#include +#include +#include + +namespace isc { +namespace config { + +/// @brief Exception indicating that the handler specified is not valid +class InvalidCommandHandler : public Exception { +public: + InvalidCommandHandler(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + +/// @brief Exception indicating that the command name is not valid +class InvalidCommandName : public Exception { +public: + InvalidCommandName(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + +/// @brief Commands Manager, responsible for processing external commands. +/// +/// Commands Manager is a generic interface for handling external commands. +/// Commands are received over control sockets. Derivations of this class +/// provide implementations of the control socket layers, e.g. unix domain +/// sockets, TCP sockets etc. This base class merely provides methods to manage +/// command handling functions, i.e. register commands, deregister commands. +/// It also includes a @ref BaseCommandMgr::processCommand method which +/// uses the command as an input and invokes appropriate handlers. +/// +/// The commands and responses are formatted using JSON. +/// See http://kea.isc.org/wiki/StatsDesign for details. +/// +/// Below is an example of the command using JSON format: +/// @code +/// { +/// "command": "statistic-get", +/// "arguments": { +/// "name": "received-packets" +/// } +/// } +/// @endcode +/// +/// And the response is: +/// +/// @code +/// { +/// "result": 0, +/// "observations": { +/// "received-packets": [ [ 1234, "2015-04-15 12:34:45.123" ] ] +/// } +/// } +/// @endcode +/// +/// BaseCommandsMgr does not implement the commands (except one, +/// "list-commands") itself, but rather provides an interface +/// (see @ref registerCommand, @ref deregisterCommand, @ref processCommand) +/// for other components to use it. +class BaseCommandMgr { +public: + + /// @brief Defines command handler type + /// + /// Command handlers are expected to use this format. + /// + /// @param name name of the commands + /// @param params parameters specific to the command + /// @return response (created with createAnswer()) + typedef boost::function CommandHandler; + + /// @brief Constructor. + /// + /// Registers "list-commands" command. + BaseCommandMgr(); + + /// @brief Destructor. + virtual ~BaseCommandMgr() { }; + + /// @brief Triggers command processing. + /// + /// This method processes specified command. The command is specified using + /// a single Element. See @ref BaseCommandMgr for description of its syntax. + /// + /// @param cmd Pointer to the data element representing command in JSON + /// format. + virtual isc::data::ConstElementPtr + processCommand(const isc::data::ConstElementPtr& cmd); + + /// @brief Registers specified command handler for a given command + /// + /// @param cmd Name of the command to be handled. + /// @param handler Pointer to the method that will handle the command. + void registerCommand(const std::string& cmd, CommandHandler handler); + + /// @brief Deregisters specified command handler. + /// + /// @param cmd Name of the command that's no longer handled. + void deregisterCommand(const std::string& cmd); + + /// @brief Auxiliary method that removes all installed commands. + /// + /// The only unwipeable method is list-commands, which is internally + /// handled at all times. + void deregisterAll(); + +protected: + + /// @brief Type of the container for command handlers. + typedef std::map HandlerContainer; + + /// @brief Container for command handlers. + HandlerContainer handlers_; + +private: + + /// @brief 'list-commands' command handler. + /// + /// This method implements command 'list-commands'. It returns a list of all + /// currently supported commands. + /// + /// @param name Name of the command (should always be 'list-commands'). + /// @param params Additional parameters (ignored). + /// + /// @return Pointer to the structure that includes all currently supported + /// commands. + isc::data::ConstElementPtr + listCommandsHandler(const std::string& name, + const isc::data::ConstElementPtr& params); +}; + +} // end of namespace isc::config +} // end of namespace isc + +#endif diff --git a/src/lib/config/command_mgr.cc b/src/lib/config/command_mgr.cc index ae33188014..1239df650e 100644 --- a/src/lib/config/command_mgr.cc +++ b/src/lib/config/command_mgr.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2015-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 @@ -17,9 +17,8 @@ using namespace isc::data; namespace isc { namespace config { -CommandMgr::CommandMgr() { - registerCommand("list-commands", - boost::bind(&CommandMgr::listCommandsHandler, this, _1, _2)); +CommandMgr::CommandMgr() + : BaseCommandMgr() { } CommandSocketPtr @@ -76,48 +75,6 @@ CommandMgr::instance() { return (cmd_mgr); } -void CommandMgr::registerCommand(const std::string& cmd, CommandHandler handler) { - - if (!handler) { - isc_throw(InvalidCommandHandler, "Specified command handler is NULL"); - } - - HandlerContainer::const_iterator it = handlers_.find(cmd); - if (it != handlers_.end()) { - isc_throw(InvalidCommandName, "Handler for command '" << cmd - << "' is already installed."); - } - - handlers_.insert(make_pair(cmd, handler)); - - LOG_DEBUG(command_logger, DBG_COMMAND, COMMAND_REGISTERED).arg(cmd); -} - -void CommandMgr::deregisterCommand(const std::string& cmd) { - if (cmd == "list-commands") { - isc_throw(InvalidCommandName, - "Can't uninstall internal command 'list-commands'"); - } - - HandlerContainer::iterator it = handlers_.find(cmd); - if (it == handlers_.end()) { - isc_throw(InvalidCommandName, "Handler for command '" << cmd - << "' not found."); - } - handlers_.erase(it); - - LOG_DEBUG(command_logger, DBG_COMMAND, COMMAND_DEREGISTERED).arg(cmd); -} - -void CommandMgr::deregisterAll() { - - // No need to log anything here. deregisterAll is not used in production - // code, just in tests. - handlers_.clear(); - registerCommand("list-commands", - boost::bind(&CommandMgr::listCommandsHandler, this, _1, _2)); -} - void CommandMgr::commandReader(int sockfd) { @@ -191,48 +148,5 @@ CommandMgr::commandReader(int sockfd) { } } -isc::data::ConstElementPtr -CommandMgr::processCommand(const isc::data::ConstElementPtr& cmd) { - if (!cmd) { - return (createAnswer(CONTROL_RESULT_ERROR, - "Command processing failed: NULL command parameter")); - } - - try { - ConstElementPtr arg; - std::string name = parseCommand(arg, cmd); - - LOG_INFO(command_logger, COMMAND_RECEIVED).arg(name); - - HandlerContainer::const_iterator it = handlers_.find(name); - if (it == handlers_.end()) { - // Ok, there's no such command. - return (createAnswer(CONTROL_RESULT_ERROR, - "'" + name + "' command not supported.")); - } - - // Call the actual handler and return whatever it returned - return (it->second(name, arg)); - - } catch (const Exception& e) { - LOG_WARN(command_logger, COMMAND_PROCESS_ERROR2).arg(e.what()); - return (createAnswer(CONTROL_RESULT_ERROR, - std::string("Error during command processing:") - + e.what())); - } -} - -isc::data::ConstElementPtr -CommandMgr::listCommandsHandler(const std::string& name, - const isc::data::ConstElementPtr& params) { - using namespace isc::data; - ElementPtr commands = Element::createList(); - for (HandlerContainer::const_iterator it = handlers_.begin(); - it != handlers_.end(); ++it) { - commands->add(Element::create(it->first)); - } - return (createAnswer(CONTROL_RESULT_SUCCESS, commands)); -} - }; // end of isc::config }; // end of isc diff --git a/src/lib/config/command_mgr.h b/src/lib/config/command_mgr.h index 976b2223b1..31a41ad814 100644 --- a/src/lib/config/command_mgr.h +++ b/src/lib/config/command_mgr.h @@ -1,4 +1,4 @@ -// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2015-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 @@ -8,72 +8,21 @@ #define COMMAND_MGR_H #include +#include #include #include -#include -#include #include -#include namespace isc { namespace config { -/// @brief CommandMgr exception indicating that the handler specified is not valid -class InvalidCommandHandler : public Exception { -public: - InvalidCommandHandler(const char* file, size_t line, const char* what) : - isc::Exception(file, line, what) { }; -}; - -/// @brief CommandMgr exception indicating that the command name is not valid -class InvalidCommandName : public Exception { -public: - InvalidCommandName(const char* file, size_t line, const char* what) : - isc::Exception(file, line, what) { }; -}; - -/// @brief Commands Manager, responsible for processing external commands +/// @brief Commands Manager implementation for the Kea servers. /// -/// Commands Manager is a generic interface for handling external commands. -/// Commands can be received over control sockets. Currently unix socket is -/// supported, but additional type (udp, tcp, https etc.) may be added later. -/// The commands and responses are sent in JSON format. -/// See http://kea.isc.org/wiki/StatsDesign for details. -/// -/// In general, the command has the following format: -/// { -/// "command": "statistic-get", -/// "arguments": { -/// "name": "received-packets" -/// } -/// } -/// -/// And the response is: -/// -/// { -/// "result": 0, -/// "observations": { -/// "received-packets": [ [ 1234, "2015-04-15 12:34:45.123" ] ] -/// } -/// } -/// -/// CommandsMgr does not implement the commands (except one, "list-commands") -/// itself, but rather provides an interface (see @ref registerCommand, -/// @ref deregisterCommand, @ref processCommand) for other components to use -/// it. The @ref CommandHandler type is specified in a way to easily use -/// existing command handlers in DHCPv4 and DHCPv6 components. -class CommandMgr : public boost::noncopyable { +/// This class extends @ref BaseCommandMgr with the ability to receive and +/// respond to commands over unix domain sockets. +class CommandMgr : public BaseCommandMgr, public boost::noncopyable { public: - /// @brief Defines command handler type - /// - /// Command handlers are expected to use this format. - /// @param name name of the commands - /// @param params parameters specific to the command - /// @return response (created with createAnswer()) - typedef boost::function CommandHandler; - /// @brief CommandMgr is a singleton class. This method returns reference /// to its sole instance. /// @@ -98,26 +47,6 @@ public: /// @brief Shuts down any open control sockets void closeCommandSocket(); - /// @brief Registers specified command handler for a given command - /// - /// @param cmd name of the command to be handled - /// @param handler pointer to the method that will handle the command - void registerCommand(const std::string& cmd, CommandHandler handler); - - /// @brief Deregisters specified command handler - /// - /// @param cmd name of the command that's no longer handled - void deregisterCommand(const std::string& cmd); - - /// @brief Triggers command processing - /// - /// This method processes specified command. The command is specified using - /// a single Element. See @ref CommandMgr for description of its syntax. - /// Typically, this method is called internally, when there's a new data - /// received over control socket. However, in some cases (e.g. signal received) - /// it may be called by external code explicitly. Hence this method is public. - isc::data::ConstElementPtr processCommand(const isc::data::ConstElementPtr& cmd); - /// @brief Reads data from a socket, parses as JSON command and processes it /// /// This method is used to handle traffic on connected socket. This callback @@ -130,12 +59,6 @@ public: /// @param sockfd socket descriptor of a connected socket static void commandReader(int sockfd); - /// @brief Auxiliary method that removes all installed commands. - /// - /// The only unwipeable method is list-commands, which is internally - /// handled at all times. - void deregisterAll(); - /// @brief Adds an information about opened connection socket /// /// @param conn Connection socket to be stored @@ -161,22 +84,6 @@ private: /// Registers internal 'list-commands' command. CommandMgr(); - /// @brief 'list-commands' command handler - /// - /// This method implements command 'list-commands'. It returns a list of all - /// currently supported commands. - /// @param name name of the command (should always be 'list-commands') - /// @param params additional parameters (ignored) - /// @return structure that includes all currently supported commands - isc::data::ConstElementPtr - listCommandsHandler(const std::string& name, - const isc::data::ConstElementPtr& params); - - typedef std::map HandlerContainer; - - /// @brief Container for command handlers - HandlerContainer handlers_; - /// @brief Control socket structure /// /// This is the socket that accepts incoming connections. There can be at