On 12/17/2015 04:37 AM, Ofer Ben Yacov wrote:
> From: Ofer Ben-Yacov <ofer.benya...@gmail.com>
> 
> Currently the IDL does not support passive TCP connection,
> i.e. when the OVSDB connects to its manager.
> 
> This patch enables IDL to use an already-open session
> (the one which was previously used for retrieving the db schema).
> In addition, it enables IDL to go back to "listen mode" in case the connection
> is lost.

Thanks for the patch.  I was able to apply this version.  As discussed
off-list, I had problems applying previous submissions of this patch.
I'll start looking this over.

> 
> LIMITATIONS:
> ----------------------
> 
> This patch enables a **SINGLE** TCP connection from an OVSDB server to an
> agent that uses IDL with {IP,PORT} pair. Therefore, the agent will support
> only **ONE** OVSDB server using {IP,PORT} pair.
> 
> Future development may add multi-session server capability that will allow
> an agent to use single {IP,PORT} pair to connect to multiple OVSDB servers.

I'm curious, what's your use case for this? If your use case is only
connecting to a single ovsdb server, what's the advantage of this mode?

> TESTING:
> ---------------
> 
> To be able to test passive TCP to the OVSDB, a helper program was used.
> The program flow is:
> 1. Open TCP connection to OVSDB
> 2. Open TCP connection to the agent application that uses IDL
> 3. Forward data between connections (Proxy)
> 4. Simulate a connection problem by stopping and restarting the program

Would you be willing to add a test case for this?  The ovs Python
library actually has pretty good test coverage when running "make check"
for ovs.

Take a look at the files in the tests/ directory.  In particular, see
test-ovsdb.py and the *.at files that call it such as ovsdb-idl.at.  How
about adding a test case that exercises this?  That would also help make
sure I don't break it in the Python 3 port I'm doing.

> Requested-by: Ben Pfaff <blp at nicira.com>, "D M, Vikas" <vikas....@hpe.com>,
>       "Kamat, Maruti Haridas" <maruti.ka...@hpe.com>,
>       "Sukhdev Kapur" <sukh...@arista.com>,
>       "Migliaccio, Armando" <armando.migliac...@hpe.com>

This should be one email address per line.

Some more fairly minor comments inline.

> 
> ---
>  python/ovs/db/idl.py  | 18 +++++++++++++++---
>  python/ovs/jsonrpc.py | 19 +++++++++++--------
>  python/ovs/stream.py  | 47 +++++++++++++++++++++++++++++++----------------
>  3 files changed, 57 insertions(+), 27 deletions(-)
> 
> diff --git a/python/ovs/db/idl.py b/python/ovs/db/idl.py
> index c8990c7..4b492fe 100644
> --- a/python/ovs/db/idl.py
> +++ b/python/ovs/db/idl.py
> @@ -83,7 +83,7 @@ class Idl(object):
>        currently being constructed, if there is one, or None otherwise.
>  """
>  
> -    def __init__(self, remote, schema):
> +    def __init__(self, remote, schema, session = None):

Please remove the spaces around the '='.  (PEP8)

Try running flake8 against the code before and after your patch to make
sure you don't introduce any new issues.  I have a separate patch series
cleaning up most of it.

>          """Creates and returns a connection to the database named 'db_name' 
> on
>          'remote', which should be in a form acceptable to
>          ovs.jsonrpc.session.open().  The connection will maintain an 
> in-memory
> @@ -101,7 +101,16 @@ class Idl(object):
>          As a convenience to users, 'schema' may also be an instance of the
>          SchemaHelper class.
>  
> -        The IDL uses and modifies 'schema' directly."""
> +        The IDL uses and modifies 'schema' directly.
> +
> +        In passive mode ( where the OVSDB connects to its manager ),
> +        we first need to wait for the OVSDB to connect and then
> +        pass the 'session' object (while the it is still open ) and
> +        the schema we retrieved from the open session to the IDL to use it.
> +
> +        If in active mode, do not pass 'session' and it will be created
> +        by IDL by using 'remote'.
> +        """
>  
>          assert isinstance(schema, SchemaHelper)
>          schema = schema.get_idl_schema()
> @@ -109,7 +118,10 @@ class Idl(object):
>          self.tables = schema.tables
>          self.readonly = schema.readonly
>          self._db = schema
> -        self._session = ovs.jsonrpc.Session.open(remote)
> +        if session:
> +            self._session = session
> +        else:
> +            self._session = ovs.jsonrpc.Session.open(remote)
>          self._monitor_request_id = None
>          self._last_seqno = None
>          self.change_seqno = 0
> diff --git a/python/ovs/jsonrpc.py b/python/ovs/jsonrpc.py
> index d54d74b..1b68d3f 100644
> --- a/python/ovs/jsonrpc.py
> +++ b/python/ovs/jsonrpc.py
> @@ -418,23 +418,25 @@ class Session(object):
>          self.__disconnect()
>  
>          name = self.reconnect.get_name()
> -        if not self.reconnect.is_passive():
> -            error, self.stream = ovs.stream.Stream.open(name)
> +        if self.reconnect.is_passive():
> +            if self.pstream is not None:
> +                self.pstream.close()
> +            error, self.pstream = ovs.stream.PassiveStream.open(name)
>              if not error:
> -                self.reconnect.connecting(ovs.timeval.msec())
> +                self.reconnect.listening(ovs.timeval.msec())
>              else:
>                  self.reconnect.connect_failed(ovs.timeval.msec(), error)
> -        elif self.pstream is not None:
> -            error, self.pstream = ovs.stream.PassiveStream.open(name)
> +        else:
> +            error, self.stream = ovs.stream.Stream.open(name)
>              if not error:
> -                self.reconnect.listening(ovs.timeval.msec())
> +                self.reconnect.connecting(ovs.timeval.msec())
>              else:
>                  self.reconnect.connect_failed(ovs.timeval.msec(), error)
>  
>          self.seqno += 1
>  
>      def run(self):
> -        if self.pstream is not None:
> +        if self.pstream is not None and self.stream is None:
>              error, stream = self.pstream.accept()
>              if error == 0:
>                  if self.rpc or self.stream:
> @@ -444,11 +446,11 @@ class Session(object):
>                      self.__disconnect()
>                  self.reconnect.connected(ovs.timeval.msec())
>                  self.rpc = Connection(stream)
> +                self.stream = stream
>              elif error != errno.EAGAIN:
>                  self.reconnect.listen_error(ovs.timeval.msec(), error)
>                  self.pstream.close()
>                  self.pstream = None
> -
>          if self.rpc:
>              backlog = self.rpc.get_backlog()
>              self.rpc.run()
> @@ -559,3 +561,4 @@ class Session(object):
>  
>      def force_reconnect(self):
>          self.reconnect.force_reconnect(ovs.timeval.msec())
> +

This looks like some unintended added whitespace.  I get a warning about
this patch introducing a whitespace error when I apply it.

> diff --git a/python/ovs/stream.py b/python/ovs/stream.py
> index fb083ee..a8be6e0 100644
> --- a/python/ovs/stream.py
> +++ b/python/ovs/stream.py
> @@ -15,7 +15,6 @@
>  import errno
>  import os
>  import socket
> -

Please drop this line removal.

>  import ovs.poller
>  import ovs.socket_util
>  import ovs.vlog
> @@ -261,11 +260,11 @@ class PassiveStream(object):
>      @staticmethod
>      def is_valid_name(name):
>          """Returns True if 'name' is a passive stream name in the form
> -        "TYPE:ARGS" and TYPE is a supported passive stream type (currently 
> only
> -        "punix:"), otherwise False."""
> -        return name.startswith("punix:")
> +        "TYPE:ARGS" and TYPE is a supported passive stream type,
> +        otherwise False."""
> +        return name.startswith("punix:") or name.startswith("ptcp:")
>  
> -    def __init__(self, sock, name, bind_path):
> +    def __init__(self, sock, name, bind_path=None):
>          self.name = name
>          self.socket = sock
>          self.bind_path = bind_path
> @@ -274,22 +273,31 @@ class PassiveStream(object):
>      def open(name):
>          """Attempts to start listening for remote stream connections.  'name'
>          is a connection name in the form "TYPE:ARGS", where TYPE is an 
> passive
> -        stream class's name and ARGS are stream class-specific.  Currently 
> the
> -        only supported TYPE is "punix".
> +        stream class's name and ARGS are stream class-specific.
>  
>          Returns (error, pstream): on success 'error' is 0 and 'pstream' is 
> the
>          new PassiveStream, on failure 'error' is a positive errno value and
>          'pstream' is None."""
>          if not PassiveStream.is_valid_name(name):
>              return errno.EAFNOSUPPORT, None
> -
> -        bind_path = name[6:]
> +        bind_path = None
>          if name.startswith("punix:"):
> +            bind_path = name[6:]
>              bind_path = ovs.util.abs_file_name(ovs.dirs.RUNDIR, bind_path)
> -        error, sock = ovs.socket_util.make_unix_socket(socket.SOCK_STREAM,
> -                                                       True, bind_path, None)
> -        if error:
> -            return error, None
> +            error, sock = 
> ovs.socket_util.make_unix_socket(socket.SOCK_STREAM,
> +                                                           True, bind_path,
> +                                                           None)
> +            if error:
> +                return error, None
> +
> +        elif name.startswith("ptcp:"):
> +            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
> +            sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
> +            remote = name.split(':')
> +            sock.bind((remote[1], int(remote[2])))

How about IPv6?

I'm not sure if the existing code supports it though.  If so, it'd be
nice to include it here.

> +
> +        else:
> +            raise Exception('Unknown connection string')
>  
>          try:
>              sock.listen(10)
> @@ -320,7 +328,10 @@ class PassiveStream(object):
>              try:
>                  sock, addr = self.socket.accept()
>                  ovs.socket_util.set_nonblocking(sock)
> -                return 0, Stream(sock, "unix:%s" % addr, 0)
> +                if (sock.family == socket.AF_UNIX):
> +                    return 0, Stream(sock, "unix:%s" % addr, 0)
> +                return 0, Stream(sock, 'ptcp:' + addr[0] + ':' + 
> str(addr[1]),
> +                                 0)
>              except socket.error, e:
>                  error = ovs.socket_util.get_exception_errno(e)
>                  if error != errno.EAGAIN:
> @@ -350,8 +361,10 @@ class UnixStream(Stream):
>      @staticmethod
>      def _open(suffix, dscp):
>          connect_path = suffix
> -        return  ovs.socket_util.make_unix_socket(socket.SOCK_STREAM,
> -                                                 True, None, connect_path)
> +        return ovs.socket_util.make_unix_socket(socket.SOCK_STREAM,
> +                                                True, None, connect_path)
> +
> +
>  Stream.register_method("unix", UnixStream)
>  
>  
> @@ -363,4 +376,6 @@ class TCPStream(Stream):
>          if not error:
>              sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
>          return error, sock
> +
> +
>  Stream.register_method("tcp", TCPStream)
> 


-- 
Russell Bryant
_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to