mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-29 21:18:02 +00:00
added datadefinition checking to the DataDefinition class, throw DataDefinitionError when that fails
first argument of group_sendmsg() is now const parkinglot now reads a parkinglot.spec file and tries to send it to the config manager (which currently fails somewhere in the msgq), file is read from cwd for now (i.e. the bin/bind10/bind10 dir) git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jelte-datadef@296 e5f2f494-b856-4b98-b285-d166d9295462
This commit is contained in:
parent
805d4406fd
commit
f39adf2274
@ -14,17 +14,26 @@
|
||||
|
||||
// $Id$
|
||||
|
||||
//
|
||||
// todo: generalize this and make it into a specific API for all modules
|
||||
// to use (i.e. connect to cc, send config and commands, get config,
|
||||
// react on config change announcements)
|
||||
//
|
||||
|
||||
|
||||
#include <stdexcept>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include <cc/cpp/data.h>
|
||||
#include <cc/cpp/data_def.h>
|
||||
#include <cc/cpp/session.h>
|
||||
|
||||
#include "common.h"
|
||||
@ -34,16 +43,53 @@ using namespace std;
|
||||
|
||||
using ISC::Data::Element;
|
||||
using ISC::Data::ElementPtr;
|
||||
using ISC::Data::DataDefinition;
|
||||
using ISC::Data::ParseError;
|
||||
using ISC::Data::DataDefinitionError;
|
||||
|
||||
void
|
||||
CommandSession::read_data_definition(const std::string& filename) {
|
||||
std::ifstream file;
|
||||
|
||||
// this file should be declared in a @something@ directive
|
||||
file.open("parkinglot.spec");
|
||||
if (!file) {
|
||||
cout << "error opening parkinglot.spec" << endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
data_definition_ = DataDefinition(file, true);
|
||||
cout << "Definition: " << endl;
|
||||
cout << data_definition_.getDefinition() << endl;
|
||||
} catch (ParseError pe) {
|
||||
cout << "Error parsing definition file: " << pe.what() << endl;
|
||||
exit(1);
|
||||
} catch (DataDefinitionError dde) {
|
||||
cout << "Error reading definition file: " << dde.what() << endl;
|
||||
exit(1);
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
CommandSession::CommandSession() :
|
||||
session_(ISC::CC::Session())
|
||||
{
|
||||
try {
|
||||
// todo: workaround, let boss wait until msgq is started
|
||||
// and remove sleep here
|
||||
sleep(1);
|
||||
session_.establish();
|
||||
session_.subscribe("ParkingLot", "*", "meonly");
|
||||
session_.subscribe("Boss", "*", "meonly");
|
||||
session_.subscribe("ConfigManager", "*", "meonly");
|
||||
session_.subscribe("statistics", "*", "meonly");
|
||||
read_data_definition("parkinglot.spec");
|
||||
sleep(1);
|
||||
ElementPtr cmd = Element::create_from_string("{ \"config_manager\": 1}");
|
||||
// why does the msgq seem to kill this msg?
|
||||
session_.group_sendmsg(data_definition_.getDefinition(), "ConfigManager");
|
||||
cout << "def sent" << endl;
|
||||
} catch (...) {
|
||||
throw std::runtime_error("SessionManager: failed to open sessions");
|
||||
}
|
||||
@ -88,12 +134,12 @@ CommandSession::getCommand(int counter) {
|
||||
return std::pair<string, string>("unknown", "");
|
||||
}
|
||||
|
||||
// should be replaced by the general config-getter in cc setup
|
||||
std::vector<std::string>
|
||||
CommandSession::getZones() {
|
||||
ElementPtr cmd, result, env;
|
||||
std::vector<std::string> zone_names;
|
||||
cmd = Element::create_from_string("{ \"command\": [ \"zone\", \"list\" ] }");
|
||||
sleep(1);
|
||||
session_.group_sendmsg(cmd, "ConfigManager");
|
||||
session_.group_recvmsg(env, result, false);
|
||||
BOOST_FOREACH(ElementPtr zone_name, result->get("result")->list_value()) {
|
||||
|
@ -20,6 +20,8 @@
|
||||
#include <string>
|
||||
|
||||
#include <cc/cpp/session.h>
|
||||
#include <cc/cpp/data_def.h>
|
||||
#include <cc/cpp/data.h>
|
||||
|
||||
class CommandSession {
|
||||
public:
|
||||
@ -28,7 +30,11 @@ public:
|
||||
std::pair<std::string, std::string> getCommand(int counter);
|
||||
std::vector<std::string> getZones();
|
||||
private:
|
||||
void read_data_definition(const std::string& filename);
|
||||
|
||||
ISC::CC::Session session_;
|
||||
ISC::Data::DataDefinition data_definition_;
|
||||
ISC::Data::ElementPtr config_;
|
||||
};
|
||||
|
||||
#endif // __CCSESSION_H
|
||||
|
@ -112,7 +112,6 @@ if __name__ == "__main__":
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
signal.signal(signal.SIGTERM, signal_handler)
|
||||
cm.read_config()
|
||||
# do loading here if necessary
|
||||
cm.notify_boss()
|
||||
cm.run()
|
||||
cm.write_config()
|
||||
|
@ -1,4 +1,4 @@
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/src/lib/cc/cpp -I$(top_srcdir)/ext
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/src/lib/cc/cpp -I$(top_srcdir)/ext -Wall -Werror
|
||||
|
||||
bin_PROGRAMS = test
|
||||
test_SOURCES = test.cc
|
||||
|
@ -5,14 +5,150 @@
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
// todo: add more context to thrown DataDefinitionErrors?
|
||||
|
||||
using namespace ISC::Data;
|
||||
|
||||
DataDefinition::DataDefinition(std::istream& in) throw(ParseError) {
|
||||
// todo: is there a direct way to get a std::string from an enum label?
|
||||
static std::string
|
||||
get_type_string(Element::types type)
|
||||
{
|
||||
switch(type) {
|
||||
case Element::integer:
|
||||
return std::string("integer");
|
||||
case Element::real:
|
||||
return std::string("real");
|
||||
case Element::boolean:
|
||||
return std::string("boolean");
|
||||
case Element::string:
|
||||
return std::string("string");
|
||||
case Element::list:
|
||||
return std::string("list");
|
||||
case Element::map:
|
||||
return std::string("map");
|
||||
default:
|
||||
return std::string("unknown");
|
||||
}
|
||||
}
|
||||
|
||||
static Element::types
|
||||
get_type_value(const std::string& type_name) {
|
||||
if (type_name == "integer") {
|
||||
return Element::integer;
|
||||
} else if (type_name == "real") {
|
||||
return Element::real;
|
||||
} else if (type_name == "boolean") {
|
||||
return Element::boolean;
|
||||
} else if (type_name == "string") {
|
||||
return Element::string;
|
||||
} else if (type_name == "list") {
|
||||
return Element::list;
|
||||
} else if (type_name == "map") {
|
||||
return Element::map;
|
||||
} else {
|
||||
throw DataDefinitionError(type_name + " is not a valid type name");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
check_leaf_item(const ElementPtr& spec, const std::string& name, Element::types type, bool mandatory)
|
||||
{
|
||||
if (spec->contains(name)) {
|
||||
if (spec->get(name)->get_type() == type) {
|
||||
return;
|
||||
} else {
|
||||
throw DataDefinitionError(name + " not of type " + get_type_string(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...
|
||||
throw DataDefinitionError(name + " missing in " + spec->str());
|
||||
}
|
||||
}
|
||||
|
||||
static void check_config_item_list(const ElementPtr& spec);
|
||||
|
||||
static void
|
||||
check_config_item(const ElementPtr& 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",
|
||||
get_type_value(spec->get("item_type")->string_value()),
|
||||
!spec->get("item_optional")->bool_value()
|
||||
);
|
||||
|
||||
// if list, check the list definition
|
||||
if (get_type_value(spec->get("item_type")->string_value()) == Element::list) {
|
||||
check_leaf_item(spec, "list_item_spec", Element::map, true);
|
||||
check_config_item(spec->get("list_item_spec"));
|
||||
}
|
||||
// todo: add stuff for type map
|
||||
if (get_type_value(spec->get("item_type")->string_value()) == Element::map) {
|
||||
check_leaf_item(spec, "map_item_spec", Element::list, true);
|
||||
check_config_item_list(spec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
check_config_item_list(const ElementPtr& spec) {
|
||||
if (spec->get_type() != Element::list) {
|
||||
throw DataDefinitionError("config_data is not a list of elements");
|
||||
}
|
||||
BOOST_FOREACH(ElementPtr item, spec->list_value()) {
|
||||
check_config_item(item);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
check_command(const ElementPtr& 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"));
|
||||
}
|
||||
|
||||
static void
|
||||
check_command_list(const ElementPtr& spec) {
|
||||
if (spec->get_type() != Element::list) {
|
||||
throw DataDefinitionError("commands is not a list of elements");
|
||||
}
|
||||
BOOST_FOREACH(ElementPtr item, spec->list_value()) {
|
||||
check_command(item);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
check_data_specification(const ElementPtr& spec) {
|
||||
check_leaf_item(spec, "module_name", Element::string, true);
|
||||
// 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"));
|
||||
}
|
||||
}
|
||||
|
||||
// checks whether the given element is a valid data definition
|
||||
// throws a DataDefinitionError if the specification is bad
|
||||
static void
|
||||
check_definition(const ElementPtr& def)
|
||||
{
|
||||
if (!def->contains("data_specification")) {
|
||||
throw DataDefinitionError("Data specification does not contain data_specification element");
|
||||
} else {
|
||||
check_data_specification(def->get("data_specification"));
|
||||
}
|
||||
}
|
||||
|
||||
DataDefinition::DataDefinition(std::istream& in, const bool check)
|
||||
throw(ParseError, DataDefinitionError) {
|
||||
definition = Element::create_from_string(in);
|
||||
// TODO, make sure the whole structure is complete and valid
|
||||
if (!definition->contains("data_specification")) {
|
||||
throw ParseError("Data specification does not contain data_specification element");
|
||||
// make sure the whole structure is complete and valid
|
||||
if (check) {
|
||||
check_definition(definition);
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,7 +236,6 @@ bool
|
||||
DataDefinition::validate_spec_list(const ElementPtr spec, const ElementPtr data) {
|
||||
ElementPtr cur_data_el;
|
||||
std::string cur_item_name;
|
||||
bool optional;
|
||||
BOOST_FOREACH(ElementPtr cur_spec_el, spec->list_value()) {
|
||||
if (!validate_spec(cur_spec_el, data)) {
|
||||
return false;
|
||||
|
@ -7,11 +7,22 @@
|
||||
|
||||
namespace ISC { namespace Data {
|
||||
|
||||
class DataDefinitionError : public std::exception {
|
||||
public:
|
||||
DataDefinitionError(std::string m = "Data definition is invalid") : msg(m) {}
|
||||
~DataDefinitionError() throw() {}
|
||||
const char* what() const throw() { return msg.c_str(); }
|
||||
private:
|
||||
std::string msg;
|
||||
};
|
||||
|
||||
class DataDefinition {
|
||||
public:
|
||||
explicit DataDefinition() {};
|
||||
explicit DataDefinition(ElementPtr e) : definition(e) {};
|
||||
explicit DataDefinition(std::istream& in) throw(ParseError);
|
||||
// todo: make check default false, or leave out option completely and always check?
|
||||
explicit DataDefinition(std::istream& in, const bool check = true)
|
||||
throw(ParseError, DataDefinitionError);
|
||||
|
||||
const ElementPtr getDefinition() { return definition; };
|
||||
// returns true if the given element conforms to this data
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"data_specification": {
|
||||
"module_name": "parkinglot",
|
||||
"module_name": "ParkingLot",
|
||||
"config_data": [
|
||||
{
|
||||
"item_name": "port",
|
||||
@ -24,21 +24,21 @@
|
||||
"commands": [
|
||||
{
|
||||
"command_name": "zone_add",
|
||||
"command_args": {
|
||||
"command_args": [ {
|
||||
"item_name": "zone_name",
|
||||
"item_type": "string",
|
||||
"item_optional": false,
|
||||
"item_default": ""
|
||||
}
|
||||
} ]
|
||||
},
|
||||
{
|
||||
"command_name": "zone_delete",
|
||||
"command_args": {
|
||||
"command_args": [ {
|
||||
"item_name": "zone_name",
|
||||
"item_type": "string",
|
||||
"item_optional": false,
|
||||
"item_default": ""
|
||||
}
|
||||
} ]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -138,7 +138,8 @@ Session::unsubscribe(std::string group, std::string instance)
|
||||
}
|
||||
|
||||
unsigned int
|
||||
Session::group_sendmsg(ElementPtr& msg, std::string group, std::string instance, std::string to)
|
||||
Session::group_sendmsg(const ElementPtr& msg, std::string group,
|
||||
std::string instance, std::string to)
|
||||
{
|
||||
ElementPtr env = Element::create(std::map<std::string, ElementPtr>());
|
||||
|
||||
|
@ -43,7 +43,7 @@ namespace ISC {
|
||||
std::string subtype = "normal");
|
||||
void unsubscribe(std::string group,
|
||||
std::string instance = "*");
|
||||
unsigned int group_sendmsg(ISC::Data::ElementPtr& msg,
|
||||
unsigned int group_sendmsg(const ISC::Data::ElementPtr& msg,
|
||||
std::string group,
|
||||
std::string instance = "*",
|
||||
std::string to = "*");
|
||||
|
@ -27,6 +27,9 @@ main(int argc, char **argv) {
|
||||
} catch (ParseError pe) {
|
||||
cout << "Error parsing definition file: " << pe.what() << endl;
|
||||
return 1;
|
||||
} catch (DataDefinitionError dde) {
|
||||
cout << "Error reading definition file: " << dde.what() << endl;
|
||||
return 1;
|
||||
}
|
||||
file.close();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user