Kea  1.9.9-git
lib/cc/simple_parser.cc
Go to the documentation of this file.
1 // Copyright (C) 2016-2021 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
9 #include <cc/simple_parser.h>
10 #include <asiolink/io_address.h>
11 #include <boost/foreach.hpp>
12 #include <boost/lexical_cast.hpp>
13 #include <cc/data.h>
14 #include <string>
15 
16 using namespace std;
17 using namespace isc::asiolink;
18 using namespace isc::dhcp;
20 
21 namespace isc {
22 namespace data {
23 
24 void
25 SimpleParser::checkRequired(const SimpleRequiredKeywords& required,
26  ConstElementPtr scope) {
27  for (auto name : required) {
28  if (scope->contains(name)) {
29  continue;
30  }
31  isc_throw(DhcpConfigError, "missing '" << name << "' parameter");
32  }
33 }
34 
35 void
36 SimpleParser::checkKeywords(const SimpleKeywords& keywords,
37  ConstElementPtr scope) {
38  string spurious;
39  for (auto entry : scope->mapValue()) {
40  if (keywords.count(entry.first) == 0) {
41  if (spurious.empty()) {
42  spurious = entry.first;
43  }
44  continue;
45  }
46  Element::types expected = keywords.at(entry.first);
47  if ((expected == Element::any) ||
48  (entry.second->getType() == expected)) {
49  continue;
50  }
51  isc_throw(DhcpConfigError, "'" << entry.first << "' parameter is not "
52  << (expected == Element::integer ? "an " : "a ")
53  << Element::typeToName(expected));
54  }
55  if (!spurious.empty()) {
56  isc_throw(DhcpConfigError, "spurious '" << spurious << "' parameter");
57  }
58 }
59 
60 std::string
61 SimpleParser::getString(ConstElementPtr scope, const std::string& name) {
62  ConstElementPtr x = scope->get(name);
63  if (!x) {
65  "missing parameter '" << name << "' ("
66  << scope->getPosition() << ")");
67  }
68  if (x->getType() != Element::string) {
70  "invalid type specified for parameter '" << name
71  << "' (" << x->getPosition() << ")");
72  }
73 
74  return (x->stringValue());
75 }
76 
77 int64_t
78 SimpleParser::getInteger(ConstElementPtr scope, const std::string& name) {
79  ConstElementPtr x = scope->get(name);
80  if (!x) {
82  "missing parameter '" << name << "' ("
83  << scope->getPosition() << ")");
84  }
85  if (x->getType() != Element::integer) {
87  "invalid type specified for parameter '" << name
88  << "' (" << x->getPosition() << ")");
89  }
90 
91  return (x->intValue());
92 }
93 
94 int64_t
95 SimpleParser::getInteger(isc::data::ConstElementPtr scope, const std::string& name,
96  int64_t min, int64_t max) {
97  int64_t tmp = getInteger(scope, name);
98  if (tmp < min || tmp > max) {
100  "The '" << name << "' value (" << tmp
101  << ") is not within expected range: (" << min << " - " << max
102  << ")");
103  }
104  return (tmp);
105 }
106 
107 bool
108 SimpleParser::getBoolean(ConstElementPtr scope, const std::string& name) {
109  ConstElementPtr x = scope->get(name);
110  if (!x) {
112  "missing parameter '" << name << "' ("
113  << scope->getPosition() << ")");
114  }
115  if (x->getType() != Element::boolean) {
117  "invalid type specified for parameter '" << name
118  << "' (" << x->getPosition() << ")");
119  }
120 
121  return (x->boolValue());
122 }
123 
124 IOAddress
125 SimpleParser::getAddress(const ConstElementPtr& scope,
126  const std::string& name) {
127  std::string str = getString(scope, name);
128  try {
129  return (IOAddress(str));
130  } catch (const std::exception& e) {
131  isc_throw(DhcpConfigError, "Failed to convert '" << str
132  << "' to address: " << e.what() << "("
133  << getPosition(name, scope) << ")");
134  }
135 }
136 
137 double
138 SimpleParser::getDouble(const ConstElementPtr& scope,
139  const std::string& name) {
140  ConstElementPtr x = scope->get(name);
141  if (!x) {
143  "missing parameter '" << name << "' ("
144  << scope->getPosition() << ")");
145  }
146 
147  if (x->getType() != Element::real) {
149  "invalid type specified for parameter '" << name
150  << "' (" << x->getPosition() << ")");
151  }
152 
153  return (x->doubleValue());
154 }
155 
156 
158 SimpleParser::getPosition(const std::string& name, const data::ConstElementPtr parent) {
159  if (!parent) {
160  return (data::Element::ZERO_POSITION());
161  }
162  ConstElementPtr elem = parent->get(name);
163  if (!elem) {
164  return (parent->getPosition());
165  }
166  return (elem->getPosition());
167 }
168 
169 size_t SimpleParser::setDefaults(ElementPtr scope,
170  const SimpleDefaults& default_values) {
171  size_t cnt = 0;
172 
173  // This is the position representing a default value. As the values
174  // we're inserting here are not present in whatever the config file
175  // came from, we need to make sure it's clearly labeled as default.
176  const Element::Position pos("<default-value>", 0, 0);
177 
178  // Let's go over all parameters we have defaults for.
179  BOOST_FOREACH(SimpleDefault def_value, default_values) {
180 
181  // Try if such a parameter is there. If it is, let's
182  // skip it, because user knows best *cough*.
183  ConstElementPtr x = scope->get(string(def_value.name_));
184  if (x) {
185  // There is such a value already, skip it.
186  continue;
187  }
188 
189  // There isn't such a value defined, let's create the default
190  // value...
191  switch (def_value.type_) {
192  case Element::string: {
193  x.reset(new StringElement(def_value.value_, pos));
194  break;
195  }
196  case Element::integer: {
197  try {
198  int int_value = boost::lexical_cast<int>(def_value.value_);
199  x.reset(new IntElement(int_value, pos));
200  }
201  catch (const std::exception& ex) {
202  isc_throw(BadValue, "Internal error. Integer value expected for: "
203  << def_value.name_ << ", value is: "
204  << def_value.value_ );
205  }
206 
207  break;
208  }
209  case Element::boolean: {
210  bool bool_value;
211  if (def_value.value_ == string("true")) {
212  bool_value = true;
213  } else if (def_value.value_ == string("false")) {
214  bool_value = false;
215  } else {
217  "Internal error. Boolean value specified as "
218  << def_value.value_ << ", expected true or false");
219  }
220  x.reset(new BoolElement(bool_value, pos));
221  break;
222  }
223  case Element::real: {
224  double dbl_value = boost::lexical_cast<double>(def_value.value_);
225  x.reset(new DoubleElement(dbl_value, pos));
226  break;
227  }
228  default:
229  // No default values for null, list or map
231  "Internal error. Incorrect default value type.");
232  }
233 
234  // ... and insert it into the provided Element tree.
235  scope->set(def_value.name_, x);
236  ++cnt;
237  }
238 
239  return (cnt);
240 }
241 
242 size_t
243 SimpleParser::setListDefaults(ConstElementPtr list,
244  const SimpleDefaults& default_values) {
245  size_t cnt = 0;
246  BOOST_FOREACH(ElementPtr entry, list->listValue()) {
247  cnt += setDefaults(entry, default_values);
248  }
249  return (cnt);
250 }
251 
252 size_t
253 SimpleParser::deriveParams(ConstElementPtr parent,
254  ElementPtr child,
255  const ParamsList& params) {
256  if ( (parent->getType() != Element::map) ||
257  (child->getType() != Element::map)) {
258  return (0);
259  }
260 
261  size_t cnt = 0;
262  BOOST_FOREACH(string param, params) {
263  ConstElementPtr x = parent->get(param);
264  if (!x) {
265  // Parent doesn't define this parameter, so there's
266  // nothing to derive from
267  continue;
268  }
269 
270  if (child->get(param)) {
271  // Child defines this parameter already. There's
272  // nothing to do here.
273  continue;
274  }
275 
276  // Copy the parameters to the child scope.
277  child->set(param, x);
278  cnt++;
279  }
280 
281  return (cnt);
282 }
283 
284 const Triplet<uint32_t>
285 SimpleParser::parseIntTriplet(const ConstElementPtr& scope,
286  const std::string& name) {
287  // Initialize as some compilers complain otherwise.
288  uint32_t value = 0;
289  bool has_value = false;
290  uint32_t min_value = 0;
291  bool has_min = false;
292  uint32_t max_value = 0;
293  bool has_max = false;
294  if (scope->contains(name)) {
295  value = getInteger(scope, name);
296  has_value = true;
297  }
298  if (scope->contains("min-" + name)) {
299  min_value = getInteger(scope, "min-" + name);
300  has_min = true;
301  }
302  if (scope->contains("max-" + name)) {
303  max_value = getInteger(scope, "max-" + name);
304  has_max = true;
305  }
306  if (!has_value && !has_min && !has_max) {
307  return (Triplet<uint32_t>());
308  }
309  if (has_value) {
310  if (!has_min && !has_max) {
311  // default only.
312  min_value = value;
313  max_value = value;
314  } else if (!has_min) {
315  // default and max.
316  min_value = value;
317  } else if (!has_max) {
318  // default and min.
319  max_value = value;
320  }
321  } else if (has_min) {
322  // min only.
323  if (!has_max) {
324  value = min_value;
325  max_value = min_value;
326  } else {
327  // min and max.
328  isc_throw(DhcpConfigError, "have min-" << name << " and max-"
329  << name << " but no " << name << " (default) in "
330  << scope->getPosition());
331  }
332  } else {
333  // max only.
334  min_value = max_value;
335  value = max_value;
336  }
337  // Check that min <= max.
338  if (min_value > max_value) {
339  if (has_min && has_max) {
340  isc_throw(DhcpConfigError, "the value of min-" << name << " ("
341  << min_value << ") is not less than max-" << name << " ("
342  << max_value << ")");
343  } else if (has_min) {
344  // Only min and default so min > default.
345  isc_throw(DhcpConfigError, "the value of min-" << name << " ("
346  << min_value << ") is not less than (default) " << name
347  << " (" << value << ")");
348  } else {
349  // Only default and max so default > max.
350  isc_throw(DhcpConfigError, "the value of (default) " << name
351  << " (" << value << ") is not less than max-" << name
352  << " (" << max_value << ")");
353  }
354  }
355  // Check that value is between min and max.
356  if ((value < min_value) || (value > max_value)) {
357  isc_throw(DhcpConfigError, "the value of (default) " << name << " ("
358  << value << ") is not between min-" << name << " ("
359  << min_value << ") and max-" << name << " ("
360  << max_value << ")");
361  }
362  return (Triplet<uint32_t>(min_value, value, max_value));
363 }
364 
365 }; // end of isc::dhcp namespace
366 }; // end of isc namespace
std::map< std::string, isc::data::Element::types > SimpleKeywords
This specifies all accepted keywords with their types.
std::vector< SimpleDefault > SimpleDefaults
This specifies all default values in a given scope (e.g. a subnet).
boost::shared_ptr< Element > ElementPtr
Definition: data.h:20
STL namespace.
const isc::data::Element::types type_
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
To be removed. Please use ConfigError instead.
Notes: IntElement type is changed to int64_t.
Definition: data.h:544
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:23
std::vector< std::string > SimpleRequiredKeywords
This specifies all required keywords.
This array defines a single entry of default values.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
Represents the position of the data element within a configuration string.
Definition: data.h:88
Defines the logger used by the top-level component of kea-dhcp-ddns.
std::vector< std::string > ParamsList
This defines a list of all parameters that are derived (or inherited) between contexts.
A generic exception that is thrown if a parameter given to a method would refer to or modify out-of-r...