ccsession.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  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. */
  160. ModuleCCSession(const std::string& spec_file_name,
  161. isc::cc::AbstractSession& session,
  162. isc::data::ConstElementPtr(*config_handler)(
  163. isc::data::ConstElementPtr new_config) = NULL,
  164. isc::data::ConstElementPtr(*command_handler)(
  165. const std::string& command,
  166. isc::data::ConstElementPtr args) = NULL
  167. );
  168. /**
  169. * Optional optimization for checkCommand loop; returns true
  170. * if there are unhandled queued messages in the cc session.
  171. * (if either this is true or there is data on the socket found
  172. * by the select() call on getSocket(), run checkCommand())
  173. *
  174. * @return true if there are unhandled queued messages
  175. */
  176. bool hasQueuedMsgs() const;
  177. /**
  178. * Check if there is a command or config change on the command
  179. * session. If so, the appropriate handler is called if set.
  180. * If not set, a default answer is returned.
  181. * This is a non-blocking read; if there is nothing this function
  182. * will return 0.
  183. */
  184. int checkCommand();
  185. /**
  186. * The config handler function should expect an ElementPtr containing
  187. * the full configuration where non-default values have been set.
  188. * Later we might want to think about more granular control
  189. * (i.e. this does not scale to for instance lists containing
  190. * 100000 zones, where the whole list is passed every time a single
  191. * thing changes)
  192. */
  193. void setConfigHandler(isc::data::ConstElementPtr(*config_handler)(
  194. isc::data::ConstElementPtr new_config))
  195. {
  196. config_handler_ = config_handler;
  197. }
  198. /**
  199. * Set a command handler; the function that is passed takes an
  200. * ElementPtr, pointing to a list element, containing
  201. * [ module_name, command_name, arg1, arg2, ... ]
  202. * The returned ElementPtr should look like
  203. * { "result": [ return_value, result_value ] }
  204. * result value here is optional and depends on the command
  205. *
  206. * This protocol is very likely to change.
  207. */
  208. void setCommandHandler(isc::data::ConstElementPtr(*command_handler)(
  209. const std::string& command,
  210. isc::data::ConstElementPtr args))
  211. {
  212. command_handler_ = command_handler;
  213. }
  214. /**
  215. * Gives access to the configuration values of a different module
  216. * Once this function has been called with the name of the specification
  217. * file of the module you want the configuration of, you can use
  218. * \c getRemoteConfigValue() to get a specific setting.
  219. * Changes are automatically updated, but you cannot specify handlers
  220. * for those changes, must use \c getRemoteConfigValue() to get a value
  221. * This function will subscribe to the relevant module channel.
  222. *
  223. * \param spec_file_name The path to the specification file of
  224. * the module we want to have configuration
  225. * values from
  226. * \return The name of the module specified in the given specification
  227. * file
  228. */
  229. std::string addRemoteConfig(const std::string& spec_file_name);
  230. /**
  231. * Removes the module with the given name from the remote config
  232. * settings. If the module was not added with \c addRemoteConfig(),
  233. * nothing happens.
  234. */
  235. void removeRemoteConfig(const std::string& module_name);
  236. /**
  237. * Returns the current configuration value for the given module
  238. * name at the given identifier. See \c ConfigData::getValue() for
  239. * more details.
  240. * Raises a ModuleCCSessionError if the module name is unknown
  241. * Raises a DataNotFoundError if the identifier does not exist
  242. * in the specification.
  243. *
  244. * \param module_name The name of the module to get a config value for
  245. * \param identifier The identifier of the config value
  246. * \return The configuration setting at the given identifier
  247. */
  248. isc::data::ConstElementPtr getRemoteConfigValue(
  249. const std::string& module_name,
  250. const std::string& identifier) const;
  251. private:
  252. ModuleSpec readModuleSpecification(const std::string& filename);
  253. void startCheck();
  254. std::string module_name_;
  255. isc::cc::AbstractSession& session_;
  256. ModuleSpec module_specification_;
  257. isc::data::ConstElementPtr handleConfigUpdate(
  258. isc::data::ConstElementPtr new_config);
  259. isc::data::ConstElementPtr checkConfigUpdateCommand(
  260. const std::string& target_module,
  261. isc::data::ConstElementPtr arg);
  262. isc::data::ConstElementPtr checkModuleCommand(
  263. const std::string& cmd_str,
  264. const std::string& target_module,
  265. isc::data::ConstElementPtr arg) const;
  266. isc::data::ConstElementPtr(*config_handler_)(
  267. isc::data::ConstElementPtr new_config);
  268. isc::data::ConstElementPtr(*command_handler_)(
  269. const std::string& command,
  270. isc::data::ConstElementPtr args);
  271. std::map<std::string, ConfigData> remote_module_configs_;
  272. void updateRemoteConfig(const std::string& module_name,
  273. isc::data::ConstElementPtr new_config);
  274. };
  275. }
  276. }
  277. #endif // __CCSESSION_H
  278. // Local Variables:
  279. // mode: c++
  280. // End: