d_cfg_mgr.cc 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. // Copyright (C) 2013 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. #include <config/ccsession.h>
  15. #include <d2/d2_log.h>
  16. #include <dhcp/libdhcp++.h>
  17. #include <d2/d_cfg_mgr.h>
  18. #include <dhcpsrv/dhcp_parsers.h>
  19. #include <util/encode/hex.h>
  20. #include <util/strutil.h>
  21. #include <boost/foreach.hpp>
  22. #include <boost/lexical_cast.hpp>
  23. #include <boost/algorithm/string.hpp>
  24. #include <limits>
  25. #include <iostream>
  26. #include <vector>
  27. #include <map>
  28. using namespace std;
  29. using namespace isc;
  30. using namespace isc::dhcp;
  31. using namespace isc::data;
  32. using namespace isc::asiolink;
  33. namespace isc {
  34. namespace d2 {
  35. // *********************** DCfgContextBase *************************
  36. DCfgContextBase::DCfgContextBase():
  37. boolean_values_(new BooleanStorage()),
  38. uint32_values_(new Uint32Storage()),
  39. string_values_(new StringStorage()) {
  40. }
  41. DCfgContextBase::DCfgContextBase(const DCfgContextBase& rhs):
  42. boolean_values_(new BooleanStorage(*(rhs.boolean_values_))),
  43. uint32_values_(new Uint32Storage(*(rhs.uint32_values_))),
  44. string_values_(new StringStorage(*(rhs.string_values_))) {
  45. }
  46. void
  47. DCfgContextBase::getParam(const std::string& name, bool& value, bool optional) {
  48. try {
  49. value = boolean_values_->getParam(name);
  50. } catch (DhcpConfigError& ex) {
  51. // If the parameter is not optional, re-throw the exception.
  52. if (!optional) {
  53. throw;
  54. }
  55. }
  56. }
  57. void
  58. DCfgContextBase::getParam(const std::string& name, uint32_t& value,
  59. bool optional) {
  60. try {
  61. value = uint32_values_->getParam(name);
  62. } catch (DhcpConfigError& ex) {
  63. // If the parameter is not optional, re-throw the exception.
  64. if (!optional) {
  65. throw;
  66. }
  67. }
  68. }
  69. void
  70. DCfgContextBase::getParam(const std::string& name, std::string& value,
  71. bool optional) {
  72. try {
  73. value = string_values_->getParam(name);
  74. } catch (DhcpConfigError& ex) {
  75. // If the parameter is not optional, re-throw the exception.
  76. if (!optional) {
  77. throw;
  78. }
  79. }
  80. }
  81. DCfgContextBase::~DCfgContextBase() {
  82. }
  83. // *********************** DCfgMgrBase *************************
  84. DCfgMgrBase::DCfgMgrBase(DCfgContextBasePtr context)
  85. : parse_order_(), context_(context) {
  86. if (!context_) {
  87. isc_throw(DCfgMgrBaseError, "DCfgMgrBase ctor: context cannot be NULL");
  88. }
  89. }
  90. DCfgMgrBase::~DCfgMgrBase() {
  91. }
  92. isc::data::ConstElementPtr
  93. DCfgMgrBase::parseConfig(isc::data::ConstElementPtr config_set) {
  94. LOG_DEBUG(dctl_logger, DBGLVL_COMMAND,
  95. DCTL_CONFIG_START).arg(config_set->str());
  96. if (!config_set) {
  97. return (isc::config::createAnswer(1,
  98. std::string("Can't parse NULL config")));
  99. }
  100. // The parsers implement data inheritance by directly accessing
  101. // configuration context. For this reason the data parsers must store
  102. // the parsed data into context immediately. This may cause data
  103. // inconsistency if the parsing operation fails after the context has been
  104. // modified. We need to preserve the original context here
  105. // so as we can rollback changes when an error occurs.
  106. DCfgContextBasePtr original_context = context_->clone();
  107. // Answer will hold the result returned to the caller.
  108. ConstElementPtr answer;
  109. // Holds the name of the element being parsed.
  110. std::string element_id;
  111. try {
  112. // Grab a map of element_ids and their data values from the new
  113. // configuration set.
  114. const std::map<std::string, ConstElementPtr>& values_map =
  115. config_set->mapValue();
  116. // Use a pre-ordered list of element ids to parse the elements in a
  117. // specific order if the list (parser_order_) is not empty; otherwise
  118. // elements are parsed in the order the value_map presents them.
  119. if (!parse_order_.empty()) {
  120. // For each element_id in the parse order list, look for it in the
  121. // value map. If the element exists in the map, pass it and it's
  122. // associated data in for parsing.
  123. // If there is no matching entry in the value map an error is
  124. // thrown. Note, that elements tagged as "optional" from the user
  125. // perspective must still have default or empty entries in the
  126. // configuration set to be parsed.
  127. int parsed_count = 0;
  128. std::map<std::string, ConstElementPtr>::const_iterator it;
  129. BOOST_FOREACH(element_id, parse_order_) {
  130. it = values_map.find(element_id);
  131. if (it != values_map.end()) {
  132. ++parsed_count;
  133. buildAndCommit(element_id, it->second);
  134. }
  135. else {
  136. LOG_ERROR(dctl_logger, DCTL_ORDER_NO_ELEMENT)
  137. .arg(element_id);
  138. isc_throw(DCfgMgrBaseError, "Element:" << element_id <<
  139. " is listed in the parse order but is not "
  140. " present in the configuration");
  141. }
  142. }
  143. // NOTE: When using ordered parsing, the parse order list MUST
  144. // include every possible element id that the value_map may contain.
  145. // Entries in the map that are not in the parse order, would not be
  146. // parsed. For now we will flag this as a programmatic error. One
  147. // could attempt to adjust for this, by identifying such entries
  148. // and parsing them either first or last but which would be correct?
  149. // Better to hold the engineer accountable. So, if we parsed none
  150. // or we parsed fewer than are in the map; then either the parse i
  151. // order is incomplete OR the map has unsupported values.
  152. if (!parsed_count ||
  153. (parsed_count && ((parsed_count + 1) < values_map.size()))) {
  154. LOG_ERROR(dctl_logger, DCTL_ORDER_ERROR);
  155. isc_throw(DCfgMgrBaseError,
  156. "Configuration contains elements not in parse order");
  157. }
  158. } else {
  159. // Order doesn't matter so iterate over the value map directly.
  160. // Pass each element and it's associated data in to be parsed.
  161. ConfigPair config_pair;
  162. BOOST_FOREACH(config_pair, values_map) {
  163. element_id = config_pair.first;
  164. buildAndCommit(element_id, config_pair.second);
  165. }
  166. }
  167. // Everything was fine. Configuration set processed successfully.
  168. LOG_INFO(dctl_logger, DCTL_CONFIG_COMPLETE).arg("");
  169. answer = isc::config::createAnswer(0, "Configuration committed.");
  170. } catch (const isc::Exception& ex) {
  171. LOG_ERROR(dctl_logger, DCTL_PARSER_FAIL).arg(element_id).arg(ex.what());
  172. answer = isc::config::createAnswer(1,
  173. string("Configuration parsing failed: ") + ex.what());
  174. // An error occurred, so make sure that we restore original context.
  175. context_ = original_context;
  176. return (answer);
  177. }
  178. return (answer);
  179. }
  180. void DCfgMgrBase::buildAndCommit(std::string& element_id,
  181. isc::data::ConstElementPtr value) {
  182. // Call derivation's implementation to create the appropriate parser
  183. // based on the element id.
  184. ParserPtr parser = createConfigParser(element_id);
  185. if (!parser) {
  186. isc_throw(DCfgMgrBaseError, "Could not create parser");
  187. }
  188. try {
  189. // Invoke the parser's build method passing in the value. This will
  190. // "convert" the Element form of value into the actual data item(s)
  191. // and store them in parser's local storage.
  192. parser->build(value);
  193. // Invoke the parser's commit method. This "writes" the the data
  194. // item(s) stored locally by the parser into the context. (Note that
  195. // parsers are free to do more than update the context, but that is an
  196. // nothing something we are concerned with here.)
  197. parser->commit();
  198. } catch (const isc::Exception& ex) {
  199. isc_throw(DCfgMgrBaseError,
  200. "Could not build and commit: " << ex.what());
  201. } catch (...) {
  202. isc_throw(DCfgMgrBaseError, "Non-ISC exception occurred");
  203. }
  204. }
  205. }; // end of isc::dhcp namespace
  206. }; // end of isc namespace