Branch data Line data Source code
1 : : // Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
2 : : //
3 : : // Permission to use, copy, modify, and/or distribute this software for any
4 : : // purpose with or without fee is hereby granted, provided that the above
5 : : // copyright notice and this permission notice appear in all copies.
6 : : //
7 : : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
8 : : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9 : : // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
10 : : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11 : : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
12 : : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13 : : // PERFORMANCE OF THIS SOFTWARE.
14 : :
15 : : #ifndef __CCSESSION_H
16 : : #define __CCSESSION_H 1
17 : :
18 : : #include <string>
19 : :
20 : : #include <config/config_data.h>
21 : : #include <config/module_spec.h>
22 : : #include <cc/session.h>
23 : : #include <cc/data.h>
24 : :
25 : : namespace isc {
26 : : namespace config {
27 : :
28 : : ///
29 : : /// \brief Creates a standard config/command level success answer message
30 : : /// (i.e. of the form { "result": [ 0 ] }
31 : : /// \return Standard command/config success answer message
32 : : isc::data::ConstElementPtr createAnswer();
33 : :
34 : : ///
35 : : /// \brief Creates a standard config/command level answer message
36 : : /// (i.e. of the form { "result": [ rcode, arg ] }
37 : : /// If rcode != 0, arg must be a StringElement
38 : : ///
39 : : /// \param rcode The return code (0 for success)
40 : : /// \param arg For rcode == 0, this is an optional argument of any
41 : : /// Element type. For rcode == 1, this argument is mandatory,
42 : : /// and must be a StringElement containing an error description
43 : : /// \return Standard command/config answer message
44 : : isc::data::ConstElementPtr createAnswer(const int rcode,
45 : : isc::data::ConstElementPtr arg);
46 : :
47 : : ///
48 : : /// \brief Creates a standard config/command level answer message
49 : : /// (i.e. of the form { "result": [ rcode, arg ] }
50 : : ///
51 : : /// \param rcode The return code (0 for success)
52 : : /// \param arg A string to put into the StringElement argument
53 : : /// \return Standard command/config answer message
54 : : isc::data::ConstElementPtr createAnswer(const int rcode,
55 : : const std::string& arg);
56 : :
57 : : ///
58 : : /// Parses a standard config/command level answer message
59 : : ///
60 : : /// \param rcode This value will be set to the return code contained in
61 : : /// the message
62 : : /// \param msg The message to parse
63 : : /// \return The optional argument in the message, or an empty ElementPtr
64 : : /// if there was no argument. If rcode != 0, this contains a
65 : : /// StringElement with the error description.
66 : : isc::data::ConstElementPtr parseAnswer(int &rcode,
67 : : isc::data::ConstElementPtr msg);
68 : :
69 : : ///
70 : : /// \brief Creates a standard config/command command message with no
71 : : /// argument (of the form { "command": [ "my_command" ] }
72 : : ///
73 : : /// \param command The command string
74 : : /// \return The created message
75 : : isc::data::ConstElementPtr createCommand(const std::string& command);
76 : :
77 : : ///
78 : : /// \brief Creates a standard config/command command message with the
79 : : /// given argument (of the form { "command": [ "my_command", arg ] }
80 : : ///
81 : : /// \param command The command string
82 : : /// \param arg The optional argument for the command. This can be of
83 : : /// any Element type, but it should conform to the .spec file.
84 : : /// \return The created message
85 : : isc::data::ConstElementPtr createCommand(const std::string& command,
86 : : isc::data::ConstElementPtr arg);
87 : :
88 : : ///
89 : : /// \brief Parses the given command into a string containing the actual
90 : : /// command and an ElementPtr containing the optional argument.
91 : : ///
92 : : /// Raises a CCSessionError if this is not a well-formed command
93 : : ///
94 : : /// Example code: (command_message is a ConstElementPtr that is
95 : : /// passed here)
96 : : /// \code
97 : : /// ElementPtr command_message = Element::fromJSON("{ \"command\": [\"foo\", { \"bar\": 123 } ] }");
98 : : /// try {
99 : : /// ConstElementPtr args;
100 : : /// std::string command_str = parseCommand(args, command_message);
101 : : /// if (command_str == "foo") {
102 : : /// std::cout << "The command 'foo' was given" << std::endl;
103 : : /// if (args->contains("bar")) {
104 : : /// std::cout << "It had argument name 'bar' set, which has"
105 : : /// << "value "
106 : : /// << args->get("bar")->intValue();
107 : : /// }
108 : : /// } else {
109 : : /// std::cout << "Unknown command '" << command_str << std::endl;
110 : : /// }
111 : : /// } catch (CCSessionError cse) {
112 : : /// std::cerr << "Bad command in CC Session: "
113 : : /// << cse.what() << std::endl;
114 : : /// }
115 : : /// \endcode
116 : : ///
117 : : /// \param arg This value will be set to the ElementPtr pointing to
118 : : /// the argument, or to an empty Map (ElementPtr) if there was none.
119 : : /// \param command The command message containing the command (as made
120 : : /// by createCommand()
121 : : /// \return The command name
122 : : std::string parseCommand(isc::data::ConstElementPtr& arg,
123 : : isc::data::ConstElementPtr command);
124 : :
125 : :
126 : : ///
127 : : /// \brief A standard cc session exception that is thrown if a function
128 : : /// is there is a problem with one of the messages
129 : : ///
130 : : // todo: include types and called function in the exception
131 : 51 : class CCSessionError : public isc::Exception {
132 : : public:
133 : 27 : CCSessionError(const char* file, size_t line, const char* what) :
134 [ + - ][ + - ]: 51 : isc::Exception(file, line, what) {}
[ + - ][ + - ]
135 : : };
136 : :
137 : : ///
138 : : /// \brief This exception is thrown if the constructor fails
139 : : ///
140 : 3 : class CCSessionInitError : public isc::Exception {
141 : : public:
142 : : CCSessionInitError(const char* file, size_t line, const char* what) :
143 [ # # ][ + - ]: 3 : isc::Exception(file, line, what) {}
[ + - ][ # # ]
[ - + ]
144 : : };
145 : :
146 : : ///
147 : : /// \brief This module keeps a connection to the command channel,
148 : : /// holds configuration information, and handles messages from
149 : : /// the command channel
150 : : ///
151 : : class ModuleCCSession : public ConfigData {
152 : : public:
153 : : /**
154 : : * Initialize a config/command session
155 : : *
156 : : * @param spec_file_name The name of the file containing the
157 : : * module specification.
158 : : * @param session A Session object over which configuration and command
159 : : * data are exchanged.
160 : : * @param config_handler A callback function pointer to be called when
161 : : * configuration of the local module needs to be updated.
162 : : * This must refer to a valid object of a concrete derived class of
163 : : * AbstractSession without establishing the session.
164 : : *
165 : : * Note: the design decision on who is responsible for establishing the
166 : : * session is in flux, and may change in near future.
167 : : *
168 : : * \exception CCSessionInitError when the initialization fails,
169 : : * either because the file cannot be read or there is
170 : : * a communication problem with the config manager.
171 : : *
172 : : * @param command_handler A callback function pointer to be called when
173 : : * a control command from a remote agent needs to be performed on the
174 : : * local module.
175 : : * @param start_immediately If true (default), start listening to new commands
176 : : * and configuration changes asynchronously at the end of the constructor;
177 : : * if false, it will be delayed until the start() method is explicitly
178 : : * called. (This is a short term workaround for an initialization trouble.
179 : : * We'll need to develop a cleaner solution, and then remove this knob)
180 : : * @param handle_logging If true, the ModuleCCSession will automatically
181 : : * take care of logging configuration through the virtual Logging config
182 : : * module. Defaults to true.
183 : : */
184 : : ModuleCCSession(const std::string& spec_file_name,
185 : : isc::cc::AbstractSession& session,
186 : : isc::data::ConstElementPtr(*config_handler)(
187 : : isc::data::ConstElementPtr new_config) = NULL,
188 : : isc::data::ConstElementPtr(*command_handler)(
189 : : const std::string& command,
190 : : isc::data::ConstElementPtr args) = NULL,
191 : : bool start_immediately = true,
192 : : bool handle_logging = true
193 : : );
194 : :
195 : : ///
196 : : /// Destructor
197 : : ///
198 : : /// The destructor automatically calls sendStopping(), which sends
199 : : /// a message to the ConfigManager that this module is stopping
200 : : ///
201 : : virtual ~ModuleCCSession();
202 : :
203 : : /// Start receiving new commands and configuration changes asynchronously.
204 : : ///
205 : : /// This method must be called only once, and only when the ModuleCCSession
206 : : /// was constructed with start_immediately being false. Otherwise
207 : : /// CCSessionError will be thrown.
208 : : ///
209 : : /// As noted in the constructor, this method should be considered a short
210 : : /// term workaround and will be removed in future.
211 : : void start();
212 : :
213 : : /**
214 : : * Optional optimization for checkCommand loop; returns true
215 : : * if there are unhandled queued messages in the cc session.
216 : : * (if either this is true or there is data on the socket found
217 : : * by the select() call on getSocket(), run checkCommand())
218 : : *
219 : : * @return true if there are unhandled queued messages
220 : : */
221 : : bool hasQueuedMsgs() const;
222 : :
223 : : /**
224 : : * Check if there is a command or config change on the command
225 : : * session. If so, the appropriate handler is called if set.
226 : : * If not set, a default answer is returned.
227 : : * This is a non-blocking read; if there is nothing this function
228 : : * will return 0.
229 : : */
230 : : int checkCommand();
231 : :
232 : : /**
233 : : * The config handler function should expect an ElementPtr containing
234 : : * the full configuration where non-default values have been set.
235 : : * Later we might want to think about more granular control
236 : : * (i.e. this does not scale to for instance lists containing
237 : : * 100000 zones, where the whole list is passed every time a single
238 : : * thing changes)
239 : : */
240 : : void setConfigHandler(isc::data::ConstElementPtr(*config_handler)(
241 : : isc::data::ConstElementPtr new_config))
242 : : {
243 : : config_handler_ = config_handler;
244 : : }
245 : :
246 : : /**
247 : : * Set a command handler; the function that is passed takes an
248 : : * ElementPtr, pointing to a list element, containing
249 : : * [ module_name, command_name, arg1, arg2, ... ]
250 : : * The returned ElementPtr should look like
251 : : * { "result": [ return_value, result_value ] }
252 : : * result value here is optional and depends on the command
253 : : *
254 : : * This protocol is very likely to change.
255 : : */
256 : 0 : void setCommandHandler(isc::data::ConstElementPtr(*command_handler)(
257 : : const std::string& command,
258 : : isc::data::ConstElementPtr args))
259 : : {
260 : 1 : command_handler_ = command_handler;
261 : 0 : }
262 : :
263 : : /**
264 : : * Gives access to the configuration values of a different module
265 : : * Once this function has been called with the name of the specification
266 : : * file or the module you want the configuration of, you can use
267 : : * \c getRemoteConfigValue() to get a specific setting.
268 : : * Changes are automatically updated, and you can specify handlers
269 : : * for those changes. This function will subscribe to the relevant module
270 : : * channel.
271 : : *
272 : : * This method must be called before calling the \c start() method on the
273 : : * ModuleCCSession (it also implies the ModuleCCSession must have been
274 : : * constructed with start_immediately being false).
275 : : *
276 : : * \param spec_name This specifies the module to add. It is either a
277 : : * filename of the spec file to use or a name of module
278 : : * (in case it's a module name, the spec data is
279 : : * downloaded from the configuration manager, therefore
280 : : * the configuration manager must know it). If
281 : : * spec_is_filename is true (the default), then a
282 : : * filename is assumed, otherwise a module name.
283 : : * \param handler The handler function called whenever there's a change.
284 : : * Called once initally from this function. May be NULL
285 : : * if you don't want any handler to be called and you're
286 : : * fine with requesting the data through
287 : : * getRemoteConfigValue() each time.
288 : : *
289 : : * The handler should not throw, or it'll fall trough and
290 : : * the exception will get into strange places, probably
291 : : * aborting the application.
292 : : * \param spec_is_filename Says if spec_name is filename or module name.
293 : : * \return The name of the module specified in the given specification
294 : : * file
295 : : */
296 : : std::string addRemoteConfig(const std::string& spec_name,
297 : : void (*handler)(const std::string& module_name,
298 : : isc::data::ConstElementPtr
299 : : update,
300 : : const ConfigData& config_data) = NULL,
301 : : bool spec_is_filename = true);
302 : :
303 : : /**
304 : : * Removes the module with the given name from the remote config
305 : : * settings. If the module was not added with \c addRemoteConfig(),
306 : : * nothing happens. If there was a handler for this config, it is
307 : : * removed as well.
308 : : */
309 : : void removeRemoteConfig(const std::string& module_name);
310 : :
311 : : /**
312 : : * Returns the current configuration value for the given module
313 : : * name at the given identifier. See \c ConfigData::getValue() for
314 : : * more details.
315 : : * Raises a ModuleCCSessionError if the module name is unknown
316 : : * Raises a DataNotFoundError if the identifier does not exist
317 : : * in the specification.
318 : : *
319 : : * \param module_name The name of the module to get a config value for
320 : : * \param identifier The identifier of the config value
321 : : * \return The configuration setting at the given identifier
322 : : */
323 : : isc::data::ConstElementPtr getRemoteConfigValue(
324 : : const std::string& module_name,
325 : : const std::string& identifier) const;
326 : :
327 : : /**
328 : : * Send a message to the underlying CC session.
329 : : * This has the same interface as isc::cc::Session::group_sendmsg()
330 : : *
331 : : * \param msg see isc::cc::Session::group_sendmsg()
332 : : * \param group see isc::cc::Session::group_sendmsg()
333 : : * \param instance see isc::cc::Session::group_sendmsg()
334 : : * \param to see isc::cc::Session::group_sendmsg()
335 : : * \return see isc::cc::Session::group_sendmsg()
336 : : */
337 : : int groupSendMsg(isc::data::ConstElementPtr msg,
338 : : std::string group,
339 : : std::string instance = "*",
340 : : std::string to = "*") {
341 : : return (session_.group_sendmsg(msg, group, instance, to));
342 : : };
343 : :
344 : : /**
345 : : * Receive a message from the underlying CC session.
346 : : * This has the same interface as isc::cc::Session::group_recvmsg()
347 : : *
348 : : * \param envelope see isc::cc::Session::group_recvmsg()
349 : : * \param msg see isc::cc::Session::group_recvmsg()
350 : : * \param nonblock see isc::cc::Session::group_recvmsg()
351 : : * \param seq see isc::cc::Session::group_recvmsg()
352 : : * \return see isc::cc::Session::group_recvmsg()
353 : : */
354 : : bool groupRecvMsg(isc::data::ConstElementPtr& envelope,
355 : : isc::data::ConstElementPtr& msg,
356 : : bool nonblock = true,
357 : : int seq = -1) {
358 : : return (session_.group_recvmsg(envelope, msg, nonblock, seq));
359 : : };
360 : :
361 : : private:
362 : : ModuleSpec readModuleSpecification(const std::string& filename);
363 : : void startCheck();
364 : : void sendStopping();
365 : :
366 : : bool started_;
367 : : std::string module_name_;
368 : : isc::cc::AbstractSession& session_;
369 : : ModuleSpec module_specification_;
370 : : isc::data::ConstElementPtr handleConfigUpdate(
371 : : isc::data::ConstElementPtr new_config);
372 : :
373 : : isc::data::ConstElementPtr checkConfigUpdateCommand(
374 : : const std::string& target_module,
375 : : isc::data::ConstElementPtr arg);
376 : :
377 : : isc::data::ConstElementPtr checkModuleCommand(
378 : : const std::string& cmd_str,
379 : : const std::string& target_module,
380 : : isc::data::ConstElementPtr arg) const;
381 : :
382 : : isc::data::ConstElementPtr(*config_handler_)(
383 : : isc::data::ConstElementPtr new_config);
384 : : isc::data::ConstElementPtr(*command_handler_)(
385 : : const std::string& command,
386 : : isc::data::ConstElementPtr args);
387 : :
388 : : typedef void (*RemoteHandler)(const std::string&,
389 : : isc::data::ConstElementPtr,
390 : : const ConfigData&);
391 : : std::map<std::string, ConfigData> remote_module_configs_;
392 : : std::map<std::string, RemoteHandler> remote_module_handlers_;
393 : :
394 : : void updateRemoteConfig(const std::string& module_name,
395 : : isc::data::ConstElementPtr new_config);
396 : :
397 : : ModuleSpec fetchRemoteSpec(const std::string& module, bool is_filename);
398 : : };
399 : :
400 : : /// \brief Default handler for logging config updates
401 : : ///
402 : : /// When CCSession is initialized with handle_logging set to true,
403 : : /// this callback will be used to update the logger when a configuration
404 : : /// change comes in.
405 : : ///
406 : : /// This function updates the (global) loggers by initializing a
407 : : /// LoggerManager and passing the settings as specified in the given
408 : : /// configuration update.
409 : : ///
410 : : /// \param module_name The name of the module
411 : : /// \param new_config The modified configuration values
412 : : /// \param config_data The full config data for the (remote) logging
413 : : /// module.
414 : : void
415 : : default_logconfig_handler(const std::string& module_name,
416 : : isc::data::ConstElementPtr new_config,
417 : : const ConfigData& config_data);
418 : :
419 : :
420 : : /// \brief Returns the loggers related to this module
421 : : ///
422 : : /// This function does two things;
423 : : /// - it drops the configuration parts for loggers for other modules.
424 : : /// - it replaces the '*' in the name of the loggers by the name of
425 : : /// this module, but *only* if the expanded name is not configured
426 : : /// explicitly.
427 : : ///
428 : : /// Examples: if this is the module b10-resolver,
429 : : /// For the config names ['*', 'b10-auth']
430 : : /// The '*' is replaced with 'b10-resolver', and this logger is used.
431 : : /// 'b10-auth' is ignored (of course, it will not be in the b10-auth
432 : : /// module).
433 : : ///
434 : : /// For ['*', 'b10-resolver']
435 : : /// The '*' is ignored, and only 'b10-resolver' is used.
436 : : ///
437 : : /// For ['*.reslib', 'b10-resolver']
438 : : /// Or ['b10-resolver.reslib', '*']
439 : : /// Both are used, where the * will be expanded to b10-resolver
440 : : ///
441 : : /// \note This is a public function at this time, but mostly for
442 : : /// the purposes of testing. Once we can directly test what loggers
443 : : /// are running, this function may be moved to the unnamed namespace
444 : : ///
445 : : /// \param loggers the original 'loggers' config list
446 : : /// \return ListElement containing only loggers relevant for this
447 : : /// module, where * is replaced by the root logger name
448 : : isc::data::ConstElementPtr
449 : : getRelatedLoggers(isc::data::ConstElementPtr loggers);
450 : :
451 : : } // namespace config
452 : :
453 : : } // namespace isc
454 : : #endif // __CCSESSION_H
455 : :
456 : : // Local Variables:
457 : : // mode: c++
458 : : // End:
|