Commit e0f80ec2 authored by Jelte Jansen's avatar Jelte Jansen
Browse files

common module-side API for commands and configuration


git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jelte-datadef@394 e5f2f494-b856-4b98-b285-d166d9295462
parent 1af07bbe
......@@ -71,23 +71,48 @@ CommandSession::read_data_definition(const std::string& filename) {
file.close();
}
CommandSession::CommandSession() :
CommandSession::CommandSession(std::string module_name,
std::string spec_file_name,
ISC::Data::ElementPtr(*config_handler)(ISC::Data::ElementPtr new_config),
ISC::Data::ElementPtr(*command_handler)(ISC::Data::ElementPtr command)
) :
module_name_(module_name),
session_(ISC::CC::Session())
{
config_handler_ = config_handler;
command_handler_ = command_handler;
try {
// todo: workaround, let boss wait until msgq is started
// and remove sleep here
sleep(1);
ElementPtr answer, env;
session_.establish();
session_.subscribe("ParkingLot", "*");
session_.subscribe(module_name, "*");
session_.subscribe("Boss", "*", "meonly");
session_.subscribe("ConfigManager", "*", "meonly");
session_.subscribe("statistics", "*", "meonly");
read_data_definition("parkinglot.spec");
read_data_definition(spec_file_name);
sleep(1);
ElementPtr cmd = Element::create_from_string("{ \"config_manager\": 1}");
// why does the msgq seem to kill this msg?
// send the data specification
session_.group_sendmsg(data_definition_.getDefinition(), "ConfigManager");
session_.group_recvmsg(env, answer, false);
// get any stored configuration from the manager
if (config_handler_) {
ElementPtr cmd = Element::create_from_string("{ \"command\": [ \"get_config\", \"" + module_name + "\" ] }");
session_.group_sendmsg(cmd, "ConfigManager");
session_.group_recvmsg(env, answer, false);
cout << "[XX] got config: " << endl << answer->str() << endl;
// replace string_value and "0" with int_value and 0 with new cc after merge */
if (answer->contains("result") && answer->get("result")->get(0)->string_value() == "0") {
config_handler(answer->get("result")->get(1));
} else {
cout << "[XX] no result in answer" << endl;
}
}
} catch (...) {
throw std::runtime_error("SessionManager: failed to open sessions");
}
......@@ -99,58 +124,37 @@ CommandSession::getSocket()
return (session_.getSocket());
}
std::pair<std::string, ElementPtr>
CommandSession::getCommand(int counter) {
ElementPtr cmd, routing, data, ep;
string s;
session_.group_recvmsg(routing, data, false);
string channel = routing->get("group")->string_value();
if (channel == "statistics") {
cmd = data->get("command");
if (cmd != NULL && cmd->string_value() == "getstat") {
struct timeval now;
ElementPtr resp = Element::create(std::map<std::string,
ElementPtr>());
gettimeofday(&now, NULL);
resp->set("sent", Element::create(now.tv_sec +
(double)now.tv_usec / 1000000));
resp->set("counter", Element::create(counter));
session_.group_sendmsg(resp, "statistics");
int
CommandSession::check_command()
{
cout << "[XX] check for command" << endl;
ElementPtr cmd, routing, data;
if (session_.group_recvmsg(routing, data, true)) {
/* ignore result messages (in case we're out of sync, to prevent
* pingpongs */
if (!data->get_type() == Element::map || data->contains("result")) {
return 0;
}
} else {
cout << "[parkinglot] saw message: " << data << endl;
// todo: common interface for config updates?
cmd = data->get("config_update");
if (cmd != NULL) {
return std::pair<string, ElementPtr>("config_update", cmd);
cout << "[XX] got something!" << endl << data->str() << endl;
ElementPtr answer;
if (data->contains("config_update")) {
if (config_handler_) {
// handle config update
answer = config_handler_(data->get("config_update"));
} else {
answer = Element::create_from_string("{ \"result\": [0] }");
}
}
// todo: common interface for command handling
cmd = data->get("command");
// the format is defined partly by convention;
// { "command": [ "module", "command", args... ]
// args is defined in the .spec file
// we could do checking here as well if we want
if (cmd != NULL && cmd->get(1)->string_value() == "print_message") {
cout << "[parkinglot] " << cmd->get(2)->string_value() << endl;
ElementPtr answer = Element::create_from_string("{ \"result\": [0] }");
session_.reply(routing, answer);
if (data->contains("command")) {
if (command_handler_) {
answer = command_handler_(data->get("command"));
} else {
answer = Element::create_from_string("{ \"result\": [0] }");
}
}
session_.reply(routing, answer);
}
return std::pair<string, ElementPtr>("unknown", ElementPtr());
return 0;
}
// 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\" ] }");
session_.group_sendmsg(cmd, "ConfigManager");
session_.group_recvmsg(env, result, false);
BOOST_FOREACH(ElementPtr zone_name, result->get("result")->list_value()) {
zone_names.push_back(zone_name->string_value());
}
return zone_names;
}
......@@ -25,16 +25,61 @@
class CommandSession {
public:
CommandSession();
/**
* Initialize a config/command session
* @param module_name: The name of this module. This is not a
* reference because we expect static strings
* to be passed here.
* @param spec_file_name: The name of the file containing the data
* definition.
*/
CommandSession(std::string module_name, std::string spec_file_name,
ISC::Data::ElementPtr(*config_handler)(ISC::Data::ElementPtr new_config) = NULL,
ISC::Data::ElementPtr(*command_handler)(ISC::Data::ElementPtr command) = NULL
);
int getSocket();
std::pair<std::string, ISC::Data::ElementPtr> getCommand(int counter);
std::vector<std::string> getZones();
/**
* Check if there is a command or config change on the command
* session. If so, the appropriate handler is called if set.
* If not set, a default answer is returned.
* This is a non-blocking read; if there is nothing this function
* will return 0.
*/
int check_command();
/**
* The config handler function should expect an ElementPtr containing
* the full configuration where non-default values have been set.
* Later we might want to think about more granular control
* (i.e. this does not scale to for instance lists containing
* 100000 zones, where the whole list is passed every time a single
* thing changes)
*/
void set_config_handler(ISC::Data::ElementPtr(*config_handler)(ISC::Data::ElementPtr new_config)) { config_handler_ = config_handler; };
/**
* Set a command handler; the function that is passed takes an
* ElementPtr, pointing to a list element, containing
* [ module_name, command_name, arg1, arg2, ... ]
* The returned ElementPtr should look like
* { "result": [ return_value, result_value ] }
* result value here is optional and depends on the command
*
* This protocol is very likely to change.
*/
void set_command_handler(ISC::Data::ElementPtr(*command_handler)(ISC::Data::ElementPtr command)) { command_handler_ = command_handler; };
private:
void read_data_definition(const std::string& filename);
void read_data_definition(const std::string& filename);
std::string module_name_;
ISC::CC::Session session_;
ISC::Data::DataDefinition data_definition_;
ISC::Data::ElementPtr config_;
ISC::Data::ElementPtr(*config_handler_)(ISC::Data::ElementPtr new_config);
ISC::Data::ElementPtr(*command_handler_)(ISC::Data::ElementPtr command);
};
#endif // __CCSESSION_H
......
......@@ -39,10 +39,18 @@
#include "common.h"
#include <boost/foreach.hpp>
using namespace std;
const string PROGRAM = "parkinglot";
const int DNSPORT = 53;
const string PROGRAM = "ParkingLot";
const string SPECFILE = "parkinglot.spec";
const int DNSPORT = 5300;
/* need global var for config/command handlers.
* todo: turn this around, and put handlers in the parkinglot
* class itself */
ParkingLot plot = ParkingLot(DNSPORT);
static void
usage() {
......@@ -50,6 +58,38 @@ usage() {
exit(1);
}
ISC::Data::ElementPtr
my_config_handler(ISC::Data::ElementPtr config)
{
cout << "[XX] Handle config: " << endl << config->str() << endl;
if (config->contains("zones")) {
plot.clear_zones();
BOOST_FOREACH(ISC::Data::ElementPtr zone_el, config->get("zones")->list_value()) {
plot.serve(zone_el->string_value());
}
}
if (config->contains("port")) {
// todo: what to do with port change. restart automatically?
// ignore atm
}
return ISC::Data::Element::create_from_string("{ \"result\": [0] }");
}
ISC::Data::ElementPtr
my_command_handler(ISC::Data::ElementPtr command)
{
ISC::Data::ElementPtr answer = ISC::Data::Element::create_from_string("{ \"result\": [0] }");
cout << "[XX] Handle command: " << endl << command->str() << endl;
if (command->get(1)->string_value() == "print_message") {
cout << command->get(2)->string_value() << endl;
/* let's add that message to our answer as well */
answer->get("result")->add(command->get(2));
}
return answer;
}
int
main(int argc, char* argv[]) {
int ch;
......@@ -70,18 +110,20 @@ main(int argc, char* argv[]) {
usage();
// initialize parking lot
ParkingLot plot(port);
//plot = ParkingLot(port);
// initialize command channel
CommandSession cs;
CommandSession cs = CommandSession(PROGRAM, SPECFILE, my_config_handler, my_command_handler);
//cs.set_config_handler(my_config_handler);
//cs.set_command_handler(my_command_handler);
// main server loop
fd_set fds;
int ps = plot.getSocket();
int ss = cs.getSocket();
BOOST_FOREACH(std::string zone, cs.getZones()) {
/*BOOST_FOREACH(std::string zone, cs.getZones()) {
plot.serve(zone);
}
}*/
int nfds = max(ps, ss) + 1;
int counter = 0;
......@@ -100,9 +142,11 @@ main(int argc, char* argv[]) {
plot.processMessage();
}
/* isset not really necessary, but keep it for now */
if (FD_ISSET(ss, &fds)) {
pair<string, ISC::Data::ElementPtr> cmd = cs.getCommand(counter);
plot.command(cmd);
cs.check_command();
//pair<string, ISC::Data::ElementPtr> cmd = cs.getCommand(counter);
//plot.command(cmd);
}
}
......
......@@ -28,6 +28,7 @@ public:
void processMessage();
void command(std::pair<std::string,ISC::Data::ElementPtr>);
void serve(std::string zone_name);
void clear_zones() { zones.clear_zones(); };
private:
isc::dns::Rdata::RdataPtr ns1, ns2, ns3, a, aaaa, soa;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment