Browse Source

added a feature (yes i know, but this is a serious problem) to cc/session.py;
if you pass recvmsg() or group_recvmsg() the sequence number you got back from send(), it will only give back the answer to your message (which should be sent back with group_reply()); other messages are queued and returned on subsequent calls of recvmsg()

this should fix at least the Bad config version some people have been having (which were caused by other messages getting in before the answer to certain requests made by cmdctl)

it's only in the python version, and i would like someone to take a good look at this change (perhaps the api of this part can be better, and possibly a limit on how many messages are queued). If people are ok with this solution we'll add it to the cpp version too


git-svn-id: svn://bind10.isc.org/svn/bind10/trunk@1418 e5f2f494-b856-4b98-b285-d166d9295462

Jelte Jansen 15 years ago
parent
commit
13a3a9aab4
3 changed files with 141 additions and 6 deletions
  1. 117 0
      src/bin/bindctl/bindctl.py.in
  2. 2 2
      src/bin/cmdctl/cmdctl.py.in
  3. 22 4
      src/lib/python/isc/cc/session.py

+ 117 - 0
src/bin/bindctl/bindctl.py.in

@@ -0,0 +1,117 @@
+#!@PYTHON@
+
+# Copyright (C) 2009  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+import sys; sys.path.append ('@@PYTHONPATH@@')
+
+from bindctl.moduleinfo import *
+from bindctl.bindcmd import *
+import pprint
+from optparse import OptionParser, OptionValueError
+
+__version__ = 'Bindctl'
+
+def prepare_config_commands(tool):
+    module = ModuleInfo(name = "config", desc = "Configuration commands")
+    cmd = CommandInfo(name = "show", desc = "Show configuration")
+    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")
+    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")
+    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")
+    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")
+    param = ParamInfo(name = "identifier", type = "string", optional=False)
+    cmd.add_param(param)
+    module.add_command(cmd)
+
+    cmd = CommandInfo(name = "diff", desc = "Show all local changes")
+    module.add_command(cmd)
+
+    cmd = CommandInfo(name = "revert", desc = "Revert all local changes")
+    module.add_command(cmd)
+
+    cmd = CommandInfo(name = "commit", desc = "Commit all local changes")
+    module.add_command(cmd)
+
+    cmd = CommandInfo(name = "go", desc = "Go to a specific configuration part")
+    param = ParamInfo(name = "identifier", type="string", optional=False)
+    cmd.add_param(param)
+    module.add_command(cmd)
+
+    tool.add_module_info(module)
+
+def check_port(option, opt_str, value, parser):
+    if (value < 0) or (value > 65535):
+        raise OptionValueError('%s requires a port number (0-65535)' % opt_str)
+    parser.values.port = value
+
+def check_addr(option, opt_str, value, parser):
+    ipstr = value
+    ip_family = socket.AF_INET
+    if (ipstr.find(':') != -1):
+        ip_family = socket.AF_INET6
+
+    try:
+        socket.inet_pton(ip_family, ipstr)
+    except:
+        raise OptionValueError("%s invalid ip address" % ipstr)
+
+    parser.values.addr = value
+
+def set_bindctl_options(parser):
+    parser.add_option('-p', '--port', dest = 'port', type = 'int',
+            action = 'callback', callback=check_port,
+            default = '8080', help = 'port for cmdctl of bind10')
+
+    parser.add_option('-a', '--address', dest = 'addr', type = 'string',
+            action = 'callback', callback=check_addr,
+            default = '127.0.0.1', help = 'IP address for cmdctl of bind10')
+
+
+if __name__ == '__main__':
+    try:
+        parser = OptionParser(version = __version__)
+        set_bindctl_options(parser)
+        (options, args) = parser.parse_args()
+        server_addr = options.addr + ':' + str(options.port)
+        tool = BindCmdInterpreter(server_addr)
+        prepare_config_commands(tool)
+        tool.run()
+    except Exception as e:
+        print(e, "\nFailed to connect with b10-cmdctl module, is it running?")
+
+

+ 2 - 2
src/bin/cmdctl/cmdctl.py.in

@@ -296,9 +296,9 @@ class CommandControl():
         print('b10-cmdctl send command \'%s\' to %s' %(command_name, module_name))
         try:
             msg = isc.config.ccsession.create_command(command_name, params)
-            self.cc.group_sendmsg(msg, module_name)
+            seq = self.cc.group_sendmsg(msg, module_name)
             #TODO, it may be blocked, msqg need to add a new interface waiting in timeout.
-            answer, env = self.cc.group_recvmsg(False)
+            answer, env = self.cc.group_recvmsg(False, seq)
             if answer:
                 try:
                     rcode, arg = isc.config.ccsession.parse_answer(answer)

+ 22 - 4
src/lib/python/isc/cc/session.py

@@ -32,6 +32,7 @@ class Session:
         self._recvlength = 0
         self._sequence = 1
         self._closed = False
+        self._queue = []
 
         try:
             self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -75,7 +76,18 @@ class Session:
         if msg:
             self._socket.send(msg)
 
-    def recvmsg(self, nonblock = True):
+    def recvmsg(self, nonblock = True, seq = None):
+        if len(self._queue) > 0:
+            if seq == None:
+                msg, env = self._queue.pop(0)
+            else:
+                i = 0;
+                for msg, env in self._queue:
+                    if "reply" in env and seq == env["reply"]:
+                        self._queue.remove(i)
+                        return env, msg
+                    else:
+                        i = i + 1
         if self._closed:
             raise SessionError("Session has been closed.")
         data = self._receive_full_buffer(nonblock)
@@ -83,7 +95,13 @@ class Session:
             header_length = struct.unpack('>H', data[0:2])[0]
             data_length = len(data) - 2 - header_length
             if data_length > 0:
-                return isc.cc.message.from_wire(data[2:header_length+2]), isc.cc.message.from_wire(data[header_length + 2:])
+                env = isc.cc.message.from_wire(data[2:header_length+2])
+                msg = isc.cc.message.from_wire(data[header_length + 2:])
+                if seq == None or "reply" in env and seq == env["reply"]:
+                    return env, msg
+                else:
+                    self._queue.append((env,msg))
+                    self.recvmsg(nonblock, seq)
             else:
                 return isc.cc.message.from_wire(data[2:header_length+2]), None
         return None, None
@@ -155,8 +173,8 @@ class Session:
         }, isc.cc.message.to_wire(msg))
         return seq
 
-    def group_recvmsg(self, nonblock = True):
-        env, msg  = self.recvmsg(nonblock)
+    def group_recvmsg(self, nonblock = True, seq = None):
+        env, msg  = self.recvmsg(nonblock, seq)
         if env == None:
             # return none twice to match normal return value
             # (so caller won't get a type error on no data)