diff --git a/src/lib/cc/simple_parser.cc b/src/lib/cc/simple_parser.cc index 742db7940182d6fe3f7dc5dbe5f602a2111d2b1b..621383735446cf5f073139918f6c9cd82216aa12 100644 --- a/src/lib/cc/simple_parser.cc +++ b/src/lib/cc/simple_parser.cc @@ -20,8 +20,43 @@ using isc::dhcp::DhcpConfigError; namespace isc { namespace data { +void +SimpleParser::checkRequired(const SimpleRequiredKeywords& required, + ConstElementPtr scope) { + for (auto name : required) { + if (scope->contains(name)) { + continue; + } + isc_throw(DhcpConfigError, "missing '" << name << "' parameter"); + } +} + +void +SimpleParser::checkKeywords(const SimpleKeywords& keywords, + ConstElementPtr scope) { + string spurious; + for (auto entry : scope->mapValue()) { + if (keywords.count(entry.first) == 0) { + if (spurious.empty()) { + spurious = entry.first; + } + continue; + } + Element::types expected = keywords.at(entry.first); + if (entry.second->getType() == expected) { + continue; + } + isc_throw(DhcpConfigError, "'" << entry.first << "' parameter is not " + << (expected == Element::integer ? "an " : "a ") + << Element::typeToName(expected)); + } + if (!spurious.empty()) { + isc_throw(DhcpConfigError, "spurious '" << spurious << "' parameter"); + } +} + std::string -SimpleParser::getString(isc::data::ConstElementPtr scope, const std::string& name) { +SimpleParser::getString(ConstElementPtr scope, const std::string& name) { ConstElementPtr x = scope->get(name); if (!x) { isc_throw(DhcpConfigError, @@ -38,7 +73,7 @@ SimpleParser::getString(isc::data::ConstElementPtr scope, const std::string& nam } int64_t -SimpleParser::getInteger(isc::data::ConstElementPtr scope, const std::string& name) { +SimpleParser::getInteger(ConstElementPtr scope, const std::string& name) { ConstElementPtr x = scope->get(name); if (!x) { isc_throw(DhcpConfigError, @@ -55,7 +90,7 @@ SimpleParser::getInteger(isc::data::ConstElementPtr scope, const std::string& na } bool -SimpleParser::getBoolean(isc::data::ConstElementPtr scope, const std::string& name) { +SimpleParser::getBoolean(ConstElementPtr scope, const std::string& name) { ConstElementPtr x = scope->get(name); if (!x) { isc_throw(DhcpConfigError, @@ -85,7 +120,7 @@ SimpleParser::getAddress(const ConstElementPtr& scope, } double -SimpleParser::getDouble(const isc::data::ConstElementPtr& scope, +SimpleParser::getDouble(const ConstElementPtr& scope, const std::string& name) { ConstElementPtr x = scope->get(name); if (!x) { @@ -116,7 +151,7 @@ SimpleParser::getPosition(const std::string& name, const data::ConstElementPtr p return (elem->getPosition()); } -size_t SimpleParser::setDefaults(isc::data::ElementPtr scope, +size_t SimpleParser::setDefaults(ElementPtr scope, const SimpleDefaults& default_values) { size_t cnt = 0; @@ -190,7 +225,7 @@ size_t SimpleParser::setDefaults(isc::data::ElementPtr scope, } size_t -SimpleParser::setListDefaults(isc::data::ConstElementPtr list, +SimpleParser::setListDefaults(ConstElementPtr list, const SimpleDefaults& default_values) { size_t cnt = 0; BOOST_FOREACH(ElementPtr entry, list->listValue()) { @@ -200,8 +235,8 @@ SimpleParser::setListDefaults(isc::data::ConstElementPtr list, } size_t -SimpleParser::deriveParams(isc::data::ConstElementPtr parent, - isc::data::ElementPtr child, +SimpleParser::deriveParams(ConstElementPtr parent, + ElementPtr child, const ParamsList& params) { if ( (parent->getType() != Element::map) || (child->getType() != Element::map)) { diff --git a/src/lib/cc/simple_parser.h b/src/lib/cc/simple_parser.h index 26341018ff8749fcaa7a7662de995932642c3222..60b773beab748251b9ab494ffe3495fe91fee3ff 100644 --- a/src/lib/cc/simple_parser.h +++ b/src/lib/cc/simple_parser.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -18,7 +19,7 @@ namespace isc { namespace data { -/// This array defines a single entry of default values +/// This array defines a single entry of default values. struct SimpleDefault { SimpleDefault(const char* name, isc::data::Element::types type, const char* value) :name_(name), type_(type), value_(value) {} @@ -27,11 +28,17 @@ struct SimpleDefault { const char* value_; }; -/// This specifies all default values in a given scope (e.g. a subnet) +/// This specifies all required keywords. +typedef std::vector SimpleRequiredKeywords; + +/// This specifies all accepted keywords with their types. +typedef std::map SimpleKeywords; + +/// This specifies all default values in a given scope (e.g. a subnet). typedef std::vector SimpleDefaults; /// This defines a list of all parameters that are derived (or inherited) between -/// contexts +/// contexts. typedef std::vector ParamsList; @@ -60,6 +67,30 @@ typedef std::vector ParamsList; class SimpleParser { public: + /// @brief Checks that all required keywords are present. + /// + /// This method throws an exception when a required + /// entry is not present in the given scope. + /// + /// @param required Required keywords. + /// @param scope Specified parameters which are checked. + /// @throw DhcpConfigError if a required parameter is not present. + static void checkRequired(const SimpleRequiredKeywords& required, + isc::data::ConstElementPtr scope); + + /// @brief Checks acceptable keywords with their expected type. + /// + /// This methods throws an exception when a not acceptable keyword + /// is found or when an acceptable entry does not have the expected type. + /// + /// @param keywords The @c SimpleKeywords keywords and types map. + /// @param scope Specified parameters which are checked. + /// @throw DhcpConfigError if a not acceptable keyword is found. + /// @throw DhcpConfigError if an acceptable entry does not have + /// the expected type. + static void checkKeywords(const SimpleKeywords& keywords, + isc::data::ConstElementPtr scope); + /// @brief Derives (inherits) parameters from parent scope to a child /// /// This method derives parameters from the parent scope to the child, diff --git a/src/lib/cc/tests/simple_parser_unittest.cc b/src/lib/cc/tests/simple_parser_unittest.cc index a43dc6a4fbfb272865628b16ab401678d420af37..3c1d49cb1a868fdb70c6a6f101449481e9c4cf74 100644 --- a/src/lib/cc/tests/simple_parser_unittest.cc +++ b/src/lib/cc/tests/simple_parser_unittest.cc @@ -14,6 +14,16 @@ using namespace isc::data; using namespace isc::asiolink; using isc::dhcp::DhcpConfigError; +/// This list defines required keywords. +const SimpleRequiredKeywords REQUIRED_KEYWORDS = { "foobar" }; + +/// This table defines keywords and types. +const SimpleKeywords KEYWORDS = { + { "id", Element::integer }, + { "prefix", Element::string }, + { "map", Element::map } +}; + /// This table defines sample default values. Although these are DHCPv6 /// specific, the mechanism is generic and can be used by any other component. const SimpleDefaults SAMPLE_DEFAULTS = { @@ -86,6 +96,49 @@ public: } }; +// This test checks if the checkRequired method works as expected. +TEST_F(SimpleParserTest, checkRequired) { + ConstElementPtr empty = Element::fromJSON("{ }"); + EXPECT_THROW(SimpleParser::checkRequired(REQUIRED_KEYWORDS, empty), + DhcpConfigError); + ConstElementPtr other = Element::fromJSON("{ \"foo\": 1, \"bar\": 2 }"); + EXPECT_THROW(SimpleParser::checkRequired(REQUIRED_KEYWORDS, other), + DhcpConfigError); + ConstElementPtr good = Element::fromJSON("{ \"foobar\": 2 }"); + EXPECT_NO_THROW(SimpleParser::checkRequired(REQUIRED_KEYWORDS, good)); +} + +// This test checks if the checkKeywords method works as expected. +TEST_F(SimpleParserTest, checkKeywords) { + ConstElementPtr empty = Element::fromJSON("{ }"); + EXPECT_NO_THROW(SimpleParser::checkKeywords(KEYWORDS, empty)); + ConstElementPtr id = Element::fromJSON("{ \"id\": 1 }"); + EXPECT_NO_THROW(SimpleParser::checkKeywords(KEYWORDS, id)); + ConstElementPtr bad_id = Element::fromJSON("{ \"id\": true }"); + EXPECT_THROW(SimpleParser::checkKeywords(KEYWORDS, bad_id), + DhcpConfigError); + ConstElementPtr bad_prefix = Element::fromJSON("{ \"prefix\": 12 }"); + EXPECT_THROW(SimpleParser::checkKeywords(KEYWORDS, bad_prefix), + DhcpConfigError); + ConstElementPtr bad_map = Element::fromJSON("{ \"map\": [ ] }"); + EXPECT_THROW(SimpleParser::checkKeywords(KEYWORDS, bad_map), + DhcpConfigError); + ConstElementPtr spurious = Element::fromJSON("{ \"spurious\": 1 }"); + EXPECT_THROW(SimpleParser::checkKeywords(KEYWORDS, spurious), + DhcpConfigError); + + // Bad type has precedence. + ConstElementPtr bad = Element::fromJSON("{ \"spurious\": 1, \"id\": true }"); + try { + SimpleParser::checkKeywords(KEYWORDS, bad); + ADD_FAILURE() << "expect exception"; + } catch (const DhcpConfigError& ex) { + EXPECT_EQ("'id' parameter is not an integer", std::string(ex.what())); + } catch (...) { + ADD_FAILURE() << "expect DhcpConfigError"; + } +} + // This test checks if the parameters can be inherited from the global // scope to the subnet scope. TEST_F(SimpleParserTest, deriveParams) {