module_spec.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. // Copyright (C) 2010 Internet Systems Consortium.
  2. //
  3. // Permission to use, copy, modify, and 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 INTERNET SYSTEMS CONSORTIUM
  8. // DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
  9. // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
  10. // INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
  11. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
  12. // FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  13. // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
  14. // WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. #include <config/module_spec.h>
  16. #include <sstream>
  17. #include <iostream>
  18. #include <fstream>
  19. #include <cerrno>
  20. #include <boost/foreach.hpp>
  21. // todo: add more context to thrown ModuleSpecErrors?
  22. using namespace isc::data;
  23. using namespace isc::config;
  24. namespace {
  25. //
  26. // Private functions
  27. //
  28. void
  29. check_leaf_item(ConstElementPtr spec, const std::string& name,
  30. Element::types type, bool mandatory)
  31. {
  32. if (spec->contains(name)) {
  33. if (spec->get(name)->getType() == type) {
  34. return;
  35. } else {
  36. throw ModuleSpecError(name + " not of type " + Element::typeToName(type));
  37. }
  38. } else if (mandatory) {
  39. // todo: want parent item name, and perhaps some info about location
  40. // in list? or just catch and throw new...
  41. // or make this part non-throwing and check return value...
  42. throw ModuleSpecError(name + " missing in " + spec->str());
  43. }
  44. }
  45. void check_config_item_list(ConstElementPtr spec);
  46. void
  47. check_config_item(ConstElementPtr spec) {
  48. check_leaf_item(spec, "item_name", Element::string, true);
  49. check_leaf_item(spec, "item_type", Element::string, true);
  50. check_leaf_item(spec, "item_optional", Element::boolean, true);
  51. check_leaf_item(spec, "item_default",
  52. Element::nameToType(spec->get("item_type")->stringValue()),
  53. !spec->get("item_optional")->boolValue()
  54. );
  55. // if list, check the list specification
  56. if (Element::nameToType(spec->get("item_type")->stringValue()) == Element::list) {
  57. check_leaf_item(spec, "list_item_spec", Element::map, true);
  58. check_config_item(spec->get("list_item_spec"));
  59. }
  60. // todo: add stuff for type map
  61. if (Element::nameToType(spec->get("item_type")->stringValue()) == Element::map) {
  62. check_leaf_item(spec, "map_item_spec", Element::list, true);
  63. check_config_item_list(spec->get("map_item_spec"));
  64. }
  65. }
  66. void
  67. check_config_item_list(ConstElementPtr spec) {
  68. if (spec->getType() != Element::list) {
  69. throw ModuleSpecError("config_data is not a list of elements");
  70. }
  71. BOOST_FOREACH(ConstElementPtr item, spec->listValue()) {
  72. check_config_item(item);
  73. }
  74. }
  75. void
  76. check_command(ConstElementPtr spec) {
  77. check_leaf_item(spec, "command_name", Element::string, true);
  78. check_leaf_item(spec, "command_args", Element::list, true);
  79. check_config_item_list(spec->get("command_args"));
  80. }
  81. void
  82. check_command_list(ConstElementPtr spec) {
  83. if (spec->getType() != Element::list) {
  84. throw ModuleSpecError("commands is not a list of elements");
  85. }
  86. BOOST_FOREACH(ConstElementPtr item, spec->listValue()) {
  87. check_command(item);
  88. }
  89. }
  90. void
  91. check_data_specification(ConstElementPtr spec) {
  92. check_leaf_item(spec, "module_name", Element::string, true);
  93. check_leaf_item(spec, "module_description", Element::string, false);
  94. // config_data is not mandatory; module could just define
  95. // commands and have no config
  96. if (spec->contains("config_data")) {
  97. check_config_item_list(spec->get("config_data"));
  98. }
  99. if (spec->contains("commands")) {
  100. check_command_list(spec->get("commands"));
  101. }
  102. }
  103. // checks whether the given element is a valid module specification
  104. // throws a ModuleSpecError if the specification is bad
  105. void
  106. check_module_specification(ConstElementPtr def) {
  107. try {
  108. check_data_specification(def);
  109. } catch (TypeError te) {
  110. throw ModuleSpecError(te.what());
  111. }
  112. }
  113. }
  114. namespace isc {
  115. namespace config {
  116. //
  117. // Public functions
  118. //
  119. ModuleSpec::ModuleSpec(ConstElementPtr module_spec_element,
  120. const bool check)
  121. throw(ModuleSpecError)
  122. {
  123. module_specification = module_spec_element;
  124. if (check) {
  125. check_module_specification(module_specification);
  126. }
  127. }
  128. ConstElementPtr
  129. ModuleSpec::getCommandsSpec() const {
  130. if (module_specification->contains("commands")) {
  131. return (module_specification->get("commands"));
  132. } else {
  133. return (ElementPtr());
  134. }
  135. }
  136. ConstElementPtr
  137. ModuleSpec::getConfigSpec() const {
  138. if (module_specification->contains("config_data")) {
  139. return (module_specification->get("config_data"));
  140. } else {
  141. return (ElementPtr());
  142. }
  143. }
  144. const std::string
  145. ModuleSpec::getModuleName() const {
  146. return (module_specification->get("module_name")->stringValue());
  147. }
  148. const std::string
  149. ModuleSpec::getModuleDescription() const {
  150. if (module_specification->contains("module_description")) {
  151. return (module_specification->get("module_description")->stringValue());
  152. } else {
  153. return (std::string(""));
  154. }
  155. }
  156. bool
  157. ModuleSpec::validate_config(ConstElementPtr data, const bool full) const {
  158. ConstElementPtr spec = module_specification->find("config_data");
  159. return (validate_spec_list(spec, data, full, ElementPtr()));
  160. }
  161. bool
  162. ModuleSpec::validate_command(const std::string& command,
  163. ConstElementPtr args,
  164. ElementPtr errors) {
  165. ConstElementPtr commands_spec = module_specification->find("commands");
  166. if (args->getType() != Element::map) {
  167. errors->add(Element::create("args for command " + command + " is not a map"));
  168. return (false);
  169. }
  170. if (!commands_spec) {
  171. // there are no commands according to the spec.
  172. errors->add(Element::create("The given module has no commands"));
  173. return (false);
  174. }
  175. BOOST_FOREACH(ConstElementPtr cur_command, commands_spec->listValue()) {
  176. if (cur_command->get("command_name")->stringValue() == command) {
  177. return (validate_spec_list(cur_command->get("command_args"), args, true, errors));
  178. }
  179. }
  180. // this command is unknown
  181. errors->add(Element::create("Unknown command " + command));
  182. return (false);
  183. }
  184. bool
  185. ModuleSpec::validate_config(ConstElementPtr data, const bool full,
  186. ElementPtr errors) const
  187. {
  188. ConstElementPtr spec = module_specification->find("config_data");
  189. return (validate_spec_list(spec, data, full, errors));
  190. }
  191. ModuleSpec
  192. moduleSpecFromFile(const std::string& file_name, const bool check)
  193. throw(JSONError, ModuleSpecError)
  194. {
  195. std::ifstream file;
  196. file.open(file_name.c_str());
  197. if (!file) {
  198. std::stringstream errs;
  199. errs << "Error opening " << file_name << ": " << strerror(errno);
  200. throw ModuleSpecError(errs.str());
  201. }
  202. ConstElementPtr module_spec_element = Element::fromJSON(file, file_name);
  203. if (module_spec_element->contains("module_spec")) {
  204. return (ModuleSpec(module_spec_element->get("module_spec"), check));
  205. } else {
  206. throw ModuleSpecError("No module_spec in specification");
  207. }
  208. }
  209. ModuleSpec
  210. moduleSpecFromFile(std::ifstream& in, const bool check)
  211. throw(JSONError, ModuleSpecError)
  212. {
  213. ConstElementPtr module_spec_element = Element::fromJSON(in);
  214. if (module_spec_element->contains("module_spec")) {
  215. return (ModuleSpec(module_spec_element->get("module_spec"), check));
  216. } else {
  217. throw ModuleSpecError("No module_spec in specification");
  218. }
  219. }
  220. namespace {
  221. //
  222. // private functions
  223. //
  224. //
  225. // helper functions for validation
  226. //
  227. bool
  228. check_type(ConstElementPtr spec, ConstElementPtr element) {
  229. std::string cur_item_type;
  230. cur_item_type = spec->get("item_type")->stringValue();
  231. if (cur_item_type == "any") {
  232. return (true);
  233. }
  234. switch (element->getType()) {
  235. case Element::integer:
  236. return (cur_item_type == "integer");
  237. break;
  238. case Element::real:
  239. return (cur_item_type == "real");
  240. break;
  241. case Element::boolean:
  242. return (cur_item_type == "boolean");
  243. break;
  244. case Element::string:
  245. return (cur_item_type == "string");
  246. break;
  247. case Element::list:
  248. return (cur_item_type == "list");
  249. break;
  250. case Element::map:
  251. return (cur_item_type == "map");
  252. break;
  253. }
  254. return (false);
  255. }
  256. }
  257. bool
  258. ModuleSpec::validate_item(ConstElementPtr spec, ConstElementPtr data,
  259. const bool full, ElementPtr errors) const
  260. {
  261. if (!check_type(spec, data)) {
  262. // we should do some proper error feedback here
  263. // std::cout << "type mismatch; not " << spec->get("item_type") << ": " << data << std::endl;
  264. // std::cout << spec << std::endl;
  265. if (errors) {
  266. errors->add(Element::create("Type mismatch"));
  267. }
  268. return (false);
  269. }
  270. if (data->getType() == Element::list) {
  271. ConstElementPtr list_spec = spec->get("list_item_spec");
  272. BOOST_FOREACH(ConstElementPtr list_el, data->listValue()) {
  273. if (!check_type(list_spec, list_el)) {
  274. if (errors) {
  275. errors->add(Element::create("Type mismatch"));
  276. }
  277. return (false);
  278. }
  279. if (list_spec->get("item_type")->stringValue() == "map") {
  280. if (!validate_item(list_spec, list_el, full, errors)) {
  281. return (false);
  282. }
  283. }
  284. }
  285. }
  286. if (data->getType() == Element::map) {
  287. if (!validate_spec_list(spec->get("map_item_spec"), data, full, errors)) {
  288. return (false);
  289. }
  290. }
  291. return (true);
  292. }
  293. // spec is a map with item_name etc, data is a map
  294. bool
  295. ModuleSpec::validate_spec(ConstElementPtr spec, ConstElementPtr data,
  296. const bool full, ElementPtr errors) const
  297. {
  298. std::string item_name = spec->get("item_name")->stringValue();
  299. bool optional = spec->get("item_optional")->boolValue();
  300. ConstElementPtr data_el;
  301. data_el = data->get(item_name);
  302. if (data_el) {
  303. if (!validate_item(spec, data_el, full, errors)) {
  304. return (false);
  305. }
  306. } else {
  307. if (!optional && full) {
  308. if (errors) {
  309. errors->add(Element::create("Non-optional value missing"));
  310. }
  311. return (false);
  312. }
  313. }
  314. return (true);
  315. }
  316. // spec is a list of maps, data is a map
  317. bool
  318. ModuleSpec::validate_spec_list(ConstElementPtr spec, ConstElementPtr data,
  319. const bool full, ElementPtr errors) const
  320. {
  321. bool validated = true;
  322. std::string cur_item_name;
  323. BOOST_FOREACH(ConstElementPtr cur_spec_el, spec->listValue()) {
  324. if (!validate_spec(cur_spec_el, data, full, errors)) {
  325. validated = false;
  326. }
  327. }
  328. typedef std::pair<std::string, ConstElementPtr> maptype;
  329. BOOST_FOREACH(maptype m, data->mapValue()) {
  330. bool found = false;
  331. BOOST_FOREACH(ConstElementPtr cur_spec_el, spec->listValue()) {
  332. if (cur_spec_el->get("item_name")->stringValue().compare(m.first) == 0) {
  333. found = true;
  334. }
  335. }
  336. if (!found) {
  337. validated = false;
  338. if (errors) {
  339. errors->add(Element::create("Unknown item " + m.first));
  340. }
  341. }
  342. }
  343. return (validated);
  344. }
  345. }
  346. }