mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-30 13:37:55 +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:
@@ -14,17 +14,26 @@
|
|||||||
|
|
||||||
// $Id$
|
// $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 <stdexcept>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
|
|
||||||
#include <cc/cpp/data.h>
|
#include <cc/cpp/data.h>
|
||||||
|
#include <cc/cpp/data_def.h>
|
||||||
#include <cc/cpp/session.h>
|
#include <cc/cpp/session.h>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
@@ -34,16 +43,53 @@ using namespace std;
|
|||||||
|
|
||||||
using ISC::Data::Element;
|
using ISC::Data::Element;
|
||||||
using ISC::Data::ElementPtr;
|
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() :
|
CommandSession::CommandSession() :
|
||||||
session_(ISC::CC::Session())
|
session_(ISC::CC::Session())
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
// todo: workaround, let boss wait until msgq is started
|
||||||
|
// and remove sleep here
|
||||||
|
sleep(1);
|
||||||
session_.establish();
|
session_.establish();
|
||||||
session_.subscribe("ParkingLot", "*", "meonly");
|
session_.subscribe("ParkingLot", "*", "meonly");
|
||||||
session_.subscribe("Boss", "*", "meonly");
|
session_.subscribe("Boss", "*", "meonly");
|
||||||
session_.subscribe("ConfigManager", "*", "meonly");
|
session_.subscribe("ConfigManager", "*", "meonly");
|
||||||
session_.subscribe("statistics", "*", "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 (...) {
|
} catch (...) {
|
||||||
throw std::runtime_error("SessionManager: failed to open sessions");
|
throw std::runtime_error("SessionManager: failed to open sessions");
|
||||||
}
|
}
|
||||||
@@ -88,12 +134,12 @@ CommandSession::getCommand(int counter) {
|
|||||||
return std::pair<string, string>("unknown", "");
|
return std::pair<string, string>("unknown", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// should be replaced by the general config-getter in cc setup
|
||||||
std::vector<std::string>
|
std::vector<std::string>
|
||||||
CommandSession::getZones() {
|
CommandSession::getZones() {
|
||||||
ElementPtr cmd, result, env;
|
ElementPtr cmd, result, env;
|
||||||
std::vector<std::string> zone_names;
|
std::vector<std::string> zone_names;
|
||||||
cmd = Element::create_from_string("{ \"command\": [ \"zone\", \"list\" ] }");
|
cmd = Element::create_from_string("{ \"command\": [ \"zone\", \"list\" ] }");
|
||||||
sleep(1);
|
|
||||||
session_.group_sendmsg(cmd, "ConfigManager");
|
session_.group_sendmsg(cmd, "ConfigManager");
|
||||||
session_.group_recvmsg(env, result, false);
|
session_.group_recvmsg(env, result, false);
|
||||||
BOOST_FOREACH(ElementPtr zone_name, result->get("result")->list_value()) {
|
BOOST_FOREACH(ElementPtr zone_name, result->get("result")->list_value()) {
|
||||||
|
@@ -20,6 +20,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <cc/cpp/session.h>
|
#include <cc/cpp/session.h>
|
||||||
|
#include <cc/cpp/data_def.h>
|
||||||
|
#include <cc/cpp/data.h>
|
||||||
|
|
||||||
class CommandSession {
|
class CommandSession {
|
||||||
public:
|
public:
|
||||||
@@ -28,7 +30,11 @@ public:
|
|||||||
std::pair<std::string, std::string> getCommand(int counter);
|
std::pair<std::string, std::string> getCommand(int counter);
|
||||||
std::vector<std::string> getZones();
|
std::vector<std::string> getZones();
|
||||||
private:
|
private:
|
||||||
|
void read_data_definition(const std::string& filename);
|
||||||
|
|
||||||
ISC::CC::Session session_;
|
ISC::CC::Session session_;
|
||||||
|
ISC::Data::DataDefinition data_definition_;
|
||||||
|
ISC::Data::ElementPtr config_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __CCSESSION_H
|
#endif // __CCSESSION_H
|
||||||
|
@@ -112,7 +112,6 @@ if __name__ == "__main__":
|
|||||||
signal.signal(signal.SIGINT, signal_handler)
|
signal.signal(signal.SIGINT, signal_handler)
|
||||||
signal.signal(signal.SIGTERM, signal_handler)
|
signal.signal(signal.SIGTERM, signal_handler)
|
||||||
cm.read_config()
|
cm.read_config()
|
||||||
# do loading here if necessary
|
|
||||||
cm.notify_boss()
|
cm.notify_boss()
|
||||||
cm.run()
|
cm.run()
|
||||||
cm.write_config()
|
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
|
bin_PROGRAMS = test
|
||||||
test_SOURCES = test.cc
|
test_SOURCES = test.cc
|
||||||
|
@@ -5,14 +5,150 @@
|
|||||||
|
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
|
|
||||||
|
// todo: add more context to thrown DataDefinitionErrors?
|
||||||
|
|
||||||
using namespace ISC::Data;
|
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);
|
definition = Element::create_from_string(in);
|
||||||
// TODO, make sure the whole structure is complete and valid
|
// make sure the whole structure is complete and valid
|
||||||
if (!definition->contains("data_specification")) {
|
if (check) {
|
||||||
throw ParseError("Data specification does not contain data_specification element");
|
check_definition(definition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +236,6 @@ bool
|
|||||||
DataDefinition::validate_spec_list(const ElementPtr spec, const ElementPtr data) {
|
DataDefinition::validate_spec_list(const ElementPtr spec, const ElementPtr data) {
|
||||||
ElementPtr cur_data_el;
|
ElementPtr cur_data_el;
|
||||||
std::string cur_item_name;
|
std::string cur_item_name;
|
||||||
bool optional;
|
|
||||||
BOOST_FOREACH(ElementPtr cur_spec_el, spec->list_value()) {
|
BOOST_FOREACH(ElementPtr cur_spec_el, spec->list_value()) {
|
||||||
if (!validate_spec(cur_spec_el, data)) {
|
if (!validate_spec(cur_spec_el, data)) {
|
||||||
return false;
|
return false;
|
||||||
|
@@ -7,11 +7,22 @@
|
|||||||
|
|
||||||
namespace ISC { namespace Data {
|
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 {
|
class DataDefinition {
|
||||||
public:
|
public:
|
||||||
explicit DataDefinition() {};
|
explicit DataDefinition() {};
|
||||||
explicit DataDefinition(ElementPtr e) : definition(e) {};
|
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; };
|
const ElementPtr getDefinition() { return definition; };
|
||||||
// returns true if the given element conforms to this data
|
// returns true if the given element conforms to this data
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"data_specification": {
|
"data_specification": {
|
||||||
"module_name": "parkinglot",
|
"module_name": "ParkingLot",
|
||||||
"config_data": [
|
"config_data": [
|
||||||
{
|
{
|
||||||
"item_name": "port",
|
"item_name": "port",
|
||||||
@@ -24,21 +24,21 @@
|
|||||||
"commands": [
|
"commands": [
|
||||||
{
|
{
|
||||||
"command_name": "zone_add",
|
"command_name": "zone_add",
|
||||||
"command_args": {
|
"command_args": [ {
|
||||||
"item_name": "zone_name",
|
"item_name": "zone_name",
|
||||||
"item_type": "string",
|
"item_type": "string",
|
||||||
"item_optional": false,
|
"item_optional": false,
|
||||||
"item_default": ""
|
"item_default": ""
|
||||||
}
|
} ]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command_name": "zone_delete",
|
"command_name": "zone_delete",
|
||||||
"command_args": {
|
"command_args": [ {
|
||||||
"item_name": "zone_name",
|
"item_name": "zone_name",
|
||||||
"item_type": "string",
|
"item_type": "string",
|
||||||
"item_optional": false,
|
"item_optional": false,
|
||||||
"item_default": ""
|
"item_default": ""
|
||||||
}
|
} ]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@@ -138,7 +138,8 @@ Session::unsubscribe(std::string group, std::string instance)
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsigned int
|
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>());
|
ElementPtr env = Element::create(std::map<std::string, ElementPtr>());
|
||||||
|
|
||||||
|
@@ -43,7 +43,7 @@ namespace ISC {
|
|||||||
std::string subtype = "normal");
|
std::string subtype = "normal");
|
||||||
void unsubscribe(std::string group,
|
void unsubscribe(std::string group,
|
||||||
std::string instance = "*");
|
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 group,
|
||||||
std::string instance = "*",
|
std::string instance = "*",
|
||||||
std::string to = "*");
|
std::string to = "*");
|
||||||
|
@@ -27,6 +27,9 @@ main(int argc, char **argv) {
|
|||||||
} catch (ParseError pe) {
|
} catch (ParseError pe) {
|
||||||
cout << "Error parsing definition file: " << pe.what() << endl;
|
cout << "Error parsing definition file: " << pe.what() << endl;
|
||||||
return 1;
|
return 1;
|
||||||
|
} catch (DataDefinitionError dde) {
|
||||||
|
cout << "Error reading definition file: " << dde.what() << endl;
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user