Пример #1
0
 def __init__(self, socket_path, io_loop=None):
     super(IPCMessageSubscriber, self).__init__(
         socket_path, io_loop=io_loop)
     self._read_stream_future = None
     self._saved_data = []
     self._read_in_progress = Lock()
     self.callbacks = set()
Пример #2
0
class IPCMessageSubscriber(IPCClient):
    """
    Salt IPC message subscriber

    Create an IPC client to receive messages from IPC publisher

    An example of a very simple IPCMessageSubscriber connecting to an IPCMessagePublisher.
    This example assumes an already running IPCMessagePublisher.

    IMPORTANT: The below example also assumes the IOLoop is NOT running.

    # Import Tornado libs
    import salt.ext.tornado.ioloop

    # Import Salt libs
    import salt.config
    import salt.transport.ipc

    # Create a new IO Loop.
    # We know that this new IO Loop is not currently running.
    io_loop = salt.ext.tornado.ioloop.IOLoop()

    ipc_publisher_socket_path = '/var/run/ipc_publisher.ipc'

    ipc_subscriber = salt.transport.ipc.IPCMessageSubscriber(ipc_server_socket_path, io_loop=io_loop)

    # Connect to the server
    # Use the associated IO Loop that isn't running.
    io_loop.run_sync(ipc_subscriber.connect)

    # Wait for some data
    package = ipc_subscriber.read_sync()
    """

    def __init__(self, socket_path, io_loop=None):
        super(IPCMessageSubscriber, self).__init__(socket_path, io_loop=io_loop)
        self._read_stream_future = None
        self._saved_data = []
        self._read_in_progress = Lock()

    @salt.ext.tornado.gen.coroutine
    def _read(self, timeout, callback=None):
        try:
            yield self._read_in_progress.acquire(timeout=0.00000001)
        except salt.ext.tornado.gen.TimeoutError:
            raise salt.ext.tornado.gen.Return(None)

        exc_to_raise = None
        ret = None
        try:
            while True:
                if self._read_stream_future is None:
                    self._read_stream_future = self.stream.read_bytes(
                        4096, partial=True
                    )

                if timeout is None:
                    wire_bytes = yield self._read_stream_future
                else:
                    wire_bytes = yield FutureWithTimeout(
                        self.io_loop, self._read_stream_future, timeout
                    )
                self._read_stream_future = None

                # Remove the timeout once we get some data or an exception
                # occurs. We will assume that the rest of the data is already
                # there or is coming soon if an exception doesn't occur.
                timeout = None

                self.unpacker.feed(wire_bytes)
                first_sync_msg = True
                for framed_msg in self.unpacker:
                    if callback:
                        self.io_loop.spawn_callback(callback, framed_msg["body"])
                    elif first_sync_msg:
                        ret = framed_msg["body"]
                        first_sync_msg = False
                    else:
                        self._saved_data.append(framed_msg["body"])
                if not first_sync_msg:
                    # We read at least one piece of data and we're on sync run
                    break
        except TornadoTimeoutError:
            # In the timeout case, just return None.
            # Keep 'self._read_stream_future' alive.
            ret = None
        except StreamClosedError as exc:
            log.trace("Subscriber disconnected from IPC %s", self.socket_path)
            self._read_stream_future = None
            exc_to_raise = exc
        except Exception as exc:  # pylint: disable=broad-except
            log.error("Exception occurred in Subscriber while handling stream: %s", exc)
            self._read_stream_future = None
            exc_to_raise = exc

        self._read_in_progress.release()

        if exc_to_raise is not None:
            raise exc_to_raise  # pylint: disable=E0702
        raise salt.ext.tornado.gen.Return(ret)

    def read_sync(self, timeout=None):
        """
        Read a message from an IPC socket

        The socket must already be connected.
        The associated IO Loop must NOT be running.
        :param int timeout: Timeout when receiving message
        :return: message data if successful. None if timed out. Will raise an
                 exception for all other error conditions.
        """
        if self._saved_data:
            return self._saved_data.pop(0)
        return self.io_loop.run_sync(lambda: self._read(timeout))

    @salt.ext.tornado.gen.coroutine
    def read_async(self, callback):
        """
        Asynchronously read messages and invoke a callback when they are ready.

        :param callback: A callback with the received data
        """
        while not self.connected():
            try:
                yield self.connect(timeout=5)
            except StreamClosedError:
                log.trace(
                    "Subscriber closed stream on IPC %s before connect",
                    self.socket_path,
                )
                yield salt.ext.tornado.gen.sleep(1)
            except Exception as exc:  # pylint: disable=broad-except
                log.error("Exception occurred while Subscriber connecting: %s", exc)
                yield salt.ext.tornado.gen.sleep(1)
        yield self._read(None, callback)

    def close(self):
        """
        Routines to handle any cleanup before the instance shuts down.
        Sockets and filehandles should be closed explicitly, to prevent
        leaks.
        """
        if self._closing:
            return
        super(IPCMessageSubscriber, self).close()
        # This will prevent this message from showing up:
        # '[ERROR   ] Future exception was never retrieved:
        # StreamClosedError'
        if self._read_stream_future is not None and self._read_stream_future.done():
            exc = self._read_stream_future.exception()
            if exc and not isinstance(exc, StreamClosedError):
                log.error("Read future returned exception %r", exc)

    # pylint: disable=W1701
    def __del__(self):
        if IPCMessageSubscriber in globals():
            self.close()
Пример #3
0
 def __init__(self, socket_path, io_loop=None):
     super().__init__(socket_path, io_loop=io_loop)
     self._read_stream_future = None
     self._saved_data = []
     self._read_in_progress = Lock()