[svn] commit: r405 - in /branches/parkinglot: ./ src/bin/bigtool/ src/bin/msgq/ src/bin/parkinglot/ src/lib/bigtool/ src/lib/bind-cfgd/python/ src/lib/cc/cpp/ src/lib/cc/python/ISC/CC/

BIND 10 source code commits bind10-changes at lists.isc.org
Thu Dec 24 10:00:34 UTC 2009


Author: jelte
Date: Thu Dec 24 10:00:34 2009
New Revision: 405

Log:
merged branch jelte_datadef back
Configuration syntax has changed; see README
Also the parkinglot.db format has changed and 'old' configuration will be
thrown away.
Merge back done to facilitate refactoring of names to conform to style guidelines (doing that in a separate branch could create too many conflicts)
next up: tests and documentation

Added:
    branches/parkinglot/src/bin/parkinglot/config.h.in
      - copied unchanged from r399, branches/jelte-datadef/src/bin/parkinglot/config.h.in
    branches/parkinglot/src/bin/parkinglot/parkinglot.spec
      - copied unchanged from r399, branches/jelte-datadef/src/bin/parkinglot/parkinglot.spec
    branches/parkinglot/src/lib/cc/cpp/data_def.cc
      - copied unchanged from r399, branches/jelte-datadef/src/lib/cc/cpp/data_def.cc
    branches/parkinglot/src/lib/cc/cpp/data_def.h
      - copied unchanged from r399, branches/jelte-datadef/src/lib/cc/cpp/data_def.h
    branches/parkinglot/src/lib/cc/cpp/parkinglot.data
      - copied unchanged from r399, branches/jelte-datadef/src/lib/cc/cpp/parkinglot.data
    branches/parkinglot/src/lib/cc/cpp/parkinglot.spec
      - copied unchanged from r399, branches/jelte-datadef/src/lib/cc/cpp/parkinglot.spec
    branches/parkinglot/src/lib/cc/cpp/test.cc
      - copied unchanged from r399, branches/jelte-datadef/src/lib/cc/cpp/test.cc
    branches/parkinglot/src/lib/cc/python/ISC/CC/data.py
      - copied, changed from r399, branches/jelte-datadef/src/lib/cc/python/ISC/CC/data.py
Modified:
    branches/parkinglot/   (props changed)
    branches/parkinglot/README
    branches/parkinglot/configure.ac
    branches/parkinglot/src/bin/bigtool/run_bigtool.py
    branches/parkinglot/src/bin/msgq/msgq.py
    branches/parkinglot/src/bin/parkinglot/ccsession.cc
    branches/parkinglot/src/bin/parkinglot/ccsession.h
    branches/parkinglot/src/bin/parkinglot/main.cc
    branches/parkinglot/src/bin/parkinglot/parkinglot.cc
    branches/parkinglot/src/bin/parkinglot/parkinglot.h
    branches/parkinglot/src/bin/parkinglot/zoneset.h
    branches/parkinglot/src/lib/bigtool/bigtool.py
    branches/parkinglot/src/lib/bigtool/moduleinfo.py
    branches/parkinglot/src/lib/bind-cfgd/python/bind-cfgd.py
    branches/parkinglot/src/lib/cc/cpp/Makefile.am
    branches/parkinglot/src/lib/cc/cpp/data.cc
    branches/parkinglot/src/lib/cc/cpp/data.h
    branches/parkinglot/src/lib/cc/cpp/session.cc
    branches/parkinglot/src/lib/cc/cpp/session.h
    branches/parkinglot/src/lib/cc/python/ISC/CC/session.py

Modified: branches/parkinglot/README
==============================================================================
--- branches/parkinglot/README (original)
+++ branches/parkinglot/README Thu Dec 24 10:00:34 2009
@@ -1,0 +1,89 @@
+
+BUILDING
+
+Simple build instructions:
+
+autoreconf
+./configure
+make
+
+
+RUNNING
+
+At the moment there is no install yet, you can run the bind10 parkinglot
+server from the source tree:
+./src/bin/bind10/bind10
+
+The server will listen on port 5300 for DNS requests.
+
+
+CONFIGURATION
+
+Commands can be given through the tool bigtool;
+cd src/bin/bigtool
+sh run_bigtool
+
+The server must be running for bigtool to work.
+
+The following configuration commands are available
+
+help: show the different command modules
+<module> help: show the commands for module
+<module> <command> help: show info for the command
+
+
+config show [identifier]: Show the currently set values. If no identifier is
+                          given, the current location is used. If a config
+                          option is a list or a map, the value is not
+                          shown directly, but must be requested separately.
+config go [identifier]:   Go to the given location within the configuration.
+config set [identifier] <value>: Set a configuration value.
+config unset [identifier]: Remove a value (reverts to default if the option
+                           is mandatory).
+config add [identifier] <value>: add a value to a list
+config remove [identifier] <value>: remove a value from a list 
+config revert:	Revert all changes that have not been committed
+config commit: Commit all changes
+
+
+EXAMPLE SESSION
+
+~> sh run_bigtool
+> config show
+ParkingLot/	module	
+> config show ParkingLot/
+port:	5300	integer	(default)
+zones/	list	
+a_records/	list	(default)
+aaaa_records/	list	(default)
+ns_records/	list	(default)
+> config go ParkingLot/
+/ParkingLot> config show
+port:	5300	integer	(default)
+zones/	list	
+a_records/	list	(default)
+aaaa_records/	list	(default)
+ns_records/	list	(default)
+/ParkingLot> config show zones
+/ParkingLot> config add zone tjeb.nl
+Error: /ParkingLot/zone not found
+/ParkingLot> config add zones tjeb.nl
+/ParkingLot> config show zones
+zone_name:	tjeb.nl	string	
+/ParkingLot> config show
+port:	5300	integer	(default)
+zones/	list	(modified)
+a_records/	list	(default)
+aaaa_records/	list	(default)
+ns_records/	list	(default)
+/ParkingLot> config go /
+> config show ParkingLot/port
+port:	5300	integer	(default)
+> config go ParkingLot/a_records/
+/ParkingLot/a_records> config show
+address:	127.0.0.1	string	
+/ParkingLot/a_records> config add "127.0.0.2"
+/ParkingLot/a_records> config show
+address:	127.0.0.2	string	
+/ParkingLot/a_records> 
+

Modified: branches/parkinglot/configure.ac
==============================================================================
--- branches/parkinglot/configure.ac (original)
+++ branches/parkinglot/configure.ac Thu Dec 24 10:00:34 2009
@@ -73,6 +73,7 @@
            src/bin/bind10/bind10
 	   src/bin/msgq/msgq
 	   src/bin/msgq/msgq_test
+           src/bin/parkinglot/config.h
           ], [
            chmod +x src/bin/bind-cfgd/bind-cfgd
            chmod +x src/bin/bigtool/run_bigtool

Modified: branches/parkinglot/src/bin/bigtool/run_bigtool.py
==============================================================================
--- branches/parkinglot/src/bin/bigtool/run_bigtool.py (original)
+++ branches/parkinglot/src/bin/bigtool/run_bigtool.py Thu Dec 24 10:00:34 2009
@@ -4,20 +4,6 @@
 
 
 def _prepare_fake_data(bigtool):
-    add_cmd = CommandInfo(name = "add", desc = "add one zone")
-    remove_cmd = CommandInfo(name = 'remove', desc = 'remove one zone')
-    list_cmd = CommandInfo(name = 'list', desc = 'list all zones', 
-                           need_inst_param = False)                                                                                           
-
-    zone_module = ModuleInfo(name = "zone", 
-                             inst_name = "zone_name", 
-                             inst_type = STRING_TYPE, 
-                             inst_desc = "the name of one zone",
-                             desc = "manage all the zones")
-    zone_module.add_command(add_cmd)
-    zone_module.add_command(remove_cmd)
-    zone_module.add_command(list_cmd)
-
     shutdown_param = ParamInfo(name = "module_name", desc = "the name of module")
     shutdown_cmd = CommandInfo(name = 'shutdown', desc = "stop bind10",
                                need_inst_param = False)
@@ -25,18 +11,70 @@
     boss_module = ModuleInfo(name = "boss", desc = "boss of bind10")
     boss_module.add_command(shutdown_cmd)               
 
-    bigtool.add_module_info(zone_module)
     bigtool.add_module_info(boss_module)
+
+def prepare_commands(bigtool, command_spec):
+    for module_name in command_spec.keys():
+        bigtool.prepare_module_commands(module_name, command_spec[module_name])
+
+def prepare_config_commands(bigtool):
+    module = ModuleInfo(name = "config", desc = "Configuration commands")
+    cmd = CommandInfo(name = "show", desc = "Show configuration", need_inst_param = False)
+    param = ParamInfo(name = "identifier", type = "string", optional=True)
+    cmd.add_param(param)
+    module.add_command(cmd)
+
+    cmd = CommandInfo(name = "add", desc = "Add entry to configuration list", need_inst_param = False)
+    param = ParamInfo(name = "identifier", type = "string", optional=True)
+    cmd.add_param(param)
+    param = ParamInfo(name = "value", type = "string", optional=False)
+    cmd.add_param(param)
+    module.add_command(cmd)
+
+    cmd = CommandInfo(name = "remove", desc = "Remove entry from configuration list", need_inst_param = False)
+    param = ParamInfo(name = "identifier", type = "string", optional=True)
+    cmd.add_param(param)
+    param = ParamInfo(name = "value", type = "string", optional=False)
+    cmd.add_param(param)
+    module.add_command(cmd)
+
+    cmd = CommandInfo(name = "set", desc = "Set a configuration value", need_inst_param = False)
+    param = ParamInfo(name = "identifier", type = "string", optional=True)
+    cmd.add_param(param)
+    param = ParamInfo(name = "value", type = "string", optional=False)
+    cmd.add_param(param)
+    module.add_command(cmd)
+
+    cmd = CommandInfo(name = "unset", desc = "Unset a configuration value", need_inst_param = False)
+    param = ParamInfo(name = "identifier", type = "string", optional=False)
+    cmd.add_param(param)
+    module.add_command(cmd)
+
+    cmd = CommandInfo(name = "revert", desc = "Revert all local changes", need_inst_param = False)
+    module.add_command(cmd)
+
+    cmd = CommandInfo(name = "commit", desc = "Commit all local changes", need_inst_param = False)
+    module.add_command(cmd)
+
+    cmd = CommandInfo(name = "go", desc = "Go to a specific configuration part", need_inst_param = False)
+    param = ParamInfo(name = "identifier", type="string", optional=False)
+    cmd.add_param(param)
+    module.add_command(cmd)
+
+    bigtool.add_module_info(module)
     
 
 if __name__ == '__main__':
     try:
         cc = ISC.CC.Session()
-        #cc.group_subscribe("BigTool", "*", "meonly")
-        #cc.group_subscribe("ConfigManager", "*", "meonly")
-        #cc.group_subscribe("Boss", "*", "meonly")
+        cc.group_subscribe("BigTool", "*")
+        cc.group_subscribe("Boss", "*")
 
         tool = BigTool(cc)
+        cc.group_sendmsg({ "command": ["get_commands"] }, "ConfigManager")
+        command_spec, env =  cc.group_recvmsg(False)
+        prepare_commands(tool, command_spec["result"][1])
+        prepare_config_commands(tool)
         _prepare_fake_data(tool)   
         tool.cmdloop()
     except ISC.CC.SessionError:

Modified: branches/parkinglot/src/bin/msgq/msgq.py
==============================================================================
--- branches/parkinglot/src/bin/msgq/msgq.py (original)
+++ branches/parkinglot/src/bin/msgq/msgq.py Thu Dec 24 10:00:34 2009
@@ -197,6 +197,8 @@
     def process_command(self, fd, sock, routing, data):
         """Process a single command.  This will split out into one of the
            other functions, above."""
+        print("[XX] got command: ")
+        print(routing)
         cmd = routing["type"]
         if cmd == 'send':
             self.process_command_send(sock, routing, data)

Modified: branches/parkinglot/src/bin/parkinglot/ccsession.cc
==============================================================================
--- branches/parkinglot/src/bin/parkinglot/ccsession.cc (original)
+++ branches/parkinglot/src/bin/parkinglot/ccsession.cc Thu Dec 24 10:00:34 2009
@@ -14,38 +14,103 @@
 
 // $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"
 #include "ccsession.h"
+#include "config.h"
 
 using namespace std;
 
 using ISC::Data::Element;
 using ISC::Data::ElementPtr;
+using ISC::Data::DataDefinition;
+using ISC::Data::ParseError;
+using ISC::Data::DataDefinitionError;
 
-CommandSession::CommandSession() :
+void
+CommandSession::read_data_definition(const std::string& filename) {
+    std::ifstream file;
+
+    // this file should be declared in a @something@ directive
+    file.open(PARKINGLOT_SPECFILE_LOCATION);
+    if (!file) {
+        cout << "error opening " << PARKINGLOT_SPECFILE_LOCATION << endl;
+        exit(1);
+    }
+
+    try {
+        data_definition_ = DataDefinition(file, true);
+    } 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(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())
 {
-    try {
-        session_.establish();
-        session_.subscribe("ParkingLot", "*");
-        session_.subscribe("Boss", "ParkingLot");
-        //session_.subscribe("ConfigManager", "*", "meonly");
-        //session_.subscribe("statistics", "*", "meonly");
-    } catch (...) {
-        throw std::runtime_error("SessionManager: failed to open sessions");
+    config_handler_ = config_handler;
+    command_handler_ = command_handler;
+
+    // todo: workaround, let boss wait until msgq is started
+    // and remove sleep here
+    sleep(1);
+
+    ElementPtr answer, env;
+
+    session_.establish();
+    session_.subscribe(module_name, "*");
+    session_.subscribe("Boss", "*");
+    session_.subscribe("statistics", "*");
+    read_data_definition(spec_file_name);
+    sleep(1);
+    // 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;
+        if (answer->contains("result") &&
+            answer->get("result")->get(0)->int_value() == 0 &&
+            answer->get("result")->size() > 1) {
+            config_handler(answer->get("result")->get(1));
+        } else {
+            cout << "[XX] no result in answer" << endl;
+        }
     }
 }
 
@@ -55,55 +120,37 @@
     return (session_.getSocket());
 }
 
-std::pair<std::string, std::string>
-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 {
-        cmd = data->get("zone_added");
-        if (cmd != NULL)
-            return std::pair<string, string>("addzone", cmd->string_value());
-        cmd = data->get("zone_deleted");
-        if (cmd != NULL) {
-            return std::pair<string, string>("delzone", cmd->string_value());
-        }
-        cmd = data->get("command");
-        if (cmd != NULL) {
-            if (cmd->get_type() == Element::string && cmd->string_value() == "shutdown") {
-                return std::pair<string, string>("shutdown", "");
+        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] }");
             }
         }
+        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, string>("unknown", "");
+    
+    return 0;
 }
 
-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()) {
-        zone_names.push_back(zone_name->string_value());
-    }
-    return zone_names;
-}

Modified: branches/parkinglot/src/bin/parkinglot/ccsession.h
==============================================================================
--- branches/parkinglot/src/bin/parkinglot/ccsession.h (original)
+++ branches/parkinglot/src/bin/parkinglot/ccsession.h Thu Dec 24 10:00:34 2009
@@ -20,15 +20,66 @@
 #include <string>
 
 #include <cc/cpp/session.h>
+#include <cc/cpp/data_def.h>
+#include <cc/cpp/data.h>
 
 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, std::string> 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);
+    
+    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

Modified: branches/parkinglot/src/bin/parkinglot/main.cc
==============================================================================
--- branches/parkinglot/src/bin/parkinglot/main.cc (original)
+++ branches/parkinglot/src/bin/parkinglot/main.cc Thu Dec 24 10:00:34 2009
@@ -31,6 +31,7 @@
 #include <dns/message.h>
 
 #include <cc/cpp/session.h>
+#include <cc/cpp/data.h>
 
 #include "zoneset.h"
 #include "parkinglot.h"
@@ -38,15 +39,75 @@
 
 #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() {
     cerr << "Usage: parkinglot [-p port]" << endl;
     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
+    }
+    if (config->contains("a_records")) {
+        plot.clearARecords();
+        BOOST_FOREACH(ISC::Data::ElementPtr rel, config->get("a_records")->list_value()) {
+            plot.addARecord(rel->string_value());
+        }
+    }
+    if (config->contains("aaaa_records")) {
+        plot.clearAAAARecords();
+        BOOST_FOREACH(ISC::Data::ElementPtr rel, config->get("aaaa_records")->list_value()) {
+            plot.addAAAARecord(rel->string_value());
+        }
+    }
+    if (config->contains("ns_records")) {
+        plot.clearNSRecords();
+        BOOST_FOREACH(ISC::Data::ElementPtr rel, config->get("ns_records")->list_value()) {
+            plot.addNSRecord(rel->string_value());
+        }
+    }
+    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 */
+        cout << "[XX] answer was: " << answer->str() << endl;
+        answer->get("result")->add(command->get(2));
+        cout << "[XX] answer now: " << answer->str() << endl;
+    }
+
+    return answer;
 }
 
 int
@@ -69,18 +130,15 @@
         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);
+    
     // main server loop
     fd_set fds;
     int ps = plot.getSocket();
     int ss = cs.getSocket();
-    BOOST_FOREACH(std::string zone, cs.getZones()) {
-        plot.serve(zone);
-    }
     int nfds = max(ps, ss) + 1;
     int counter = 0;
 
@@ -99,9 +157,9 @@
             plot.processMessage();
         }
 
+        /* isset not really necessary, but keep it for now */
         if (FD_ISSET(ss, &fds)) {
-            pair<string,string> cmd = cs.getCommand(counter);
-            plot.command(cmd);
+            cs.check_command();
         }
     }
 

Modified: branches/parkinglot/src/bin/parkinglot/parkinglot.cc
==============================================================================
--- branches/parkinglot/src/bin/parkinglot/parkinglot.cc (original)
+++ branches/parkinglot/src/bin/parkinglot/parkinglot.cc Thu Dec 24 10:00:34 2009
@@ -28,21 +28,60 @@
 #include <dns/rrset.h>
 #include <dns/message.h>
 
+#include <cc/cpp/data.h>
+
 #include "common.h"
 #include "parkinglot.h"
+
+#include <boost/foreach.hpp>
 
 using namespace std;
 
 using namespace isc::dns;
 using namespace isc::dns::Rdata::IN;
 using namespace isc::dns::Rdata::Generic;
+using namespace ISC::Data;
+
+void
+ParkingLot::addARecord(std::string data) {
+    a_records.push_back(Rdata::RdataPtr(new A(data)));
+}
+
+void
+ParkingLot::addAAAARecord(std::string data) {
+    aaaa_records.push_back(Rdata::RdataPtr(new AAAA(data)));
+}
+
+void
+ParkingLot::addNSRecord(std::string data) {
+    ns_records.push_back(Rdata::RdataPtr(new NS(data)));
+}
+
+void
+ParkingLot::setSOARecord(isc::dns::Rdata::RdataPtr soa_record) {
+}
+
+void
+ParkingLot::setDefaultZoneData() {
+    clearARecords();
+    clearAAAARecords();
+    clearNSRecords();
+
+    addARecord("127.0.0.1");
+    addAAAARecord("::1");
+    addNSRecord("ns1.parking.example");
+    addNSRecord("ns2.parking.example");
+    addNSRecord("ns3.parking.example");
+}
 
 ParkingLot::ParkingLot(int port) {
-    ns1 = Rdata::RdataPtr(new NS("ns1.parking.example"));
+    setDefaultZoneData();
+    /*ns1 = Rdata::RdataPtr(new NS("ns1.parking.example"));
     ns2 = Rdata::RdataPtr(new NS("ns2.parking.example"));
     ns3 = Rdata::RdataPtr(new NS("ns3.parking.example"));
     a = Rdata::RdataPtr(new A("127.0.0.1"));
     aaaa = Rdata::RdataPtr(new AAAA("::1"));
+    */
     soa = Rdata::RdataPtr(new SOA("parking.example", "noc.parking.example",
                                         1, 1800, 900, 604800, TTL(86400)));
 
@@ -149,23 +188,26 @@
             msg.setRcode(Message::RCODE_NOERROR);
             RRset* nsset = new RRset(query->getName(), RRClass::IN,
                                      RRType::NS, TTL(3600));
-
-            nsset->addRdata(ns1);
-            nsset->addRdata(ns2);
-            nsset->addRdata(ns3);
+            BOOST_FOREACH(isc::dns::Rdata::RdataPtr ns, ns_records) {
+                nsset->addRdata(ns);
+            }
 
             if (query->getType() == RRType::NS)
                 msg.addRRset(SECTION_ANSWER, RRsetPtr(nsset));
             else if (query->getType() == RRType::A) {
                 msg.addRRset(SECTION_AUTHORITY, RRsetPtr(nsset));
-                RR arr(query->getName(), RRClass::IN, RRType::A, TTL(3600), a);
-
-                msg.addRR(SECTION_ANSWER, arr);
+
+                BOOST_FOREACH(isc::dns::Rdata::RdataPtr a, a_records) {
+                    RR arr(query->getName(), RRClass::IN, RRType::A, TTL(3600), a);
+                    msg.addRR(SECTION_ANSWER, arr);
+                }
             } else if (query->getType() == RRType::AAAA) {
                 msg.addRRset(SECTION_AUTHORITY, RRsetPtr(nsset));
-                RR aaaarr(query->getName(), RRClass::IN, RRType::AAAA,
-                          TTL(3600), aaaa);
-                msg.addRR(SECTION_ANSWER, aaaarr);
+                BOOST_FOREACH(isc::dns::Rdata::RdataPtr aaaa, aaaa_records) {
+                    RR aaaarr(query->getName(), RRClass::IN, RRType::AAAA,
+                              TTL(3600), aaaa);
+                    msg.addRR(SECTION_ANSWER, aaaarr);
+                }
             } else {
                 RR soarr(query->getName(), RRClass::IN, RRType::SOA,
                          TTL(3600), soa);
@@ -184,13 +226,19 @@
 }
 
 void
-ParkingLot::command(pair<string,string> cmd) {
-    if (cmd.first == "addzone")
-        serve(cmd.second);
-    else if (cmd.first == "delzone")
-        zones.forget(cmd.second);
-    else if (cmd.first == "shutdown")
+ParkingLot::command(pair<string,ElementPtr> cmd) {
+    if (cmd.first == "shutdown")
         exit(0);
+    else if (cmd.first == "config_update") {
+        // what to do with port settings?
+        ElementPtr zonelist_el = (cmd.second)->get("zones");
+        // We could walk through both lists and remove and serve
+        // accordingly, or simply clear all and add everything
+        zones.clear_zones();
+        BOOST_FOREACH(ElementPtr zone, zonelist_el->list_value()) {
+            zones.serve(zone->string_value());
+        }
+    }
 }
 
 void

Modified: branches/parkinglot/src/bin/parkinglot/parkinglot.h
==============================================================================
--- branches/parkinglot/src/bin/parkinglot/parkinglot.h (original)
+++ branches/parkinglot/src/bin/parkinglot/parkinglot.h Thu Dec 24 10:00:34 2009
@@ -18,6 +18,7 @@
 #define __PARKINGLOT_H 1
 
 #include "zoneset.h"
+#include <cc/cpp/data.h>
 
 class ParkingLot {
 public:
@@ -25,11 +26,25 @@
     virtual ~ParkingLot() {}
     int getSocket() { return (sock); }
     void processMessage();
-    void command(std::pair<std::string,std::string>);
+    void command(std::pair<std::string,ISC::Data::ElementPtr>);
     void serve(std::string zone_name);
-    
+    void clear_zones() { zones.clear_zones(); };
+
+    void clearARecords() { a_records.clear(); };
+    void clearAAAARecords() { aaaa_records.clear(); };
+    void clearNSRecords() { ns_records.clear(); };
+
+    void addARecord(std::string data);
+    void addAAAARecord(std::string data);
+    void addNSRecord(std::string data);
+
+    void setSOARecord(isc::dns::Rdata::RdataPtr soa_record);
+
 private:
-    isc::dns::Rdata::RdataPtr ns1, ns2, ns3, a, aaaa, soa;
+    void setDefaultZoneData();
+
+    std::vector<isc::dns::Rdata::RdataPtr> a_records, aaaa_records, ns_records;
+    isc::dns::Rdata::RdataPtr soa;
     ZoneSet zones;
     int sock;
 };

Modified: branches/parkinglot/src/bin/parkinglot/zoneset.h
==============================================================================
--- branches/parkinglot/src/bin/parkinglot/zoneset.h (original)
+++ branches/parkinglot/src/bin/parkinglot/zoneset.h Thu Dec 24 10:00:34 2009
@@ -29,6 +29,9 @@
             std::cout << "no longer serving: " << s << std::endl;
             this->erase(s);
         }
+        void clear_zones() {
+            this->clear();
+        }
         bool contains(std::string s) {
             return (this->find(s) != this->end());
         }

Modified: branches/parkinglot/src/lib/bigtool/bigtool.py
==============================================================================
--- branches/parkinglot/src/lib/bigtool/bigtool.py (original)
+++ branches/parkinglot/src/lib/bigtool/bigtool.py Thu Dec 24 10:00:34 2009
@@ -2,11 +2,11 @@
 import readline
 from cmd import Cmd
 from exception import *
-from moduleinfo import ModuleInfo
-from moduleinfo import ParamInfo
+from moduleinfo import *
 from command import BigToolCmd
 from xml.dom import minidom
 import ISC
+import ISC.CC.data
 
 try:
     from collections import OrderedDict
@@ -29,12 +29,18 @@
 
     def __init__(self, session = None):
         Cmd.__init__(self)
-        self.prompt = '> '
+        self.location = ""
+        self.prompt_end = '> '
+        self.prompt = self.prompt_end
         self.ruler = '-'
         self.modules = OrderedDict()
         self.add_module_info(ModuleInfo("help", desc = "Get help for bigtool"))
         self.cc = session
-
+        self.config_data = ISC.CC.data.UIConfigData("", session)
+
+    def postcmd(self, stop, line):
+        self.prompt = self.location + self.prompt_end
+        return stop
 
     def validate_cmd(self, cmd):
         if not cmd.module in self.modules:
@@ -68,6 +74,15 @@
                 # the 'name' must be an integer (ie. the position of
                 # an unnamed argument
                 if type(name) == int:
+                    # lump all extraneous arguments together as one big final one
+                    # todo: check if last param type is a string?
+                    if (param_count > 2):
+                        while (param_count > len(command_info.params) - 1):
+                            params[param_count - 2] += params[param_count - 1]
+                            del(params[param_count - 1])
+                            param_count = len(params)
+                            cmd.params = params.copy()
+
                     # (-1, help is always in the all_params list)
                     if name >= len(all_params) - 1:
                         # add to last known param
@@ -87,13 +102,16 @@
             for name in manda_params:
                 if not name in params and not param_nr in params:
                     raise CmdMissParamSyntaxError(cmd.module, cmd.command, name)
-                              
+                
+                param_nr += 1
                 param_nr += 1
 
     def _handle_cmd(self, cmd):
         #to do, consist xml package and send to bind10
         if cmd.command == "help" or ("help" in cmd.params.keys()):
             self._handle_help(cmd)
+        elif cmd.module == "config":
+            self.apply_config_cmd(cmd)
         else:
             self.apply_cmd(cmd)
 
@@ -123,7 +141,9 @@
                 
     
     def onecmd(self, line):
-        if line == 'EOF'or line.lower() == "quit":
+        # check if there's anything on the cc first
+        self.check_cc_messages()
+        if line == 'EOF' or line.lower() == "quit":
             return True
             
         if line == 'h':
@@ -143,6 +163,11 @@
                 else:                       
                     hints = self._get_param_startswith(cmd.module, cmd.command,
                                                        text)
+                    if cmd.module == "config":
+                        # grm text has been stripped of slashes...
+                        my_text = self.location + "/" + cur_line.rpartition(" ")[2]
+                        list = self.config_data.config.get_item_list(my_text.rpartition("/")[0])
+                        hints.extend([val for val in list if val.startswith(text)])
             except CmdModuleNameFormatError:
                 if not text:
                     hints = list(self.modules.keys())
@@ -162,9 +187,9 @@
 
             except BigToolException:
                 hints = []
-            
+
             self.hint = hints
-            self._append_space_to_hint()
+            #self._append_space_to_hint()
 
         if state < len(self.hint):
             return self.hint[state]
@@ -206,8 +231,35 @@
 
         return []
         
+    def prepare_module_commands(self, module_name, module_commands):
+        module = ModuleInfo(name = module_name,
+                            desc = "same here")
+        for command in module_commands:
+            cmd = CommandInfo(name = command["command_name"],
+                              desc = command["command_description"],
+                              need_inst_param = False)
+            for arg in command["command_args"]:
+                param = ParamInfo(name = arg["item_name"],
+                                  type = arg["item_type"],
+                                  optional = bool(arg["item_optional"]))
+                if ("item_default" in arg):
+                    param.default = arg["item_default"]
+                cmd.add_param(param)
+            module.add_command(cmd)
+        self.add_module_info(module)
+
+    def check_cc_messages(self):
+        (message, env) = self.cc.group_recvmsg(True)
+        while message:
+            if 'commands_update' in message:
+                self.prepare_module_commands(message['commands_update'][0], message['commands_update'][1])
+            elif 'specification_update' in message:
+                self.config_data.config.specification[message['specification_update'][0]] = message['specification_update'][1]
+            (message, env) = self.cc.group_recvmsg(True)
 
     def _parse_cmd(self, line):
+        # check if there's anything on the cc first
+        self.check_cc_messages()
         try:
             cmd = BigToolCmd(line)
             self.validate_cmd(cmd)
@@ -304,7 +356,7 @@
         if not self.cc:
             return
         
-        groupName = (cmd.module == 'boss') and 'Boss' or 'ConfigManager'
+        groupName = cmd.module
         content = [cmd.module, cmd.command]
         values = cmd.params.values()
         if len(values) > 0:
@@ -312,9 +364,12 @@
 
         msg = {"command":content}
         print("begin to send the message...")
+
+        # XXTODO: remove this with new msgq
+        #self.cc.group_subscribe(groupName)
         
         try:   
-            self.cc.group_sendmsg(msg, groupName, instance = 'boss')
+            self.cc.group_sendmsg(msg, groupName)
             print("waiting for %s reply..." % groupName)
 
             reply, env = self.cc.group_recvmsg(False)
@@ -324,3 +379,4 @@
 
 
 
+

Modified: branches/parkinglot/src/lib/bigtool/moduleinfo.py
==============================================================================
--- branches/parkinglot/src/lib/bigtool/moduleinfo.py (original)
+++ branches/parkinglot/src/lib/bigtool/moduleinfo.py Thu Dec 24 10:00:34 2009
@@ -124,6 +124,7 @@
         else:
             raise KeyError(str(pos) + " is not an integer")
     
+
     def need_instance_param(self):
         return self.need_inst_param
 

Modified: branches/parkinglot/src/lib/bind-cfgd/python/bind-cfgd.py
==============================================================================
--- branches/parkinglot/src/lib/bind-cfgd/python/bind-cfgd.py (original)
+++ branches/parkinglot/src/lib/bind-cfgd/python/bind-cfgd.py Thu Dec 24 10:00:34 2009
@@ -1,59 +1,88 @@
 import ISC
-import pickle
 import signal
+import ast
+import pprint
+import os
+from ISC.CC import data
+
+class ConfigManagerData:
+    CONFIG_VERSION = 1
+    DB_FILENAME = "/tmp/parkinglot.db"
     
-class ConfigData:
     def __init__(self):
-        self.zones = {}
-
-    def add_zone(self, zone_name, zone_file):
-        self.zones[zone_name] = zone_file
-
-    def remove_zone(self, zone_name):
-        del self.zones[zone_name]
-
-class ConfigManager:
-    def __init__(self):
-        self.cc = ISC.CC.Session()
-        self.cc.group_subscribe("ConfigManager")
-        self.cc.group_subscribe("Boss", "ConfigManager")
-        self.config = ConfigData()
-        self.db_filename = "/tmp/parkinglot.db"
-        self.running = False
-
-    def notify_boss(self):
-        self.cc.group_sendmsg({"running": "configmanager"}, "Boss")
-
-    def add_zone(self, zone_name):
-        self.config.add_zone(zone_name, "todo")
-        self.write_config()
-        print("sending update zone add")
-        self.cc.group_sendmsg({"zone_added": zone_name }, "ParkingLot")
-
-    def remove_zone(self, zone_name):
-        self.config.remove_zone(zone_name)
-        print("sending update zone del")
-        self.cc.group_sendmsg({"zone_deleted": zone_name }, "ParkingLot")
-
-    def read_config(self):
-        print("Reading config")
+        self.data = {}
+        self.data['version'] = ConfigManagerData.CONFIG_VERSION
+
+    def set_data_definition(self, module_name, module_data_definition):
+        self.zones[module_name] = module_data_definition
+        self.data_definitions[module_name] = module_data_definition
+
+    def read_from_file():
+        config = ConfigManagerData()
         try:
-            file = open(self.db_filename, 'rb');
-            self.config = pickle.load(file)
+            file = open(ConfigManagerData.DB_FILENAME, 'r')
+            file_config = ast.literal_eval(file.read())
+            if 'version' in file_config and \
+                file_config['version'] == ConfigManagerData.CONFIG_VERSION:
+                config.data = file_config
+            else:
+                # of course we can put in a migration path here for old data
+                print("[bind-cfgd] Old version of data found, starting with empty configuration")
             file.close()
         except IOError as ioe:
             print("No config file found, starting with empty config")
         except EOFError as eofe:
             print("Config file empty, starting with empty config")
-
+        except:
+            print("Config file unreadable, starting with empty config")
+
+        return config
+        
+    def write_to_file(self):
+        try:
+            tmp_filename = self.DB_FILENAME + ".tmp"
+            file = open(tmp_filename, 'w');
+            pp = pprint.PrettyPrinter(indent=4)
+            s = pp.pformat(self.data)
+            file.write(s)
+            file.write("\n")
+            file.close()
+            os.rename(tmp_filename, self.DB_FILENAME)
+        except IOError as ioe:
+            print("Unable to write config file; configuration not stored")
+
+class ConfigManager:
+    def __init__(self):
+        self.commands = {}
+        self.data_definitions = {}
+        self.config = ConfigManagerData()
+        self.cc = ISC.CC.Session()
+        self.cc.group_subscribe("ConfigManager")
+        self.cc.group_subscribe("Boss", "ConfigManager")
+        self.running = False
+
+    def notify_boss(self):
+        self.cc.group_sendmsg({"running": "configmanager"}, "Boss")
+
+    def set_config(self, module_name, data_specification):
+        self.data_definitions[module_name] = data_specification
+        
+    def remove_config(self, module_name):
+        self.data_definitions[module_name]
+
+    def set_commands(self, module_name, commands):
+        self.commands[module_name] = commands
+
+    def remove_commands(self, module_name):
+        del self.commands[module_name]
+
+    def read_config(self):
+        print("Reading config")
+        self.config = ConfigManagerData.read_from_file()
+        
     def write_config(self):
         print("Writing config")
-        try:
-            file = open(self.db_filename, 'wb');
-            pickle.dump(self.config, file)
-            file.close()
-        except IOError as ioe:
-            print("Unable to write config file; configuration not stored")
+        self.config.write_to_file()
 
     def handle_msg(self, msg):
         """return answer message"""
@@ -61,7 +90,60 @@
         if "command" in msg:
             cmd = msg["command"]
             try:
-                if cmd[0] == "zone" and cmd[1] == "add":
+                if cmd[0] == "get_commands":
+                    answer["result"] = [ 0, self.commands ]
+                elif cmd[0] == "get_data_spec":
+                    if len(cmd) > 1 and cmd[1] != "":
+                        try:
+                            answer["result"] = [0, self.data_definitions[cmd[1]]]
+                        except KeyError as ke:
+                            answer["result"] = [1, "No specification for module " + cmd[1]]
+                    else:
+                        answer["result"] = [0, self.data_definitions]
+                elif cmd[0] == "get_config":
+                    # we may not have any configuration here
+                    conf_part = None
+                    if len(cmd) > 1:
+                        try:
+                            conf_part = data.find(self.config.data, cmd[1])
+                        except data.DataNotFoundError as dnfe:
+                            pass
+                    else:
+                        conf_part = self.config.data
+                    if conf_part:
+                        answer["result"] = [ 0, conf_part ]
+                    else:
+                        answer["result"] = [ 0 ]
+                elif cmd[0] == "set_config":
+                    if len(cmd) == 3:
+                        # todo: use api (and check types?)
+                        if cmd[1] != "":
+                            conf_part = data.find_no_exc(self.config.data, cmd[1])
+                            if not conf_part:
+                                conf_part = data.set(self.config.data, cmd[1], "")
+                        else:
+                            conf_part = self.config.data
+                        data.merge(conf_part, cmd[2])
+                        print("[XX bind-cfgd] new config (part):")
+                        print(conf_part)
+                        # send out changed info
+                        self.cc.group_sendmsg({ "config_update": conf_part }, cmd[1])
+                        answer["result"] = [ 0 ]
+                    elif len(cmd) == 2:
+                        print("[XX bind-cfgd] old config:")
+                        print(self.config.data)
+                        print("[XX bind-cfgd] updating with:")
+                        print(cmd[1])
+                        data.merge(self.config.data, cmd[1])
+                        print("[XX bind-cfgd] new config:")
+                        print(self.config.data)
+                        # send out changed info
+                        for module in self.config.data:
+                            self.cc.group_sendmsg({ "config_update": self.config.data[module] }, module)
+                        answer["result"] = [ 0 ]
+                    else:
+                        answer["result"] = [ 1, "Wrong number of arguments" ]
+                elif cmd[0] == "zone" and cmd[1] == "add":
                     self.add_zone(cmd[2])
                     answer["result"] = [ 0 ]
                 elif cmd[0] == "zone" and cmd[1] == "remove":
@@ -73,7 +155,7 @@
                         # a separate exception for that?
                         answer["result"] = [ 1, "Unknown zone" ]
                 elif cmd[0] == "zone" and cmd[1] == "list":
-                    answer["result"]     = list(self.config.zones.keys())
+                    answer["result"]     = []#list(self.config.zones.keys())
                 elif cmd == "shutdown":
                     print("[bind-cfgd] Received shutdown command")
                     self.running = False
@@ -82,7 +164,18 @@
                     answer["result"] = [ 1, "Unknown command: " + str(cmd) ]
             except IndexError as ie:
                 print("[bind-cfgd] missing argument")
-                answer["result"] = [ 1, "Missing argument in command" ]
+                answer["result"] = [ 1, "Missing argument in command: " + str(ie) ]
+                raise ie
+        elif "data_specification" in msg:
+            # todo: validate? (no direct access to spec as
+            spec = msg["data_specification"]
+            if "config_data" in spec:
+                self.set_config(spec["module_name"], spec["config_data"])
+                self.cc.group_sendmsg({ "specification_update": [ spec["module_name"], spec["config_data"] ] }, "BigTool")
+            if "commands" in spec:
+                self.set_commands(spec["module_name"], spec["commands"])
+                self.cc.group_sendmsg({ "commands_update": [ spec["module_name"], spec["commands"] ] }, "BigTool")
+            answer["result"] = [ 0 ]
         else:
             print("[bind-cfgd] unknown message: " + str(msg))
             answer["result"] = [ 1, "Unknown module: " + str(msg) ]
@@ -93,13 +186,8 @@
         while (self.running):
             msg, env = self.cc.group_recvmsg(False)
             if msg:
-                print("[bind-cfgd] received message: ")
-                print(msg)
                 answer = self.handle_msg(msg);
-                print("[bind-cfgd] sending answer: ")
-                print(answer)
                 self.cc.group_reply(env, answer)
-                print("[bind-cfgd] answer sent")
             else:
                 self.running = False
 
@@ -116,7 +204,6 @@
         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()

Modified: branches/parkinglot/src/lib/cc/cpp/Makefile.am
==============================================================================
--- branches/parkinglot/src/lib/cc/cpp/Makefile.am (original)
+++ branches/parkinglot/src/lib/cc/cpp/Makefile.am Thu Dec 24 10:00:34 2009
@@ -1,7 +1,10 @@
-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
+test_LDADD = libcc.a
 lib_LIBRARIES = libcc.a
-libcc_a_SOURCES = data.cc data.h session.cc session.h
+libcc_a_SOURCES = data.cc data.h data_def.h data_def.cc session.cc session.h
 
 TESTS =
 if HAVE_GTEST

Modified: branches/parkinglot/src/lib/cc/cpp/data.cc
==============================================================================
--- branches/parkinglot/src/lib/cc/cpp/data.cc (original)
+++ branches/parkinglot/src/lib/cc/cpp/data.cc Thu Dec 24 10:00:34 2009
@@ -32,6 +32,13 @@
     return out << e->str();
 }
 
+const char*
+ParseError::what() const throw() {
+    stringstream ss;
+    ss << msg << " line " << line << " pos " << pos;
+    return ss.str().c_str();
+}
+
 //
 // factory functions
 //
@@ -113,83 +120,113 @@
 }
 
 static void
-skip_chars(std::stringstream &in, const char *chars)
+skip_chars(std::istream &in, const char *chars, int& line, int& pos)
 {
     char c = in.peek();
     while (char_in(c, chars) && c != EOF) {
+        if (c == '\n') {
+            line++;
+            pos = 1;
+        } else {
+            pos++;
+        }
         in.get();
         c = in.peek();
     }
 }
+/*static void
+skip_chars(std::istream &in, const char *chars)
+{
+    int l = 0, p = 0;
+    skip_chars(in, chars, l, p);
+}*/
 
 // skip on the input stream to one of the characters in chars
 // if another character is found this function returns false
 // unles that character is specified in the optional may_skip
 //
 // the character found is left on the stream
-static bool
-skip_to(std::stringstream &in, const char *chars, const char *may_skip="")
+static void
+skip_to(std::istream &in, int& line, int& pos, const char* chars, const char* may_skip="")
 {
     char c = in.get();
+    pos++;
     while (c != EOF) {
+        if (c == '\n') {
+            pos = 1;
+            line++;
+        }
         if (char_in(c, may_skip)) {
             c = in.get();
+            pos++;
         } else if (char_in(c, chars)) {
             while(char_in(in.peek(), may_skip)) {
+                if (in.peek() == '\n') {
+                    pos = 1;
+                    line++;
+                }
                 in.get();
+                pos++;
             }
             in.putback(c);
-            return true;
+            pos--;
+            return;
         } else {
-            // TODO: provide feeback mechanism?
-            cout << "error, '" << c << "' read; one of \"" << chars << "\" expected" << endl;
-            in.putback(c);
-            return false;
-        }
-    }
-    // TODO: provide feeback mechanism?
-    cout << "error, EOF read; one of \"" << chars << "\" expected" << endl;
-            in.putback(c);
-    return false;
-}
+            throw ParseError(std::string("'") + c + "' read, one of \"" + chars + "\" expected", line, pos);
+        }
+    }
+    throw ParseError(std::string("EOF read, one of \"") + chars + "\" expected", line, pos);
+}
+
+/*static bool
+skip_to(std::istream &in, const char *chars, const char *may_skip="") {
+    int line = 0, pos = 0;
+    return skip_to(in, line, pos, chars, may_skip);
+}*/
 
 static std::string
-str_from_stringstream(std::stringstream &in)
+str_from_stringstream(std::istream &in, int& line, int& pos) throw (ParseError)
 {
     char c = 0;
     std::stringstream ss;
     c = in.get();
+    pos++;
     if (c == '"') {
         c = in.get();
-    } else {
-        return "badstring";
+        pos++;
+    } else {
+        throw ParseError("String expected", line, pos);
     }
     while (c != EOF && c != '"') {
         ss << c;
         if (c == '\\' && in.peek() == '"') {
             ss << in.get();
+            pos++;
         }
         c = in.get();
+        pos++;
     }
     return ss.str();
 }
 
 static std::string
-word_from_stringstream(std::stringstream &in)
+word_from_stringstream(std::istream &in, int& line, int& pos)
 {
     std::stringstream ss;
     while (isalpha(in.peek())) {
         ss << (char) in.get();
     }
+    pos += ss.str().size();
     return ss.str();
 }
 
 
 static ElementPtr
-from_stringstream_int_or_double(std::stringstream &in)
+from_stringstream_int_or_double(std::istream &in, int &line, int &pos)
 {
     int i;
     in >> i;
+    // TODO count pos
     if (in.peek() == '.') {
         double d;
         in >> d;
@@ -201,9 +238,9 @@
 }
 
 static ElementPtr
-from_stringstream_bool(std::stringstream &in)
-{
-    std::string word = word_from_stringstream(in);
+from_stringstream_bool(std::istream &in, int& line, int& pos)
+{
+    std::string word = word_from_stringstream(in, line, pos);
     if (boost::iequals(word, "True")) {
         return Element::create(true);
     } else if (boost::iequals(word, "False")) {
@@ -214,65 +251,78 @@
 }
 
 static ElementPtr
-from_stringstream_string(std::stringstream &in)
-{
-    return Element::create(str_from_stringstream(in));
+from_stringstream_string(std::istream &in, int& line, int& pos)
+{
+    return Element::create(str_from_stringstream(in, line, pos));
 }
 
 static ElementPtr
-from_stringstream_list(std::stringstream &in)
+from_stringstream_list(std::istream &in, int& line, int& pos)
 {
     char c = 0;
     std::vector<ElementPtr> v;
     ElementPtr cur_list_element;
-
-    skip_chars(in, " \t\n");
+    //cout << "reading list at line " << line << " pos " << pos << endl;
+
+    skip_chars(in, " \t\n", line, pos);
     while (c != EOF && c != ']') {
-        cur_list_element = Element::create_from_string(in);
-        v.push_back(cur_list_element);
-        if (!skip_to(in, ",]", " \t\n")) {
-            return ElementPtr();
+        //cout << "at line " << line << " pos " << pos << " cur c: " << c << " next c: " << char(in.peek()) << endl;
+        if (in.peek() != ']') {
+            cur_list_element = Element::create_from_string(in, line, pos);
+            v.push_back(cur_list_element);
+            skip_to(in, line, pos, ",]", " \t\n");
         }
         c = in.get();
+        pos++;
     }
     return Element::create(v);
 }
 
 static ElementPtr
-from_stringstream_map(std::stringstream &in)
+from_stringstream_map(std::istream &in, int& line, int& pos)
 {
     char c = 0;
     std::map<std::string, ElementPtr> m;
     std::pair<std::string, ElementPtr> p;
     std::string cur_map_key;
     ElementPtr cur_map_element;
-    skip_chars(in, " \t\n");
+    skip_chars(in, " \t\n", line, pos);
     while (c != EOF && c != '}') {
-        p.first = str_from_stringstream(in);
-        if (!skip_to(in, ":", " \t\n")) {
-            return ElementPtr();
-        } else {
-            // skip the :
-            in.get();
-        }
-        p.second = Element::create_from_string(in);
-        if (!p.second) { return ElementPtr(); };
+        p.first = str_from_stringstream(in, line, pos);
+        skip_to(in, line, pos, ":", " \t\n");
+        // skip the :
+        in.get();
+        pos++;
+        p.second = Element::create_from_string(in, line, pos);
+        if (!p.second) {
+            throw ParseError(std::string("missing map value for ") + p.first, line, pos);
+        };
         m.insert(p);
-        skip_to(in, ",}", " \t\n");
+        skip_to(in, line, pos, ",}", " \t\n");
         c = in.get();
+        pos++;
     }
     return Element::create(m);
 }
 
 ElementPtr
-Element::create_from_string(std::stringstream &in)
+Element::create_from_string(std::istream &in) throw(ParseError)
+{
+    int line = 1, pos = 1;
+    return create_from_string(in, line, pos);
+}
+
+ElementPtr
+Element::create_from_string(std::istream &in, int& line, int& pos) throw(ParseError)
 {
     char c = 0;
     ElementPtr element;
     bool el_read = false;
-    skip_chars(in, " \n\t");
+    skip_chars(in, " \n\t", line, pos);
     while (c != EOF && !el_read) {
         c = in.get();
+        pos++;
+        //std::cout << c << std::endl;
         switch(c) {
             case '1':
             case '2':
@@ -285,7 +335,7 @@
             case '9':
             case '0':
                 in.putback(c);
-                element = from_stringstream_int_or_double(in);
+                element = from_stringstream_int_or_double(in, line, pos);
                 el_read = true;
                 break;
             case 't':
@@ -293,35 +343,33 @@
             case 'f':
             case 'F':
                 in.putback(c);
-                element = from_stringstream_bool(in);
+                element = from_stringstream_bool(in, line, pos);
                 el_read = true;
                 break;
             case '"':
                 in.putback('"');
-                element = from_stringstream_string(in);
+                element = from_stringstream_string(in, line, pos);
                 el_read = true;
                 break;
             case '[':
-                element = from_stringstream_list(in);
+                element = from_stringstream_list(in, line, pos);
                 el_read = true;
                 break;
             case '{':
-                element = from_stringstream_map(in);
+                element = from_stringstream_map(in, line, pos);
                 el_read = true;
                 break;
+            case EOF:
+                break;
             default:
-                // TODO this might not be a fatal error
-                // provide feedback mechanism?
-                cout << "error: unexpected '" << c << "'" << endl;
-                return ElementPtr();
+                throw ParseError(std::string("error: unexpected character ") + c, line, pos);
                 break;
         }
     }
     if (el_read) {
         return element;
     } else {
-        // throw exception?
-        return ElementPtr();
+        throw ParseError("nothing read");
     }
 }
 
@@ -574,7 +622,8 @@
 ElementPtr
 decode_int(std::stringstream& in, int& item_length)
 {
-    return from_stringstream_int_or_double(in);
+    int skip, me;
+    return from_stringstream_int_or_double(in, skip, me);
 }
 
 ElementPtr

Modified: branches/parkinglot/src/lib/cc/cpp/data.h
==============================================================================
--- branches/parkinglot/src/lib/cc/cpp/data.h (original)
+++ branches/parkinglot/src/lib/cc/cpp/data.h Thu Dec 24 10:00:34 2009
@@ -18,6 +18,17 @@
         const char* what() const throw() { return msg.c_str(); }
     private:
         std::string msg;
+    };
+
+    class ParseError : public std::exception {
+    public:
+        ParseError(std::string m = "Parse error in element data", int l = 0, int p = 0) : msg(m), line(l), pos(p) {}
+        ~ParseError() throw() {}
+        const char* what() const throw();
+    private:
+        std::string msg;
+        int line;
+        int pos;
     };
 
     class DecodeError : public std::exception {
@@ -88,11 +99,13 @@
         virtual void set(const int i, ElementPtr element) { throw TypeError(); };
         virtual void add(ElementPtr element) { throw TypeError(); };
         virtual void remove(const int i) { throw TypeError(); };
+        virtual size_t size() { throw TypeError(); };
 
         // for maps
         virtual ElementPtr get(const std::string& name) { throw TypeError(); } ;
         virtual void set(const std::string& name, ElementPtr element) { throw TypeError(); };
         virtual void remove(const std::string& name) { throw TypeError(); };
+        virtual bool contains(const std::string& s) { throw TypeError(); }
         virtual ElementPtr find(const std::string& identifier) { throw TypeError(); };
         virtual bool find(const std::string& id, ElementPtr& t) { return false; };
 
@@ -145,8 +158,11 @@
         // the memory could not be allocated
         // example:
         // ElementPtr my_element = Element::create_from_string("{\"foo\": [ 1, 2, false ] }");
-        static ElementPtr create_from_string(std::stringstream& in);
+        //static ElementPtr create_from_string(std::stringstream& in);
         static ElementPtr create_from_string(const std::string& in);
+        static ElementPtr create_from_string(std::istream& in) throw(ParseError);
+        // make this one private?
+        static ElementPtr create_from_string(std::istream& in, int& line, int &pos) throw(ParseError);
         
         //static ElementPtr create_from_xml(std::stringstream& in);
 
@@ -222,6 +238,7 @@
         std::string str();
         std::string str_xml(size_t prefix = 0);
         std::string to_wire(int omit_length = 1);
+        size_t size() { return l.size(); }
     };
 
     class MapElement : public Element {
@@ -235,6 +252,7 @@
         ElementPtr get(const std::string& s) { return m[s]; };
         void set(const std::string& s, ElementPtr p) { m[s] = p; };
         void remove(const std::string& s) { m.erase(s); }
+        bool contains(const std::string& s) { return m.find(s) != m.end(); }
         std::string str();
         std::string str_xml(size_t prefix = 0);
         std::string to_wire(int omit_length = 1);

Modified: branches/parkinglot/src/lib/cc/cpp/session.cc
==============================================================================
--- branches/parkinglot/src/lib/cc/cpp/session.cc (original)
+++ branches/parkinglot/src/lib/cc/cpp/session.cc Thu Dec 24 10:00:34 2009
@@ -226,7 +226,8 @@
 }
 
 unsigned int
-Session::group_sendmsg(ElementPtr& msg, std::string group, std::string instance, std::string to)
+Session::group_sendmsg(ElementPtr msg, std::string group,
+                       std::string instance, std::string to)
 {
     ElementPtr env = Element::create(std::map<std::string, ElementPtr>());
 
@@ -265,7 +266,7 @@
     env->set("group", Element::create(envelope->get("group")->string_value()));
     env->set("instance", Element::create(envelope->get("instance")->string_value()));
     env->set("seq", Element::create(sequence));
-    env->set("reply", Element::create(envelope->get("seq")->string_value()));
+    env->set("reply", Element::create(envelope->get("seq")->int_value()));
 
     sendmsg(env, newmsg);
 

Modified: branches/parkinglot/src/lib/cc/cpp/session.h
==============================================================================
--- branches/parkinglot/src/lib/cc/cpp/session.h (original)
+++ branches/parkinglot/src/lib/cc/cpp/session.h Thu Dec 24 10:00:34 2009
@@ -30,8 +30,8 @@
 
             Session();
 
-	    // XXX: quick hack to allow the user to watch the socket directly.
-	    int getSocket() const { return (sock); }
+            // XXX: quick hack to allow the user to watch the socket directly.
+            int getSocket() const { return (sock); }
 
             void establish();
             void disconnect();
@@ -46,7 +46,7 @@
                            std::string instance = "*");
             void unsubscribe(std::string group,
                              std::string instance = "*");
-            unsigned int group_sendmsg(ISC::Data::ElementPtr& msg,
+            unsigned int group_sendmsg(ISC::Data::ElementPtr msg,
                                        std::string group,
                                        std::string instance = "*",
                                        std::string to = "*");

Modified: branches/parkinglot/src/lib/cc/python/ISC/CC/session.py
==============================================================================
--- branches/parkinglot/src/lib/cc/python/ISC/CC/session.py (original)
+++ branches/parkinglot/src/lib/cc/python/ISC/CC/session.py Thu Dec 24 10:00:34 2009
@@ -39,6 +39,8 @@
 
             self.sendmsg({ "type": "getlname" })
             env, msg = self.recvmsg(False)
+            if not env:
+                raise ProtocolError("Could not get local name")
             self._lname = msg["lname"]
             if not self._lname:
                 raise ProtocolError("Could not get local name")
@@ -55,6 +57,8 @@
         self._closed = True
 
     def sendmsg(self, env, msg = None):
+        XXmsg = msg
+        XXenv = env
         if self._closed:
             raise SessionError("Session has been closed.")
         if type(env) == dict:




More information about the bind10-changes mailing list