ccsession.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  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. * Note: the design decision on who is responsible for establishing the
  150. * session is in flux, and may change in near future.
  151. *
  152. * \exception CCSessionInitError when the initialization fails,
  153. * either because the file cannot be read or there is
  154. * a communication problem with the config manager.
  155. *
  156. * @param command_handler A callback function pointer to be called when
  157. * a control command from a remote agent needs to be performed on the
  158. * local module.
  159. * @start_immediately If true (default), start listening to new commands
  160. * and configuration changes asynchronously at the end of the constructor;
  161. * if false, it will be delayed until the start() method is explicitly
  162. * called. (This is a short term workaround for an initialization trouble.
  163. * We'll need to develop a cleaner solution, and then remove this knob)
  164. */
  165. ModuleCCSession(const std::string& spec_file_name,
  166. isc::cc::AbstractSession& session,
  167. isc::data::ConstElementPtr(*config_handler)(
  168. isc::data::ConstElementPtr new_config) = NULL,
  169. isc::data::ConstElementPtr(*command_handler)(
  170. const std::string& command,
  171. isc::data::ConstElementPtr args) = NULL,
  172. bool start_immediately = true
  173. );
  174. /// Start asynchronous receiving new commands and configuration changes
  175. /// asynchronously.
  176. ///
  177. /// This method must be called only once, and only when the ModuleCCSession
  178. /// was constructed with start_immediately being false. Otherwise
  179. /// CCSessionError will be thrown.
  180. ///
  181. /// As noted in the constructor, this method should be considered a short
  182. /// term workaround and will be removed in future.
  183. void start();
  184. /**
  185. * Optional optimization for checkCommand loop; returns true
  186. * if there are unhandled queued messages in the cc session.
  187. * (if either this is true or there is data on the socket found
  188. * by the select() call on getSocket(), run checkCommand())
  189. *
  190. * @return true if there are unhandled queued messages
  191. */
  192. bool hasQueuedMsgs() const;
  193. /**
  194. * Check if there is a command or config change on the command
  195. * session. If so, the appropriate handler is called if set.
  196. * If not set, a default answer is returned.
  197. * This is a non-blocking read; if there is nothing this function
  198. * will return 0.
  199. */
  200. int checkCommand();
  201. /**
  202. * The config handler function should expect an ElementPtr containing
  203. * the full configuration where non-default values have been set.
  204. * Later we might want to think about more granular control
  205. * (i.e. this does not scale to for instance lists containing
  206. * 100000 zones, where the whole list is passed every time a single
  207. * thing changes)
  208. */
  209. void setConfigHandler(isc::data::ConstElementPtr(*config_handler)(
  210. isc::data::ConstElementPtr new_config))
  211. {
  212. config_handler_ = config_handler;
  213. }
  214. /**
  215. * Set a command handler; the function that is passed takes an
  216. * ElementPtr, pointing to a list element, containing
  217. * [ module_name, command_name, arg1, arg2, ... ]
  218. * The returned ElementPtr should look like
  219. * { "result": [ return_value, result_value ] }
  220. * result value here is optional and depends on the command
  221. *
  222. * This protocol is very likely to change.
  223. */
  224. void setCommandHandler(isc::data::ConstElementPtr(*command_handler)(
  225. const std::string& command,
  226. isc::data::ConstElementPtr args))
  227. {
  228. command_handler_ = command_handler;
  229. }
  230. /**
  231. * Gives access to the configuration values of a different module
  232. * Once this function has been called with the name of the specification
  233. * file or the module you want the configuration of, you can use
  234. * \c getRemoteConfigValue() to get a specific setting.
  235. * Changes are automatically updated, and you can specify handlers
  236. * for those changes. This function will subscribe to the relevant module
  237. * channel.
  238. *
  239. * \param spec_name This specifies the module to add. It is either a
  240. * filename of the spec file to use or a name of module
  241. * (in case it's a module name, the spec data is
  242. * downloaded from the configuration manager, therefore
  243. * the configuration manager must know it). A heuristic
  244. * is used to guess which should be used - if it contains
  245. * a slash or dot, filename is assumed, otherwise
  246. * name of module is assumed.
  247. * \param handler The handler function called whenever there's a change.
  248. * Called once initally from this function. May be NULL
  249. * if you don't want any handler to be called and you're
  250. * fine with requesting the data through
  251. * getRemoteConfigValue() each time.
  252. * \return The name of the module specified in the given specification
  253. * file
  254. */
  255. std::string addRemoteConfig(const std::string& spec_name,
  256. void (*handler)(const std::string& module_name,
  257. isc::data::ConstElementPtr
  258. update) = NULL);
  259. /**
  260. * Removes the module with the given name from the remote config
  261. * settings. If the module was not added with \c addRemoteConfig(),
  262. * nothing happens. If there was a handler for this config, it is
  263. * removed as well.
  264. */
  265. void removeRemoteConfig(const std::string& module_name);
  266. /**
  267. * Returns the current configuration value for the given module
  268. * name at the given identifier. See \c ConfigData::getValue() for
  269. * more details.
  270. * Raises a ModuleCCSessionError if the module name is unknown
  271. * Raises a DataNotFoundError if the identifier does not exist
  272. * in the specification.
  273. *
  274. * \param module_name The name of the module to get a config value for
  275. * \param identifier The identifier of the config value
  276. * \return The configuration setting at the given identifier
  277. */
  278. isc::data::ConstElementPtr getRemoteConfigValue(
  279. const std::string& module_name,
  280. const std::string& identifier) const;
  281. private:
  282. ModuleSpec readModuleSpecification(const std::string& filename);
  283. void startCheck();
  284. bool started_;
  285. std::string module_name_;
  286. isc::cc::AbstractSession& session_;
  287. ModuleSpec module_specification_;
  288. isc::data::ConstElementPtr handleConfigUpdate(
  289. isc::data::ConstElementPtr new_config);
  290. isc::data::ConstElementPtr checkConfigUpdateCommand(
  291. const std::string& target_module,
  292. isc::data::ConstElementPtr arg);
  293. isc::data::ConstElementPtr checkModuleCommand(
  294. const std::string& cmd_str,
  295. const std::string& target_module,
  296. isc::data::ConstElementPtr arg) const;
  297. isc::data::ConstElementPtr(*config_handler_)(
  298. isc::data::ConstElementPtr new_config);
  299. isc::data::ConstElementPtr(*command_handler_)(
  300. const std::string& command,
  301. isc::data::ConstElementPtr args);
  302. typedef void (*RemoteHandler)(const std::string&,
  303. isc::data::ConstElementPtr);
  304. std::map<std::string, ConfigData> remote_module_configs_;
  305. std::map<std::string, RemoteHandler> remote_module_handlers_;
  306. void updateRemoteConfig(const std::string& module_name,
  307. isc::data::ConstElementPtr new_config);
  308. };
  309. }
  310. }
  311. #endif // __CCSESSION_H
  312. // Local Variables:
  313. // mode: c++
  314. // End: