[ovs-dev] [python vlog 2/6] python: Break unixctl implementation into registry, client, and server.

Ethan Jackson ethan at nicira.com
Fri May 11 22:14:29 UTC 2012


Looks good, thanks.

Ethan

On Tue, May 1, 2012 at 2:28 PM, Ben Pfaff <blp at nicira.com> wrote:
> I wish to add some unixctl commands to the Python vlog module.  However,
> importing ovs.unixctl in ovs.vlog creates a circular dependency, because
> ovs.unixctl imports ovs.vlog already.  The solution, in this commit, is to
> break the unixctl module into three parts: a register (ovs.unixctl) that
> does not depend on ovs.vlog, and client (ovs.unixctl.client) and server
> (ovs.unixctl.server) modules that do.  This breaks the circular dependency.
>
> Signed-off-by: Ben Pfaff <blp at nicira.com>
> ---
>  debian/ovs-monitor-ipsec                           |    3 +-
>  python/automake.mk                                 |    4 +-
>  python/ovs/unixctl/__init__.py                     |   83 +++++++++++++++++++
>  python/ovs/unixctl/client.py                       |   70 ++++++++++++++++
>  python/ovs/{unixctl.py => unixctl/server.py}       |   85 +++-----------------
>  tests/appctl.py                                    |    3 +-
>  tests/test-unixctl.py                              |    3 +-
>  .../usr_share_openvswitch_scripts_ovs-xapi-sync    |    3 +-
>  8 files changed, 174 insertions(+), 80 deletions(-)
>  create mode 100644 python/ovs/unixctl/__init__.py
>  create mode 100644 python/ovs/unixctl/client.py
>  rename python/ovs/{unixctl.py => unixctl/server.py} (75%)
>
> diff --git a/debian/ovs-monitor-ipsec b/debian/ovs-monitor-ipsec
> index 5024277..a50a669 100755
> --- a/debian/ovs-monitor-ipsec
> +++ b/debian/ovs-monitor-ipsec
> @@ -38,6 +38,7 @@ import ovs.util
>  import ovs.daemon
>  import ovs.db.idl
>  import ovs.unixctl
> +import ovs.unixctl.server
>  import ovs.vlog
>
>  vlog = ovs.vlog.Vlog("ovs-monitor-ipsec")
> @@ -414,7 +415,7 @@ def main():
>     ovs.daemon.daemonize()
>
>     ovs.unixctl.command_register("exit", "", 0, 0, unixctl_exit, None)
> -    error, unixctl_server = ovs.unixctl.UnixctlServer.create(None)
> +    error, unixctl_server = ovs.unixctl.server.UnixctlServer.create(None)
>     if error:
>         ovs.util.ovs_fatal(error, "could not create unixctl server", vlog)
>
> diff --git a/python/automake.mk b/python/automake.mk
> index 59db000..01056a4 100644
> --- a/python/automake.mk
> +++ b/python/automake.mk
> @@ -29,7 +29,9 @@ ovs_pyfiles = \
>        python/ovs/socket_util.py \
>        python/ovs/stream.py \
>        python/ovs/timeval.py \
> -       python/ovs/unixctl.py \
> +       python/ovs/unixctl/__init__.py \
> +       python/ovs/unixctl/client.py \
> +       python/ovs/unixctl/server.py \
>        python/ovs/util.py \
>        python/ovs/version.py \
>        python/ovs/vlog.py
> diff --git a/python/ovs/unixctl/__init__.py b/python/ovs/unixctl/__init__.py
> new file mode 100644
> index 0000000..85643df
> --- /dev/null
> +++ b/python/ovs/unixctl/__init__.py
> @@ -0,0 +1,83 @@
> +# Copyright (c) 2011, 2012 Nicira Networks
> +#
> +# Licensed under the Apache License, Version 2.0 (the "License");
> +# you may not use this file except in compliance with the License.
> +# You may obtain a copy of the License at:
> +#
> +#     http://www.apache.org/licenses/LICENSE-2.0
> +#
> +# Unless required by applicable law or agreed to in writing, software
> +# distributed under the License is distributed on an "AS IS" BASIS,
> +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +# See the License for the specific language governing permissions and
> +# limitations under the License.
> +
> +import types
> +
> +import ovs.util
> +
> +commands = {}
> +strtypes = types.StringTypes
> +
> +
> +class _UnixctlCommand(object):
> +    def __init__(self, usage, min_args, max_args, callback, aux):
> +        self.usage = usage
> +        self.min_args = min_args
> +        self.max_args = max_args
> +        self.callback = callback
> +        self.aux = aux
> +
> +
> +def _unixctl_help(conn, unused_argv, unused_aux):
> +    reply = "The available commands are:\n"
> +    command_names = sorted(commands.keys())
> +    for name in command_names:
> +        reply += "  "
> +        usage = commands[name].usage
> +        if usage:
> +            reply += "%-23s %s" % (name, usage)
> +        else:
> +            reply += name
> +        reply += "\n"
> +    conn.reply(reply)
> +
> +
> +def command_register(name, usage, min_args, max_args, callback, aux):
> +    """ Registers a command with the given 'name' to be exposed by the
> +    UnixctlServer. 'usage' describes the arguments to the command; it is used
> +    only for presentation to the user in "help" output.
> +
> +    'callback' is called when the command is received.  It is passed a
> +    UnixctlConnection object, the list of arguments as unicode strings, and
> +    'aux'.  Normally 'callback' should reply by calling
> +    UnixctlConnection.reply() or UnixctlConnection.reply_error() before it
> +    returns, but if the command cannot be handled immediately, then it can
> +    defer the reply until later.  A given connection can only process a single
> +    request at a time, so a reply must be made eventually to avoid blocking
> +    that connection."""
> +
> +    assert isinstance(name, strtypes)
> +    assert isinstance(usage, strtypes)
> +    assert isinstance(min_args, int)
> +    assert isinstance(max_args, int)
> +    assert isinstance(callback, types.FunctionType)
> +
> +    if name not in commands:
> +        commands[name] = _UnixctlCommand(usage, min_args, max_args, callback,
> +                                         aux)
> +
> +def socket_name_from_target(target):
> +    assert isinstance(target, strtypes)
> +
> +    if target.startswith("/"):
> +        return 0, target
> +
> +    pidfile_name = "%s/%s.pid" % (ovs.dirs.RUNDIR, target)
> +    pid = ovs.daemon.read_pidfile(pidfile_name)
> +    if pid < 0:
> +        return -pid, "cannot read pidfile \"%s\"" % pidfile_name
> +
> +    return 0, "%s/%s.%d.ctl" % (ovs.dirs.RUNDIR, target, pid)
> +
> +command_register("help", "", 0, 0, _unixctl_help, None)
> diff --git a/python/ovs/unixctl/client.py b/python/ovs/unixctl/client.py
> new file mode 100644
> index 0000000..7005821
> --- /dev/null
> +++ b/python/ovs/unixctl/client.py
> @@ -0,0 +1,70 @@
> +# Copyright (c) 2011, 2012 Nicira Networks
> +#
> +# Licensed under the Apache License, Version 2.0 (the "License");
> +# you may not use this file except in compliance with the License.
> +# You may obtain a copy of the License at:
> +#
> +#     http://www.apache.org/licenses/LICENSE-2.0
> +#
> +# Unless required by applicable law or agreed to in writing, software
> +# distributed under the License is distributed on an "AS IS" BASIS,
> +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +# See the License for the specific language governing permissions and
> +# limitations under the License.
> +
> +import copy
> +import errno
> +import os
> +import types
> +
> +import ovs.jsonrpc
> +import ovs.stream
> +import ovs.util
> +
> +
> +vlog = ovs.vlog.Vlog("unixctl_client")
> +strtypes = types.StringTypes
> +
> +
> +class UnixctlClient(object):
> +    def __init__(self, conn):
> +        assert isinstance(conn, ovs.jsonrpc.Connection)
> +        self._conn = conn
> +
> +    def transact(self, command, argv):
> +        assert isinstance(command, strtypes)
> +        assert isinstance(argv, list)
> +        for arg in argv:
> +            assert isinstance(arg, strtypes)
> +
> +        request = ovs.jsonrpc.Message.create_request(command, argv)
> +        error, reply = self._conn.transact_block(request)
> +
> +        if error:
> +            vlog.warn("error communicating with %s: %s"
> +                      % (self._conn.name, os.strerror(error)))
> +            return error, None, None
> +
> +        if reply.error is not None:
> +            return 0, str(reply.error), None
> +        else:
> +            assert reply.result is not None
> +            return 0, None, str(reply.result)
> +
> +    def close(self):
> +        self._conn.close()
> +        self.conn = None
> +
> +    @staticmethod
> +    def create(path):
> +        assert isinstance(path, str)
> +
> +        unix = "unix:%s" % ovs.util.abs_file_name(ovs.dirs.RUNDIR, path)
> +        error, stream = ovs.stream.Stream.open_block(
> +            ovs.stream.Stream.open(unix))
> +
> +        if error:
> +            vlog.warn("failed to connect to %s" % path)
> +            return error, None
> +
> +        return 0, UnixctlClient(ovs.jsonrpc.Connection(stream))
> diff --git a/python/ovs/unixctl.py b/python/ovs/unixctl/server.py
> similarity index 75%
> rename from python/ovs/unixctl.py
> rename to python/ovs/unixctl/server.py
> index 396d1be..c40b121 100644
> --- a/python/ovs/unixctl.py
> +++ b/python/ovs/unixctl/server.py
> @@ -17,89 +17,19 @@ import errno
>  import os
>  import types
>
> -import ovs.daemon
>  import ovs.dirs
>  import ovs.jsonrpc
>  import ovs.stream
> +import ovs.unixctl
>  import ovs.util
>  import ovs.version
>  import ovs.vlog
>
>  Message = ovs.jsonrpc.Message
> -vlog = ovs.vlog.Vlog("unixctl")
> -commands = {}
> +vlog = ovs.vlog.Vlog("unixctl_server")
>  strtypes = types.StringTypes
>
>
> -class _UnixctlCommand(object):
> -    def __init__(self, usage, min_args, max_args, callback, aux):
> -        self.usage = usage
> -        self.min_args = min_args
> -        self.max_args = max_args
> -        self.callback = callback
> -        self.aux = aux
> -
> -
> -def _unixctl_help(conn, unused_argv, unused_aux):
> -    assert isinstance(conn, UnixctlConnection)
> -    reply = "The available commands are:\n"
> -    command_names = sorted(commands.keys())
> -    for name in command_names:
> -        reply += "  "
> -        usage = commands[name].usage
> -        if usage:
> -            reply += "%-23s %s" % (name, usage)
> -        else:
> -            reply += name
> -        reply += "\n"
> -    conn.reply(reply)
> -
> -
> -def _unixctl_version(conn, unused_argv, version):
> -    assert isinstance(conn, UnixctlConnection)
> -    version = "%s (Open vSwitch) %s" % (ovs.util.PROGRAM_NAME, version)
> -    conn.reply(version)
> -
> -
> -def command_register(name, usage, min_args, max_args, callback, aux):
> -    """ Registers a command with the given 'name' to be exposed by the
> -    UnixctlServer. 'usage' describes the arguments to the command; it is used
> -    only for presentation to the user in "help" output.
> -
> -    'callback' is called when the command is received.  It is passed a
> -    UnixctlConnection object, the list of arguments as unicode strings, and
> -    'aux'.  Normally 'callback' should reply by calling
> -    UnixctlConnection.reply() or UnixctlConnection.reply_error() before it
> -    returns, but if the command cannot be handled immediately, then it can
> -    defer the reply until later.  A given connection can only process a single
> -    request at a time, so a reply must be made eventually to avoid blocking
> -    that connection."""
> -
> -    assert isinstance(name, strtypes)
> -    assert isinstance(usage, strtypes)
> -    assert isinstance(min_args, int)
> -    assert isinstance(max_args, int)
> -    assert isinstance(callback, types.FunctionType)
> -
> -    if name not in commands:
> -        commands[name] = _UnixctlCommand(usage, min_args, max_args, callback,
> -                                         aux)
> -
> -
> -def socket_name_from_target(target):
> -    assert isinstance(target, strtypes)
> -
> -    if target.startswith("/"):
> -        return 0, target
> -
> -    pidfile_name = "%s/%s.pid" % (ovs.dirs.RUNDIR, target)
> -    pid = ovs.daemon.read_pidfile(pidfile_name)
> -    if pid < 0:
> -        return -pid, "cannot read pidfile \"%s\"" % pidfile_name
> -
> -    return 0, "%s/%s.%d.ctl" % (ovs.dirs.RUNDIR, target, pid)
> -
> -
>  class UnixctlConnection(object):
>     def __init__(self, rpc):
>         assert isinstance(rpc, ovs.jsonrpc.Connection)
> @@ -177,7 +107,7 @@ class UnixctlConnection(object):
>         error = None
>         params = request.params
>         method = request.method
> -        command = commands.get(method)
> +        command = ovs.unixctl.commands.get(method)
>         if command is None:
>             error = '"%s" is not a valid command' % method
>         elif len(params) < command.min_args:
> @@ -200,6 +130,11 @@ class UnixctlConnection(object):
>             self.reply_error(error)
>
>
> +def _unixctl_version(conn, unused_argv, version):
> +    assert isinstance(conn, UnixctlConnection)
> +    version = "%s (Open vSwitch) %s" % (ovs.util.PROGRAM_NAME, version)
> +    conn.reply(version)
> +
>  class UnixctlServer(object):
>     def __init__(self, listener):
>         assert isinstance(listener, ovs.stream.PassiveStream)
> @@ -262,8 +197,8 @@ class UnixctlServer(object):
>                                % path)
>             return error, None
>
> -        command_register("help", "", 0, 0, _unixctl_help, None)
> -        command_register("version", "", 0, 0, _unixctl_version, version)
> +        ovs.unixctl.command_register("version", "", 0, 0, _unixctl_version,
> +                                     version)
>
>         return 0, UnixctlServer(listener)
>
> diff --git a/tests/appctl.py b/tests/appctl.py
> index 4a2865e..ed61c7f 100644
> --- a/tests/appctl.py
> +++ b/tests/appctl.py
> @@ -18,6 +18,7 @@ import sys
>
>  import ovs.daemon
>  import ovs.unixctl
> +import ovs.unixctl.client
>  import ovs.util
>  import ovs.vlog
>
> @@ -29,7 +30,7 @@ def connect_to_target(target):
>     else:
>         socket_name = str_result
>
> -    error, client = ovs.unixctl.UnixctlClient.create(socket_name)
> +    error, client = ovs.unixctl.client.UnixctlClient.create(socket_name)
>     if error:
>         ovs.util.ovs_fatal(error, "cannot connect to \"%s\"" % socket_name)
>
> diff --git a/tests/test-unixctl.py b/tests/test-unixctl.py
> index c262a95..392999e 100644
> --- a/tests/test-unixctl.py
> +++ b/tests/test-unixctl.py
> @@ -17,6 +17,7 @@ import sys
>
>  import ovs.daemon
>  import ovs.unixctl
> +import ovs.unixctl.server
>
>  vlog = ovs.vlog.Vlog("test-unixctl")
>  exiting = False
> @@ -55,7 +56,7 @@ def main():
>     ovs.vlog.handle_args(args)
>
>     ovs.daemon.daemonize_start()
> -    error, server = ovs.unixctl.UnixctlServer.create(args.unixctl)
> +    error, server = ovs.unixctl.server.UnixctlServer.create(args.unixctl)
>     if error:
>         ovs.util.ovs_fatal(error, "could not create unixctl server at %s"
>                            % args.unixctl, vlog)
> diff --git a/xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync b/xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync
> index 77c07bc..63cd614 100755
> --- a/xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync
> +++ b/xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync
> @@ -35,6 +35,7 @@ from ovs.db import types
>  import ovs.daemon
>  import ovs.db.idl
>  import ovs.unixctl
> +import ovs.unixctl.server
>
>  vlog = ovs.vlog.Vlog("ovs-xapi-sync")
>  session = None
> @@ -236,7 +237,7 @@ def main():
>     ovs.unixctl.command_register("exit", "", 0, 0, unixctl_exit, None)
>     ovs.unixctl.command_register("flush-cache", "", 0, 0, unixctl_flush_cache,
>                                  None)
> -    error, unixctl_server = ovs.unixctl.UnixctlServer.create(None)
> +    error, unixctl_server = ovs.unixctl.server.UnixctlServer.create(None)
>     if error:
>         ovs.util.ovs_fatal(error, "could not create unixctl server", vlog)
>
> --
> 1.7.2.5
>
> _______________________________________________
> dev mailing list
> dev at openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev



More information about the dev mailing list