ccsession.h 10.2 KB
Newer Older
JINMEI Tatuya's avatar
JINMEI Tatuya committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
// Copyright (C) 2009  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.

// $Id$

#ifndef __CCSESSION_H
#define __CCSESSION_H 1

#include <string>

22
#include <config/config_data.h>
23
#include <config/module_spec.h>
24 25
#include <cc/session.h>
#include <cc/data.h>
JINMEI Tatuya's avatar
JINMEI Tatuya committed
26

27 28 29 30 31 32
namespace boost {
namespace asio {
class io_service;
}
}

33 34 35
namespace isc {
namespace config {

36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
///
/// \brief Creates a standard config/command level success answer message
///        (i.e. of the form { "result": [ 0 ] }
/// \return Standard command/config success answer message
ElementPtr createAnswer();

///
/// \brief Creates a standard config/command level answer message
///        (i.e. of the form { "result": [ rcode, arg ] }
/// If rcode != 0, arg must be a StringElement
///
/// \param rcode The return code (0 for success)
/// \param arg For rcode == 0, this is an optional argument of any
///            Element type. For rcode == 1, this argument is mandatory,
///            and must be a StringElement containing an error description
/// \return Standard command/config answer message
52
ElementPtr createAnswer(const int rcode, const ElementPtr arg);
53 54 55 56 57 58 59 60

///
/// \brief Creates a standard config/command level answer message
/// (i.e. of the form { "result": [ rcode, arg ] }
///
/// \param rcode The return code (0 for success)
/// \param arg A string to put into the StringElement argument
/// \return Standard command/config answer message
61
ElementPtr createAnswer(const int rcode, const std::string& arg);
62 63 64 65 66 67 68 69 70 71

///
/// Parses a standard config/command level answer message
/// 
/// \param rcode This value will be set to the return code contained in
///              the message
/// \param msg The message to parse
/// \return The optional argument in the message, or an empty ElementPtr
///         if there was no argument. If rcode != 0, this contains a
///         StringElement with the error description.
72 73
ElementPtr parseAnswer(int &rcode, const ElementPtr msg);

74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90

///
/// \brief Creates a standard config/command command message with no
/// argument (of the form { "command": [ "my_command" ] }
/// 
/// \param command The command string
/// \return The created message
ElementPtr createCommand(const std::string& command);

///
/// \brief Creates a standard config/command command message with the
/// given argument (of the form { "command": [ "my_command", arg ] }
/// 
/// \param command The command string
/// \param arg The optional argument for the command. This can be of 
///        any Element type, but it should conform to the .spec file.
/// \return The created message
91
ElementPtr createCommand(const std::string& command, ElementPtr arg);
92 93 94 95 96 97 98 99 100 101

///
/// \brief Parses the given command into a string containing the actual
///        command and an ElementPtr containing the optional argument.
///
/// \param arg This value will be set to the ElementPtr pointing to
///        the argument, or to an empty ElementPtr if there was none.
/// \param command The command message containing the command (as made
///        by createCommand()
/// \return The command string
102 103 104
const std::string parseCommand(ElementPtr& arg, const ElementPtr command);


Jelte Jansen's avatar
Jelte Jansen committed
105 106 107 108 109 110 111 112 113 114 115
///
/// \brief A standard cc session exception that is thrown if a function
/// is there is a problem with one of the messages
///
// todo: include types and called function in the exception
class CCSessionError : public isc::Exception {
public:
    CCSessionError(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) {}
};

116 117 118 119 120
///
/// \brief This modules keeps a connection to the command channel,
/// holds configuration information, and handles messages from
/// the command channel
///
121
class ModuleCCSession : public ConfigData {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
122
public:
123 124 125 126 127
    /**
     * Initialize a config/command session
     * @param module_name: The name of this module. This is not a
     *                     reference because we expect static strings
     *                     to be passed here.
128 129
     * @param spec_file_name: The name of the file containing the
     *                        module specification.
130
     */
131 132
    ModuleCCSession(std::string spec_file_name,
                    isc::data::ElementPtr(*config_handler)(isc::data::ElementPtr new_config) = NULL,
Jelte Jansen's avatar
Jelte Jansen committed
133
                    isc::data::ElementPtr(*command_handler)(const std::string& command, const isc::data::ElementPtr args) = NULL
134
                    ) throw (isc::cc::SessionError);
135 136 137 138 139
    ModuleCCSession(std::string spec_file_name,
                    boost::asio::io_service& io_service,
                    isc::data::ElementPtr(*config_handler)(isc::data::ElementPtr new_config) = NULL,
                    isc::data::ElementPtr(*command_handler)(const std::string& command, const isc::data::ElementPtr args) = NULL
                    ) throw (isc::cc::SessionError);
140 141 142 143 144 145 146 147 148 149

    /**
     * Returns the socket that is used to communicate with the msgq
     * command channel. This socket should *only* be used to run a
     * select() loop over it. And if not time-critical, it is strongly
     * recommended to only use checkCommand() to check for messages
     *
     * @return The socket used to communicate with the msgq command
     *         channel.
     */
JINMEI Tatuya's avatar
JINMEI Tatuya committed
150
    int getSocket();
151

Jelte Jansen's avatar
Jelte Jansen committed
152 153 154 155 156 157 158 159 160 161
    /**
     * Optional optimization for checkCommand loop; returns true
     * if there are unhandled queued messages in the cc session.
     * (if either this is true or there is data on the socket found
     * by the select() call on getSocket(), run checkCommand())
     * 
     * @return true if there are unhandled queued messages
     */
    bool hasQueuedMsgs();

162 163 164 165 166 167 168
    /**
     * Check if there is a command or config change on the command
     * session. If so, the appropriate handler is called if set.
     * If not set, a default answer is returned.
     * This is a non-blocking read; if there is nothing this function
     * will return 0.
     */
169
    int checkCommand();
170 171 172 173 174 175 176 177 178

    /**
     * The config handler function should expect an ElementPtr containing
     * the full configuration where non-default values have been set.
     * Later we might want to think about more granular control
     * (i.e. this does not scale to for instance lists containing
     * 100000 zones, where the whole list is passed every time a single
     * thing changes)
     */
179
    void setConfigHandler(isc::data::ElementPtr(*config_handler)(isc::data::ElementPtr new_config)) { config_handler_ = config_handler; };
180 181 182 183 184 185 186 187 188 189 190

    /**
     * Set a command handler; the function that is passed takes an
     * ElementPtr, pointing to a list element, containing
     * [ module_name, command_name, arg1, arg2, ... ]
     * The returned ElementPtr should look like
     * { "result": [ return_value, result_value ] }
     * result value here is optional and depends on the command
     *
     * This protocol is very likely to change.
     */
191
    void setCommandHandler(isc::data::ElementPtr(*command_handler)(const std::string& command, const isc::data::ElementPtr args)) { command_handler_ = command_handler; };
Jelte Jansen's avatar
Jelte Jansen committed
192

193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
    /**
     * Gives access to the configuration values of a different module
     * Once this function has been called with the name of the specification
     * file of the module you want the configuration of, you can use
     * \c getRemoteConfigValue() to get a specific setting.
     * Changes are automatically updated, but you cannot specify handlers
     * for those changes, must use \c getRemoteConfigValue() to get a value
     * This function will subscribe to the relevant module channel.
     *
     * \param spec_file_name The path to the specification file of
     *                       the module we want to have configuration
     *                       values from
     * \return The name of the module specified in the given specification
     *         file
     */
    std::string addRemoteConfig(const std::string& spec_file_name);

    /**
     * Removes the module with the given name from the remote config
     * settings. If the module was not added with \c addRemoteConfig(),
     * nothing happens.
     */
    void removeRemoteConfig(const std::string& module_name);

    /**
     * Returns the current configuration value for the given module
     * name at the given identifier. See \c ConfigData::getValue() for
     * more details.
     * Raises a ModuleCCSessionError if the module name is unknown
     * Raises a DataNotFoundError if the identifier does not exist
     * in the specification.
     *
     * \param module_name The name of the module to get a config value for
     * \param identifier The identifier of the config value
     * \return The configuration setting at the given identifier
     */
    ElementPtr getRemoteConfigValue(const std::string& module_name, const std::string& identifier);
    
JINMEI Tatuya's avatar
JINMEI Tatuya committed
231
private:
232 233 234 235 236 237 238
    void init(
        std::string spec_file_name,
        isc::data::ElementPtr(*config_handler)(
            isc::data::ElementPtr new_config),
        isc::data::ElementPtr(*command_handler)(
            const std::string& command, const isc::data::ElementPtr args)
        ) throw (isc::cc::SessionError);
239
    ModuleSpec readModuleSpecification(const std::string& filename);
240
    void startCheck();
241 242
    
    std::string module_name_;
Jelte Jansen's avatar
Jelte Jansen committed
243
    isc::cc::Session session_;
244
    ModuleSpec module_specification_;
Jelte Jansen's avatar
Jelte Jansen committed
245
    ElementPtr handleConfigUpdate(ElementPtr new_config);
246

Jelte Jansen's avatar
Jelte Jansen committed
247
    isc::data::ElementPtr(*config_handler_)(isc::data::ElementPtr new_config);
Jelte Jansen's avatar
Jelte Jansen committed
248
    isc::data::ElementPtr(*command_handler_)(const std::string& command, const isc::data::ElementPtr args);
249 250 251

    std::map<std::string, ConfigData> remote_module_configs_;
    void updateRemoteConfig(const std::string& module_name, ElementPtr new_config);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
252 253
};

254 255
}
}
JINMEI Tatuya's avatar
JINMEI Tatuya committed
256 257 258 259 260
#endif // __CCSESSION_H

// Local Variables:
// mode: c++
// End: