ccsession.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  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. #ifndef __CCSESSION_H
  15. #define __CCSESSION_H 1
  16. #include <string>
  17. #include <config/config_data.h>
  18. #include <config/module_spec.h>
  19. #include <cc/session.h>
  20. #include <cc/data.h>
  21. namespace isc {
  22. namespace config {
  23. ///
  24. /// \brief Creates a standard config/command level success answer message
  25. /// (i.e. of the form { "result": [ 0 ] }
  26. /// \return Standard command/config success answer message
  27. isc::data::ConstElementPtr createAnswer();
  28. ///
  29. /// \brief Creates a standard config/command level answer message
  30. /// (i.e. of the form { "result": [ rcode, arg ] }
  31. /// If rcode != 0, arg must be a StringElement
  32. ///
  33. /// \param rcode The return code (0 for success)
  34. /// \param arg For rcode == 0, this is an optional argument of any
  35. /// Element type. For rcode == 1, this argument is mandatory,
  36. /// and must be a StringElement containing an error description
  37. /// \return Standard command/config answer message
  38. isc::data::ConstElementPtr createAnswer(const int rcode,
  39. isc::data::ConstElementPtr arg);
  40. ///
  41. /// \brief Creates a standard config/command level answer message
  42. /// (i.e. of the form { "result": [ rcode, arg ] }
  43. ///
  44. /// \param rcode The return code (0 for success)
  45. /// \param arg A string to put into the StringElement argument
  46. /// \return Standard command/config answer message
  47. isc::data::ConstElementPtr createAnswer(const int rcode,
  48. const std::string& arg);
  49. ///
  50. /// Parses a standard config/command level answer message
  51. ///
  52. /// \param rcode This value will be set to the return code contained in
  53. /// the message
  54. /// \param msg The message to parse
  55. /// \return The optional argument in the message, or an empty ElementPtr
  56. /// if there was no argument. If rcode != 0, this contains a
  57. /// StringElement with the error description.
  58. isc::data::ConstElementPtr parseAnswer(int &rcode,
  59. isc::data::ConstElementPtr msg);
  60. ///
  61. /// \brief Creates a standard config/command command message with no
  62. /// argument (of the form { "command": [ "my_command" ] }
  63. ///
  64. /// \param command The command string
  65. /// \return The created message
  66. isc::data::ConstElementPtr createCommand(const std::string& command);
  67. ///
  68. /// \brief Creates a standard config/command command message with the
  69. /// given argument (of the form { "command": [ "my_command", arg ] }
  70. ///
  71. /// \param command The command string
  72. /// \param arg The optional argument for the command. This can be of
  73. /// any Element type, but it should conform to the .spec file.
  74. /// \return The created message
  75. isc::data::ConstElementPtr createCommand(const std::string& command,
  76. isc::data::ConstElementPtr arg);
  77. ///
  78. /// \brief Parses the given command into a string containing the actual
  79. /// command and an ElementPtr containing the optional argument.
  80. ///
  81. /// Raises a CCSessionError if this is not a well-formed command
  82. ///
  83. /// Example code: (command_message is a ConstElementPtr that is
  84. /// passed here)
  85. /// \code
  86. /// ElementPtr command_message = Element::fromJSON("{ \"command\": [\"foo\", { \"bar\": 123 } ] }");
  87. /// try {
  88. /// ConstElementPtr args;
  89. /// std::string command_str = parseCommand(args, command_message);
  90. /// if (command_str == "foo") {
  91. /// std::cout << "The command 'foo' was given" << std::endl;
  92. /// if (args->contains("bar")) {
  93. /// std::cout << "It had argument name 'bar' set, which has"
  94. /// << "value "
  95. /// << args->get("bar")->intValue();
  96. /// }
  97. /// } else {
  98. /// std::cout << "Unknown command '" << command_str << std::endl;
  99. /// }
  100. /// } catch (CCSessionError cse) {
  101. /// std::cerr << "Bad command in CC Session: "
  102. /// << cse.what() << std::endl;
  103. /// }
  104. /// \endcode
  105. ///
  106. /// \param arg This value will be set to the ElementPtr pointing to
  107. /// the argument, or to an empty Map (ElementPtr) if there was none.
  108. /// \param command The command message containing the command (as made
  109. /// by createCommand()
  110. /// \return The command name
  111. std::string parseCommand(isc::data::ConstElementPtr& arg,
  112. isc::data::ConstElementPtr command);
  113. ///
  114. /// \brief A standard cc session exception that is thrown if a function
  115. /// is there is a problem with one of the messages
  116. ///
  117. // todo: include types and called function in the exception
  118. class CCSessionError : public isc::Exception {
  119. public:
  120. CCSessionError(const char* file, size_t line, const char* what) :
  121. isc::Exception(file, line, what) {}
  122. };
  123. ///
  124. /// \brief This exception is thrown if the constructor fails
  125. ///
  126. class CCSessionInitError : public isc::Exception {
  127. public:
  128. CCSessionInitError(const char* file, size_t line, const char* what) :
  129. isc::Exception(file, line, what) {}
  130. };
  131. ///
  132. /// \brief This module keeps a connection to the command channel,
  133. /// holds configuration information, and handles messages from
  134. /// the command channel
  135. ///
  136. class ModuleCCSession : public ConfigData {
  137. public:
  138. /**
  139. * Initialize a config/command session
  140. *
  141. * @param spec_file_name The name of the file containing the
  142. * module specification.
  143. * @param session A Session object over which configuration and command
  144. * data are exchanged.
  145. * @param config_handler A callback function pointer to be called when
  146. * configuration of the local module needs to be updated.
  147. * This must refer to a valid object of a concrete derived class of
  148. * AbstractSession without establishing the session.
  149. *
  150. * Note: the design decision on who is responsible for establishing the
  151. * session is in flux, and may change in near future.
  152. *
  153. * \exception CCSessionInitError when the initialization fails,
  154. * either because the file cannot be read or there is
  155. * a communication problem with the config manager.
  156. *
  157. * @param command_handler A callback function pointer to be called when
  158. * a control command from a remote agent needs to be performed on the
  159. * local module.
  160. * @param start_immediately If true (default), start listening to new commands
  161. * and configuration changes asynchronously at the end of the constructor;
  162. * if false, it will be delayed until the start() method is explicitly
  163. * called. (This is a short term workaround for an initialization trouble.
  164. * We'll need to develop a cleaner solution, and then remove this knob)
  165. * @param handle_logging If true, the ModuleCCSession will automatically
  166. * take care of logging configuration through the virtual Logging config
  167. * module.
  168. */
  169. ModuleCCSession(const std::string& spec_file_name,
  170. isc::cc::AbstractSession& session,
  171. isc::data::ConstElementPtr(*config_handler)(
  172. isc::data::ConstElementPtr new_config) = NULL,
  173. isc::data::ConstElementPtr(*command_handler)(
  174. const std::string& command,
  175. isc::data::ConstElementPtr args) = NULL,
  176. bool start_immediately = true,
  177. bool handle_logging = false
  178. );
  179. /// Start receiving new commands and configuration changes asynchronously.
  180. ///
  181. /// This method must be called only once, and only when the ModuleCCSession
  182. /// was constructed with start_immediately being false. Otherwise
  183. /// CCSessionError will be thrown.
  184. ///
  185. /// As noted in the constructor, this method should be considered a short
  186. /// term workaround and will be removed in future.
  187. void start();
  188. /**
  189. * Optional optimization for checkCommand loop; returns true
  190. * if there are unhandled queued messages in the cc session.
  191. * (if either this is true or there is data on the socket found
  192. * by the select() call on getSocket(), run checkCommand())
  193. *
  194. * @return true if there are unhandled queued messages
  195. */
  196. bool hasQueuedMsgs() const;
  197. /**
  198. * Check if there is a command or config change on the command
  199. * session. If so, the appropriate handler is called if set.
  200. * If not set, a default answer is returned.
  201. * This is a non-blocking read; if there is nothing this function
  202. * will return 0.
  203. */
  204. int checkCommand();
  205. /**
  206. * The config handler function should expect an ElementPtr containing
  207. * the full configuration where non-default values have been set.
  208. * Later we might want to think about more granular control
  209. * (i.e. this does not scale to for instance lists containing
  210. * 100000 zones, where the whole list is passed every time a single
  211. * thing changes)
  212. */
  213. void setConfigHandler(isc::data::ConstElementPtr(*config_handler)(
  214. isc::data::ConstElementPtr new_config))
  215. {
  216. config_handler_ = config_handler;
  217. }
  218. /**
  219. * Set a command handler; the function that is passed takes an
  220. * ElementPtr, pointing to a list element, containing
  221. * [ module_name, command_name, arg1, arg2, ... ]
  222. * The returned ElementPtr should look like
  223. * { "result": [ return_value, result_value ] }
  224. * result value here is optional and depends on the command
  225. *
  226. * This protocol is very likely to change.
  227. */
  228. void setCommandHandler(isc::data::ConstElementPtr(*command_handler)(
  229. const std::string& command,
  230. isc::data::ConstElementPtr args))
  231. {
  232. command_handler_ = command_handler;
  233. }
  234. /**
  235. * Gives access to the configuration values of a different module
  236. * Once this function has been called with the name of the specification
  237. * file or the module you want the configuration of, you can use
  238. * \c getRemoteConfigValue() to get a specific setting.
  239. * Changes are automatically updated, and you can specify handlers
  240. * for those changes. This function will subscribe to the relevant module
  241. * channel.
  242. *
  243. * This method must be called before calling the \c start() method on the
  244. * ModuleCCSession (it also implies the ModuleCCSession must have been
  245. * constructed with start_immediately being false).
  246. *
  247. * \param spec_name This specifies the module to add. It is either a
  248. * filename of the spec file to use or a name of module
  249. * (in case it's a module name, the spec data is
  250. * downloaded from the configuration manager, therefore
  251. * the configuration manager must know it). If
  252. * spec_is_filename is true (the default), then a
  253. * filename is assumed, otherwise a module name.
  254. * \param handler The handler function called whenever there's a change.
  255. * Called once initally from this function. May be NULL
  256. * if you don't want any handler to be called and you're
  257. * fine with requesting the data through
  258. * getRemoteConfigValue() each time.
  259. *
  260. * The handler should not throw, or it'll fall trough and
  261. * the exception will get into strange places, probably
  262. * aborting the application.
  263. * \param spec_is_filename Says if spec_name is filename or module name.
  264. * \return The name of the module specified in the given specification
  265. * file
  266. */
  267. std::string addRemoteConfig(const std::string& spec_name,
  268. void (*handler)(const std::string& module_name,
  269. isc::data::ConstElementPtr
  270. update,
  271. const ConfigData& config_data) = NULL,
  272. bool spec_is_filename = true);
  273. /**
  274. * Removes the module with the given name from the remote config
  275. * settings. If the module was not added with \c addRemoteConfig(),
  276. * nothing happens. If there was a handler for this config, it is
  277. * removed as well.
  278. */
  279. void removeRemoteConfig(const std::string& module_name);
  280. /**
  281. * Returns the current configuration value for the given module
  282. * name at the given identifier. See \c ConfigData::getValue() for
  283. * more details.
  284. * Raises a ModuleCCSessionError if the module name is unknown
  285. * Raises a DataNotFoundError if the identifier does not exist
  286. * in the specification.
  287. *
  288. * \param module_name The name of the module to get a config value for
  289. * \param identifier The identifier of the config value
  290. * \return The configuration setting at the given identifier
  291. */
  292. isc::data::ConstElementPtr getRemoteConfigValue(
  293. const std::string& module_name,
  294. const std::string& identifier) const;
  295. private:
  296. ModuleSpec readModuleSpecification(const std::string& filename);
  297. void startCheck();
  298. bool started_;
  299. std::string module_name_;
  300. isc::cc::AbstractSession& session_;
  301. ModuleSpec module_specification_;
  302. isc::data::ConstElementPtr handleConfigUpdate(
  303. isc::data::ConstElementPtr new_config);
  304. isc::data::ConstElementPtr checkConfigUpdateCommand(
  305. const std::string& target_module,
  306. isc::data::ConstElementPtr arg);
  307. isc::data::ConstElementPtr checkModuleCommand(
  308. const std::string& cmd_str,
  309. const std::string& target_module,
  310. isc::data::ConstElementPtr arg) const;
  311. isc::data::ConstElementPtr(*config_handler_)(
  312. isc::data::ConstElementPtr new_config);
  313. isc::data::ConstElementPtr(*command_handler_)(
  314. const std::string& command,
  315. isc::data::ConstElementPtr args);
  316. typedef void (*RemoteHandler)(const std::string&,
  317. isc::data::ConstElementPtr,
  318. const ConfigData&);
  319. std::map<std::string, ConfigData> remote_module_configs_;
  320. std::map<std::string, RemoteHandler> remote_module_handlers_;
  321. void updateRemoteConfig(const std::string& module_name,
  322. isc::data::ConstElementPtr new_config);
  323. ModuleSpec fetchRemoteSpec(const std::string& module, bool is_filename);
  324. };
  325. /// \brief Default handler for logging config updates
  326. ///
  327. /// When CCSession is initialized with handle_logging set to true,
  328. /// this callback will be used to update the logger when a configuration
  329. /// change comes in.
  330. ///
  331. /// This function updates the (global) loggers by initializing a
  332. /// LoggerManager and passing the settings as specified in the given
  333. /// configuration update.
  334. ///
  335. /// \param module_name The name of the module
  336. /// \param new_config The modified configuration values
  337. /// \param config_data The full config data for the (remote) logging
  338. /// module.
  339. void
  340. default_logconfig_handler(const std::string& module_name,
  341. isc::data::ConstElementPtr new_config,
  342. const ConfigData& config_data);
  343. }
  344. }
  345. #endif // __CCSESSION_H
  346. // Local Variables:
  347. // mode: c++
  348. // End: