123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500 |
- // Copyright (C) 2010, 2011 Internet Systems Consortium.
- //
- // Permission to use, copy, modify, and distribute this software for any
- // purpose with or without fee is hereby granted, provided that the above
- // copyright notice and this permission notice appear in all copies.
- //
- // THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
- // DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
- // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
- // INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
- // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
- // FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
- // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
- // WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- #include <config/module_spec.h>
- #include <sstream>
- #include <iostream>
- #include <fstream>
- #include <cerrno>
- #include <boost/foreach.hpp>
- // todo: add more context to thrown ModuleSpecErrors?
- using namespace isc::data;
- using namespace isc::config;
- namespace {
- //
- // Private functions
- //
- void
- check_leaf_item(ConstElementPtr spec, const std::string& name,
- Element::types type, bool mandatory)
- {
- if (spec->contains(name)) {
- if (spec->get(name)->getType() == type) {
- return;
- } else {
- isc_throw(ModuleSpecError,
- name + " not of type " + Element::typeToName(type));
- }
- } else if (mandatory) {
- // todo: want parent item name, and perhaps some info about location
- // in list? or just catch and throw new...
- // or make this part non-throwing and check return value...
- isc_throw(ModuleSpecError, name + " missing in " + spec->str());
- }
- }
- void check_config_item_list(ConstElementPtr spec);
- void
- check_config_item(ConstElementPtr spec) {
- check_leaf_item(spec, "item_name", Element::string, true);
- check_leaf_item(spec, "item_type", Element::string, true);
- check_leaf_item(spec, "item_optional", Element::boolean, true);
- check_leaf_item(spec, "item_default",
- Element::nameToType(spec->get("item_type")->stringValue()),
- !spec->get("item_optional")->boolValue()
- );
- // if list, check the list specification
- if (Element::nameToType(spec->get("item_type")->stringValue()) == Element::list) {
- check_leaf_item(spec, "list_item_spec", Element::map, true);
- check_config_item(spec->get("list_item_spec"));
- }
- if (spec->get("item_type")->stringValue() == "map") {
- check_leaf_item(spec, "map_item_spec", Element::list, true);
- check_config_item_list(spec->get("map_item_spec"));
- } else if (spec->get("item_type")->stringValue() == "named_set") {
- check_leaf_item(spec, "named_set_item_spec", Element::map, true);
- check_config_item(spec->get("named_set_item_spec"));
- }
- }
- void
- check_config_item_list(ConstElementPtr spec) {
- if (spec->getType() != Element::list) {
- isc_throw(ModuleSpecError, "config_data is not a list of elements");
- }
- BOOST_FOREACH(ConstElementPtr item, spec->listValue()) {
- check_config_item(item);
- }
- }
- // checks whether the given element is a valid statistics specification
- // returns false if the specification is bad
- bool
- check_format(ConstElementPtr value, ConstElementPtr format_name) {
- typedef std::map<std::string, std::string> format_types;
- format_types time_formats;
- // TODO: should be added other format types if necessary
- time_formats.insert(
- format_types::value_type("date-time", "%Y-%m-%dT%H:%M:%SZ") );
- time_formats.insert(
- format_types::value_type("date", "%Y-%m-%d") );
- time_formats.insert(
- format_types::value_type("time", "%H:%M:%S") );
- BOOST_FOREACH (const format_types::value_type& f, time_formats) {
- if (format_name->stringValue() == f.first) {
- struct tm tm;
- std::vector<char> buf(32);
- memset(&tm, 0, sizeof(tm));
- // reverse check
- return (strptime(value->stringValue().c_str(),
- f.second.c_str(), &tm) != NULL
- && strftime(&buf[0], buf.size(),
- f.second.c_str(), &tm) != 0
- && strncmp(value->stringValue().c_str(),
- &buf[0], buf.size()) == 0);
- }
- }
- return (false);
- }
- void check_statistics_item_list(ConstElementPtr spec);
- void
- check_statistics_item_list(ConstElementPtr spec) {
- if (spec->getType() != Element::list) {
- isc_throw(ModuleSpecError, "statistics is not a list of elements");
- }
- BOOST_FOREACH(ConstElementPtr item, spec->listValue()) {
- check_config_item(item);
- // additional checks for statistics
- check_leaf_item(item, "item_title", Element::string, true);
- check_leaf_item(item, "item_description", Element::string, true);
- check_leaf_item(item, "item_format", Element::string, false);
- // checks name of item_format and validation of item_default
- if (item->contains("item_format")
- && item->contains("item_default")) {
- if(!check_format(item->get("item_default"),
- item->get("item_format"))) {
- isc_throw(ModuleSpecError,
- "item_default not valid type of item_format");
- }
- }
- }
- }
- void
- check_command(ConstElementPtr spec) {
- check_leaf_item(spec, "command_name", Element::string, true);
- check_leaf_item(spec, "command_args", Element::list, true);
- check_config_item_list(spec->get("command_args"));
- }
- void
- check_command_list(ConstElementPtr spec) {
- if (spec->getType() != Element::list) {
- isc_throw(ModuleSpecError, "commands is not a list of elements");
- }
- BOOST_FOREACH(ConstElementPtr item, spec->listValue()) {
- check_command(item);
- }
- }
- void
- check_data_specification(ConstElementPtr spec) {
- check_leaf_item(spec, "module_name", Element::string, true);
- check_leaf_item(spec, "module_description", Element::string, false);
- // config_data is not mandatory; module could just define
- // commands and have no config
- if (spec->contains("config_data")) {
- check_config_item_list(spec->get("config_data"));
- }
- if (spec->contains("commands")) {
- check_command_list(spec->get("commands"));
- }
- if (spec->contains("statistics")) {
- check_statistics_item_list(spec->get("statistics"));
- }
- }
- // checks whether the given element is a valid module specification
- // throws a ModuleSpecError if the specification is bad
- void
- check_module_specification(ConstElementPtr def) {
- try {
- check_data_specification(def);
- } catch (const TypeError& te) {
- isc_throw(ModuleSpecError, te.what());
- }
- }
- }
- namespace isc {
- namespace config {
- //
- // Public functions
- //
- ModuleSpec::ModuleSpec(ConstElementPtr module_spec_element,
- const bool check)
- throw(ModuleSpecError)
-
- {
- module_specification = module_spec_element;
- if (check) {
- check_module_specification(module_specification);
- }
- }
- ConstElementPtr
- ModuleSpec::getCommandsSpec() const {
- if (module_specification->contains("commands")) {
- return (module_specification->get("commands"));
- } else {
- return (ElementPtr());
- }
- }
- ConstElementPtr
- ModuleSpec::getConfigSpec() const {
- if (module_specification->contains("config_data")) {
- return (module_specification->get("config_data"));
- } else {
- return (ElementPtr());
- }
- }
- ConstElementPtr
- ModuleSpec::getStatisticsSpec() const {
- if (module_specification->contains("statistics")) {
- return (module_specification->get("statistics"));
- } else {
- return (ElementPtr());
- }
- }
- const std::string
- ModuleSpec::getModuleName() const {
- return (module_specification->get("module_name")->stringValue());
- }
- const std::string
- ModuleSpec::getModuleDescription() const {
- if (module_specification->contains("module_description")) {
- return (module_specification->get("module_description")->stringValue());
- } else {
- return (std::string(""));
- }
- }
- bool
- ModuleSpec::validateConfig(ConstElementPtr data, const bool full) const {
- ConstElementPtr spec = module_specification->find("config_data");
- return (validateSpecList(spec, data, full, ElementPtr()));
- }
- bool
- ModuleSpec::validateStatistics(ConstElementPtr data, const bool full) const {
- ConstElementPtr spec = module_specification->find("statistics");
- return (validateSpecList(spec, data, full, ElementPtr()));
- }
- bool
- ModuleSpec::validateCommand(const std::string& command,
- ConstElementPtr args,
- ElementPtr errors) const
- {
- if (args->getType() != Element::map) {
- errors->add(Element::create("args for command " +
- command + " is not a map"));
- return (false);
- }
- ConstElementPtr commands_spec = module_specification->find("commands");
- if (!commands_spec) {
- // there are no commands according to the spec.
- errors->add(Element::create("The given module has no commands"));
- return (false);
- }
- BOOST_FOREACH(ConstElementPtr cur_command, commands_spec->listValue()) {
- if (cur_command->get("command_name")->stringValue() == command) {
- return (validateSpecList(cur_command->get("command_args"),
- args, true, errors));
- }
- }
- // this command is unknown
- errors->add(Element::create("Unknown command " + command));
- return (false);
- }
- bool
- ModuleSpec::validateConfig(ConstElementPtr data, const bool full,
- ElementPtr errors) const
- {
- ConstElementPtr spec = module_specification->find("config_data");
- return (validateSpecList(spec, data, full, errors));
- }
- bool
- ModuleSpec::validateStatistics(ConstElementPtr data, const bool full,
- ElementPtr errors) const
- {
- ConstElementPtr spec = module_specification->find("statistics");
- return (validateSpecList(spec, data, full, errors));
- }
- ModuleSpec
- moduleSpecFromFile(const std::string& file_name, const bool check)
- throw(JSONError, ModuleSpecError)
- {
- std::ifstream file;
- file.open(file_name.c_str());
- if (!file) {
- std::stringstream errs;
- errs << "Error opening " << file_name << ": " << strerror(errno);
- isc_throw(ModuleSpecError, errs.str());
- }
- ConstElementPtr module_spec_element = Element::fromJSON(file, file_name);
- if (module_spec_element->contains("module_spec")) {
- return (ModuleSpec(module_spec_element->get("module_spec"), check));
- } else {
- isc_throw(ModuleSpecError, "No module_spec in specification");
- }
- }
- ModuleSpec
- moduleSpecFromFile(std::ifstream& in, const bool check)
- throw(JSONError, ModuleSpecError)
- {
- ConstElementPtr module_spec_element = Element::fromJSON(in);
- if (module_spec_element->contains("module_spec")) {
- return (ModuleSpec(module_spec_element->get("module_spec"), check));
- } else {
- isc_throw(ModuleSpecError, "No module_spec in specification");
- }
- }
- namespace {
- //
- // private functions
- //
- //
- // helper functions for validation
- //
- bool
- check_type(ConstElementPtr spec, ConstElementPtr element) {
- std::string cur_item_type;
- cur_item_type = spec->get("item_type")->stringValue();
- if (cur_item_type == "any") {
- return (true);
- }
- switch (element->getType()) {
- case Element::integer:
- return (cur_item_type == "integer");
- break;
- case Element::real:
- return (cur_item_type == "real");
- break;
- case Element::boolean:
- return (cur_item_type == "boolean");
- break;
- case Element::string:
- return (cur_item_type == "string");
- break;
- case Element::list:
- return (cur_item_type == "list");
- break;
- case Element::map:
- return (cur_item_type == "map" ||
- cur_item_type == "named_set");
- break;
- }
- return (false);
- }
- }
- bool
- ModuleSpec::validateItem(ConstElementPtr spec, ConstElementPtr data,
- const bool full, ElementPtr errors) const
- {
- if (!check_type(spec, data)) {
- // we should do some proper error feedback here
- // std::cout << "type mismatch; not " << spec->get("item_type") << ": " << data << std::endl;
- // std::cout << spec << std::endl;
- if (errors) {
- errors->add(Element::create("Type mismatch"));
- }
- return (false);
- }
- if (data->getType() == Element::list) {
- ConstElementPtr list_spec = spec->get("list_item_spec");
- BOOST_FOREACH(ConstElementPtr list_el, data->listValue()) {
- if (!check_type(list_spec, list_el)) {
- if (errors) {
- errors->add(Element::create("Type mismatch"));
- }
- return (false);
- }
- if (list_spec->get("item_type")->stringValue() == "map") {
- if (!validateItem(list_spec, list_el, full, errors)) {
- return (false);
- }
- }
- }
- }
- if (data->getType() == Element::map) {
- // either a normal 'map' or a 'named set' (determined by which
- // subspecification it has)
- if (spec->contains("map_item_spec")) {
- if (!validateSpecList(spec->get("map_item_spec"), data, full, errors)) {
- return (false);
- }
- } else {
- typedef std::pair<std::string, ConstElementPtr> maptype;
- BOOST_FOREACH(maptype m, data->mapValue()) {
- if (!validateItem(spec->get("named_set_item_spec"), m.second, full, errors)) {
- return (false);
- }
- }
- }
- }
- if (spec->contains("item_format")) {
- if (!check_format(data, spec->get("item_format"))) {
- if (errors) {
- errors->add(Element::create("Format mismatch"));
- }
- return (false);
- }
- }
- return (true);
- }
- // spec is a map with item_name etc, data is a map
- bool
- ModuleSpec::validateSpec(ConstElementPtr spec, ConstElementPtr data,
- const bool full, ElementPtr errors) const
- {
- std::string item_name = spec->get("item_name")->stringValue();
- bool optional = spec->get("item_optional")->boolValue();
- ConstElementPtr data_el;
- data_el = data->get(item_name);
-
- if (data_el) {
- if (!validateItem(spec, data_el, full, errors)) {
- return (false);
- }
- } else {
- if (!optional && full) {
- if (errors) {
- errors->add(Element::create("Non-optional value missing"));
- }
- return (false);
- }
- }
- return (true);
- }
- // spec is a list of maps, data is a map
- bool
- ModuleSpec::validateSpecList(ConstElementPtr spec, ConstElementPtr data,
- const bool full, ElementPtr errors) const
- {
- bool validated = true;
- BOOST_FOREACH(ConstElementPtr cur_spec_el, spec->listValue()) {
- if (!validateSpec(cur_spec_el, data, full, errors)) {
- validated = false;
- }
- }
- typedef std::pair<std::string, ConstElementPtr> maptype;
-
- BOOST_FOREACH(maptype m, data->mapValue()) {
- bool found = false;
- // Ignore 'version' as a config element
- if (m.first.compare("version") != 0) {
- BOOST_FOREACH(ConstElementPtr cur_spec_el, spec->listValue()) {
- if (cur_spec_el->get("item_name")->stringValue().compare(m.first) == 0) {
- found = true;
- }
- }
- if (!found) {
- validated = false;
- if (errors) {
- errors->add(Element::create("Unknown item " + m.first));
- }
- }
- }
- }
- return (validated);
- }
- }
- }
|