示例#1
0
def test_env_util_smoke(protocol):
    ca.get_environment_variables()
    try:
        ca.get_netifaces_addresses()
    except RuntimeError:
        # Netifaces may be unavailable
        ...

    ca.get_address_list(protocol=protocol)
    ca.get_beacon_address_list(protocol=protocol)
    ca._utils.get_manually_specified_beacon_addresses(protocol=protocol)
    ca._utils.get_manually_specified_client_addresses(protocol=protocol)
    ca.get_server_address_list(protocol=protocol)
示例#2
0
    async def run(self, *, log_pv_names=False):
        'Start the server'
        self.log.info('Curio server starting up...')
        try:
            for address in ca.get_beacon_address_list():
                sock = ca.bcast_socket(socket)
                await sock.connect(address)
                interface, _ = sock.getsockname()
                self.beacon_socks[address] = (interface, sock)

            async def make_socket(interface, port):
                return curio.network.tcp_server_socket(interface, port)

            self.port, self.tcp_sockets = await self._bind_tcp_sockets_with_consistent_port_number(
                make_socket)

            async with curio.TaskGroup() as self._task_group:
                g = self._task_group
                for interface, sock in self.tcp_sockets.items():
                    # Use run_server instead of tcp_server so we can hand in a
                    # socket that is already bound, avoiding a race between the
                    # moment we check for port availability and the moment the
                    # TCP server binds.
                    self.log.info("Listening on %s:%d", interface, self.port)
                    await g.spawn(curio.network.run_server,
                                  sock, self.tcp_handler)

                await g.spawn(self._await_stop)
                await g.spawn(self.broadcaster_udp_server_loop)
                await g.spawn(self.broadcaster_queue_loop)
                await g.spawn(self.subscription_queue_loop)
                await g.spawn(self.broadcast_beacon_loop)

                async_lib = CurioAsyncLayer()
                for name, method in self.startup_methods.items():
                    self.log.debug('Calling startup method %r', name)
                    await g.spawn(method, async_lib)
                self.log.info('Server startup complete.')
                if log_pv_names:
                    self.log.info('PVs available:\n%s', '\n'.join(self.pvdb))
            self._log_task_group_exceptions(self._task_group)
        except curio.TaskCancelled as ex:
            self.log.info('Server task cancelled. Must shut down.')
            raise ServerExit() from ex
        finally:
            self.log.info('Server exiting....')
            async_lib = CurioAsyncLayer()
            async with curio.TaskGroup() as task_group:
                for name, method in self.shutdown_methods.items():
                    self.log.debug('Calling shutdown method %r', name)
                    await task_group.spawn(method, async_lib)
            self._log_task_group_exceptions(task_group)
            for sock in self.tcp_sockets.values():
                await sock.close()
            for sock in self.udp_socks.values():
                await sock.close()
            for _interface, sock in self.beacon_socks.values():
                await sock.close()
            self._task_group = None
示例#3
0
文件: server.py 项目: ZLLentz/caproto
    async def broadcast_beacon_loop(self):
        beacon_period = self.environ['EPICS_CAS_BEACON_PERIOD']
        addresses = get_beacon_address_list()

        while True:
            beacon = ca.RsrvIsUpResponse(13, self.port, self.beacon_count,
                                         self.host)
            bytes_to_send = self.broadcaster.send(beacon)
            for addr_port in addresses:
                await self.udp_sock.sendto(bytes_to_send, addr_port)
            self.beacon_count += 1
            await curio.sleep(beacon_period)
示例#4
0
def test_beacon_addresses(monkeypatch, protocol, default_port, env_auto,
                          env_addr, expected):
    env = ca.get_environment_variables()

    key = ca.Protocol(protocol).server_env_key
    env[f'EPICS_{key}_BEACON_ADDR_LIST'] = env_addr
    env[f'EPICS_{key}_AUTO_BEACON_ADDR_LIST'] = env_auto
    if protocol == ca.Protocol.ChannelAccess:
        env['EPICS_CAS_BEACON_PORT'] = int(default_port)
    else:
        env['EPICS_PVAS_BROADCAST_PORT'] = int(default_port)

    patch_env(monkeypatch, env)
    assert set(ca.get_beacon_address_list(protocol=protocol)) == set(expected)
示例#5
0
    async def run(self, *, log_pv_names=False):
        'Start the server'
        self.log.info('Asyncio server starting up...')

        async def make_socket(interface, port):
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
            s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            s.setblocking(False)
            s.bind((interface, port))
            return s

        self.port, self.tcp_sockets = await self._bind_tcp_sockets_with_consistent_port_number(
            make_socket)
        tasks = []
        for interface, sock in self.tcp_sockets.items():
            self.log.info("Listening on %s:%d", interface, self.port)
            tasks.append(self.loop.create_task(self.server_accept_loop(sock)))

        class BcastLoop(asyncio.Protocol):
            parent = self
            loop = self.loop

            def __init__(self, *args, **kwargs):
                self.transport = None
                self._tasks = ()

            def connection_made(self, transport):
                self.transport = transport

            def datagram_received(self, data, addr):
                if data:
                    tsk = self.loop.create_task(
                        self.parent._broadcaster_recv_datagram(data, addr))

                    self._tasks = tuple(t for t in self._tasks + (tsk, )
                                        if not t.done())

            def error_received(self, exc):
                self.parent.log.error('BcastLoop received error', exc_info=exc)

        class TransportWrapper:
            """Make an asyncio transport something you can call sendto on."""
            def __init__(self, transport):
                self.transport = transport

            async def sendto(self, bytes_to_send, addr_port):
                try:
                    self.transport.sendto(bytes_to_send, addr_port)
                except OSError as exc:
                    host, port = addr_port
                    raise ca.CaprotoNetworkError(
                        f"Failed to send to {host}:{port}") from exc

            def close(self):
                return self.transport.close()

        class ConnectedTransportWrapper:
            """Make an asyncio transport something you can call send on."""
            def __init__(self, transport, address):
                self.transport = transport
                self.address = address

            async def send(self, bytes_to_send):
                try:
                    self.transport.sendto(bytes_to_send, self.address)
                except OSError as exc:
                    host, port = self.address
                    raise ca.CaprotoNetworkError(
                        f"Failed to send to {host}:{port}") from exc

            def close(self):
                return self.transport.close()

        reuse_port = sys.platform not in ('win32', ) and hasattr(
            socket, 'SO_REUSEPORT')
        for address in ca.get_beacon_address_list():
            transport, _ = await self.loop.create_datagram_endpoint(
                BcastLoop,
                remote_addr=address,
                allow_broadcast=True,
                reuse_address=True,
                reuse_port=reuse_port)
            wrapped_transport = ConnectedTransportWrapper(transport, address)
            self.beacon_socks[address] = (interface, wrapped_transport)

        for interface in self.interfaces:
            transport, self.p = await self.loop.create_datagram_endpoint(
                BcastLoop,
                local_addr=(interface, self.ca_server_port),
                allow_broadcast=True,
                reuse_address=True,
                reuse_port=reuse_port)
            self.udp_socks[interface] = TransportWrapper(transport)
            self.log.debug('UDP socket bound on %s:%d', interface,
                           self.ca_server_port)

        tasks.append(self.loop.create_task(self.broadcaster_queue_loop()))
        tasks.append(self.loop.create_task(self.subscription_queue_loop()))
        tasks.append(self.loop.create_task(self.broadcast_beacon_loop()))

        async_lib = AsyncioAsyncLayer(self.loop)
        for name, method in self.startup_methods.items():
            self.log.debug('Calling startup method %r', name)
            tasks.append(self.loop.create_task(method(async_lib)))
        self.log.info('Server startup complete.')
        if log_pv_names:
            self.log.info('PVs available:\n%s', '\n'.join(self.pvdb))

        try:
            await asyncio.gather(*tasks)
        except asyncio.CancelledError:
            self.log.info('Server task cancelled. Will shut down.')
            all_tasks = (
                tasks + self._server_tasks +
                [c._cq_task for c in self.circuits if c._cq_task is not None] +
                [c._sq_task for c in self.circuits if c._sq_task is not None] +
                [t for c in self.circuits
                 for t in c._write_tasks] + list(self.p._tasks))
            for t in all_tasks:
                t.cancel()
            await asyncio.wait(all_tasks)
            return
        except Exception:
            self.log.exception('Server error. Will shut down')
            raise
        finally:
            self.log.info('Server exiting....')
            shutdown_tasks = []
            async_lib = AsyncioAsyncLayer(self.loop)
            for name, method in self.shutdown_methods.items():
                self.log.debug('Calling shutdown method %r', name)
                task = self.loop.create_task(method(async_lib))
                shutdown_tasks.append(task)
            await asyncio.gather(*shutdown_tasks)
            for sock in self.tcp_sockets.values():
                sock.close()
            for sock in self.udp_socks.values():
                sock.close()
            for interface, sock in self.beacon_socks.values():
                sock.close()
示例#6
0
    async def run(self, *, log_pv_names=False):
        'Start the server'
        self.log.info('Server starting up...')
        try:
            async with trio.open_nursery() as self.nursery:
                for address in ca.get_beacon_address_list():
                    sock = ca.bcast_socket(socket)
                    await sock.connect(address)
                    interface, _ = sock.getsockname()
                    self.beacon_socks[address] = (interface, sock)

                # This reproduces the common with
                # self._bind_tcp_sockets_with_consistent_port_number because
                # trio makes socket binding async where asyncio and curio make
                # it synchronous.
                # Find a random port number that is free on all interfaces,
                # and get a bound TCP socket with that port number on each
                # interface.
                tcp_sockets = {}  # maps interface to bound socket
                stashed_ex = None
                for port in ca.random_ports(100):
                    try:
                        for interface in self.interfaces:
                            s = trio.socket.socket()
                            await s.bind((interface, port))
                            tcp_sockets[interface] = s
                    except IOError as ex:
                        stashed_ex = ex
                        for s in tcp_sockets.values():
                            s.close()
                    else:
                        self.port = port
                        break
                else:
                    raise RuntimeError('No available ports and/or bind failed'
                                       ) from stashed_ex
                # (End of reproduced code)

                for interface, listen_sock in tcp_sockets.items():
                    self.log.info("Listening on %s:%d", interface, self.port)
                    await self.nursery.start(self.server_accept_loop,
                                             listen_sock)
                await self.nursery.start(self.broadcaster_udp_server_loop)
                await self.nursery.start(self.broadcaster_queue_loop)
                await self.nursery.start(self.subscription_queue_loop)
                await self.nursery.start(self.broadcast_beacon_loop)

                async_lib = TrioAsyncLayer()
                for name, method in self.startup_methods.items():
                    self.log.debug('Calling startup method %r', name)

                    async def startup(task_status):
                        task_status.started()
                        await method(async_lib)

                    await self.nursery.start(startup)
                self.log.info('Server startup complete.')
                if log_pv_names:
                    self.log.info('PVs available:\n%s', '\n'.join(self.pvdb))
        except trio.Cancelled:
            self.log.info('Server task cancelled. Will shut down.')
        finally:
            self.log.info('Server exiting....')
示例#7
0
    async def run(self, *, log_pv_names=False):
        'Start the server'
        self.log.info('Trio server starting up...')
        try:
            async with trio.open_nursery() as self.nursery:
                for address in ca.get_beacon_address_list():
                    sock = ca.bcast_socket(socket)
                    await sock.connect(address)
                    interface, _ = sock.getsockname()
                    self.beacon_socks[address] = (interface, sock)

                async def make_socket(interface, port):
                    s = trio.socket.socket()
                    await s.bind((interface, port))
                    return s

                res = await self._bind_tcp_sockets_with_consistent_port_number(
                    make_socket)
                self.port, self.tcp_sockets = res

                await self.nursery.start(self.broadcaster_udp_server_loop)
                await self.nursery.start(self.broadcaster_queue_loop)
                await self.nursery.start(self.subscription_queue_loop)
                await self.nursery.start(self.broadcast_beacon_loop)

                # Only after all loops have been started, begin listening:
                for interface, listen_sock in self.tcp_sockets.items():
                    self.log.info("Listening on %s:%d", interface, self.port)
                    await self.nursery.start(self.server_accept_loop,
                                             listen_sock)

                async_lib = TrioAsyncLayer()
                for name, method in self.startup_methods.items():
                    self.log.debug('Calling startup method %r', name)

                    async def startup(task_status):
                        task_status.started()
                        await method(async_lib)

                    await self.nursery.start(startup)
                self.log.info('Server startup complete.')
                if log_pv_names:
                    self.log.info('PVs available:\n%s', '\n'.join(self.pvdb))
        except trio.Cancelled:
            self.log.info('Server task cancelled. Will shut down.')
        finally:
            self.log.info('Server exiting....')
            async_lib = TrioAsyncLayer()
            async with trio.open_nursery() as nursery:
                for name, method in self.shutdown_methods.items():
                    self.log.debug('Calling shutdown method %r', name)

                    async def shutdown(task_status):
                        task_status.started()
                        await method(async_lib)

                    await nursery.start(shutdown)
            for sock in self.tcp_sockets.values():
                sock.close()
            for sock in self.udp_socks.values():
                sock.close()
            for interface, sock in self.beacon_socks.values():
                sock.close()
示例#8
0
    async def run(self, *, log_pv_names=False, startup_hook=None):
        'Start the server'
        self.log.info('Asyncio server starting up...')

        self.port, self.tcp_sockets = await self._bind_tcp_sockets_with_consistent_port_number(
            _create_bound_tcp_socket)
        tasks = _TaskHandler()
        for interface, sock in self.tcp_sockets.items():
            self.log.info("Listening on %s:%d", interface, self.port)
            self.broadcaster.server_addresses.append((interface, self.port))
            tasks.create(self.server_accept_loop(sock))

        for address in ca.get_beacon_address_list():
            sock = _create_udp_socket()
            try:
                sock.connect(address)
            except Exception as ex:
                self.log.error(
                    'Beacon (%s:%d) socket setup failed: %s',
                    *address,
                    ex,
                )
                continue

            wrapped_transport = _UdpTransportWrapper(sock,
                                                     address,
                                                     loop=self.loop)
            self.beacon_socks[address] = (
                interface,  # TODO; this is incorrect
                wrapped_transport)

        for interface in self.interfaces:
            sock = _create_udp_socket()
            sock.bind((interface, self.ca_server_port))
            transport, _ = await self.loop.create_datagram_endpoint(
                functools.partial(_DatagramProtocol,
                                  parent=self,
                                  identifier=interface,
                                  queue=self.broadcaster_datagram_queue),
                sock=sock,
            )
            self.udp_socks[interface] = _UdpTransportWrapper(transport,
                                                             loop=self.loop)
            self.log.debug('UDP socket bound on %s:%d', interface,
                           self.ca_server_port)

        tasks.create(self.broadcaster_receive_loop())
        tasks.create(self.broadcaster_queue_loop())
        tasks.create(self.subscription_queue_loop())
        tasks.create(self.broadcast_beacon_loop())

        async_lib = AsyncioAsyncLayer()

        if startup_hook is not None:
            self.log.debug('Calling startup hook %r', startup_hook.__name__)
            tasks.create(startup_hook(async_lib))

        for name, method in self.startup_methods.items():
            self.log.debug('Calling startup method %r', name)
            tasks.create(method(async_lib))
        self.log.info('Server startup complete.')
        if log_pv_names:
            self.log.info('PVs available:\n%s', '\n'.join(self.pvdb))

        try:
            await asyncio.gather(*tasks.tasks)
        except asyncio.CancelledError:
            self.log.info('Server task cancelled. Will shut down.')
            await tasks.cancel_all()
            await self.server_tasks.cancel_all()
            for circuit in self.circuits:
                await circuit.tasks.cancel_all()
            return
        except Exception:
            self.log.exception('Server error. Will shut down')
            raise
        finally:
            self.log.info('Server exiting....')
            shutdown_tasks = []
            async_lib = AsyncioAsyncLayer()
            for name, method in self.shutdown_methods.items():
                self.log.debug('Calling shutdown method %r', name)
                task = self.loop.create_task(method(async_lib))
                shutdown_tasks.append(task)
            await asyncio.gather(*shutdown_tasks)
            for sock in self.tcp_sockets.values():
                sock.close()
            for sock in self.udp_socks.values():
                sock.close()
            for _, sock in self.beacon_socks.values():
                sock.close()
示例#9
0
    async def run(self, *, log_pv_names=False):
        'Start the server'
        self.log.info('Server starting up...')

        def make_socket(interface, port):
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
            s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            s.setblocking(False)
            s.bind((interface, port))
            return s
        port, tcp_sockets = self._bind_tcp_sockets_with_consistent_port_number(
            make_socket)
        self.port = port
        tasks = []
        for interface, sock in tcp_sockets.items():
            self.log.info("Listening on %s:%d", interface, self.port)
            tasks.append(self.loop.create_task(self.server_accept_loop(sock)))

        class BcastLoop(asyncio.Protocol):
            parent = self
            loop = self.loop

            def __init__(self, *args, **kwargs):
                self.transport = None
                self._tasks = ()

            def connection_made(self, transport):
                self.transport = transport

            def datagram_received(self, data, addr):
                tsk = self.loop.create_task(self.parent._broadcaster_recv_datagram(
                    data, addr))
                self._tasks = tuple(t for t in self._tasks + (tsk,)
                                    if not t.done())

        class TransportWrapper:
            """Make an asyncio transport something you can call sendto on."""
            def __init__(self, transport):
                self.transport = transport

            async def sendto(self, bytes_to_send, addr_port):
                self.transport.sendto(bytes_to_send, addr_port)

        class ConnectedTransportWrapper:
            """Make an asyncio transport something you can call send on."""
            def __init__(self, transport, address):
                self.transport = transport
                self.address = address

            async def send(self, bytes_to_send):
                self.transport.sendto(bytes_to_send, self.address)

        for address in ca.get_beacon_address_list():
            # Connected sockets do not play well with asyncio, so connect to
            # one and then discard it.
            temp_sock = ca.bcast_socket(socket)
            temp_sock.connect(address)
            interface, _ = temp_sock.getsockname()
            temp_sock.close()
            sock = ca.bcast_socket(socket)
            transport, _ = await self.loop.create_datagram_endpoint(
                BcastLoop, sock=sock)
            wrapped_transport = ConnectedTransportWrapper(transport, address)
            self.beacon_socks[address] = (interface, wrapped_transport)

        for interface in self.interfaces:
            udp_sock = bcast_socket()
            try:
                udp_sock.bind((interface, ca.EPICS_CA1_PORT))
            except Exception:
                self.log.exception('UDP bind failure on interface %r',
                                   interface)
                raise

            transport, self.p = await self.loop.create_datagram_endpoint(
                BcastLoop, sock=udp_sock)
            self.udp_socks[interface] = TransportWrapper(transport)
            self.log.debug('Broadcasting on %s:%d', interface,
                           ca.EPICS_CA1_PORT)

        tasks.append(self.loop.create_task(self.broadcaster_queue_loop()))
        tasks.append(self.loop.create_task(self.subscription_queue_loop()))
        tasks.append(self.loop.create_task(self.broadcast_beacon_loop()))

        async_lib = AsyncioAsyncLayer(self.loop)
        for name, method in self.startup_methods.items():
            self.log.debug('Calling startup method %r', name)
            tasks.append(self.loop.create_task(method(async_lib)))
        self.log.info('Server startup complete.')
        if log_pv_names:
            self.log.info('PVs available:\n%s', '\n'.join(self.pvdb))

        try:
            await asyncio.gather(*tasks)
        except asyncio.CancelledError:
            self.log.info('Server task cancelled. Will shut down.')
            udp_sock.close()
            all_tasks = (tasks + self._server_tasks + [c._cq_task
                                                       for c in self.circuits
                                                       if c._cq_task is not None] +
                         [t for c in self.circuits for t in c._write_tasks] +
                         list(self.p._tasks))
            for t in all_tasks:
                t.cancel()
            await asyncio.wait(all_tasks)
            return
        except Exception as ex:
            self.log.exception('Server error. Will shut down')
            udp_sock.close()
            raise
        finally:
            self.log.info('Server exiting....')
示例#10
0
    async def run(self, *, log_pv_names=False):
        'Start the server'
        self.log.info('Asyncio server starting up...')

        async def make_socket(interface, port):
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
            s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            s.setblocking(False)
            s.bind((interface, port))
            return s

        self.port, self.tcp_sockets = await self._bind_tcp_sockets_with_consistent_port_number(
            make_socket)
        tasks = _TaskHandler()
        for interface, sock in self.tcp_sockets.items():
            self.log.info("Listening on %s:%d", interface, self.port)
            self.broadcaster.server_addresses.append((interface, self.port))
            tasks.create(self.server_accept_loop(sock))

        class ConnectedTransportWrapper:
            """Make an asyncio transport something you can call send on."""
            def __init__(self, transport, address):
                self.transport = transport
                self.address = address

            async def send(self, bytes_to_send):
                try:
                    self.transport.sendto(bytes_to_send, self.address)
                except OSError as exc:
                    host, port = self.address
                    raise ca.CaprotoNetworkError(
                        f"Failed to send to {host}:{port}") from exc

            def close(self):
                return self.transport.close()

        reuse_port = sys.platform not in ('win32', ) and hasattr(
            socket, 'SO_REUSEPORT')
        for address in ca.get_beacon_address_list():
            transport, _ = await self.loop.create_datagram_endpoint(
                functools.partial(_DatagramProtocol,
                                  parent=self,
                                  recv_func=self._datagram_received),
                remote_addr=address,
                allow_broadcast=True,
                reuse_port=reuse_port)
            wrapped_transport = ConnectedTransportWrapper(transport, address)
            self.beacon_socks[address] = (
                interface,  # TODO; this is incorrect
                wrapped_transport)

        for interface in self.interfaces:
            sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM,
                                 socket.IPPROTO_UDP)
            # Python says this is unsafe, but we need it to have
            # multiple servers live on the same host.
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            if reuse_port:
                sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
            sock.setblocking(False)
            sock.bind((interface, self.ca_server_port))

            transport, _ = await self.loop.create_datagram_endpoint(
                functools.partial(_DatagramProtocol,
                                  parent=self,
                                  recv_func=self._datagram_received),
                sock=sock)
            self.udp_socks[interface] = _TransportWrapper(transport)
            self.log.debug('UDP socket bound on %s:%d', interface,
                           self.ca_server_port)

        tasks.create(self.broadcaster_queue_loop())
        tasks.create(self.subscription_queue_loop())
        tasks.create(self.broadcast_beacon_loop())

        async_lib = AsyncioAsyncLayer()
        for name, method in self.startup_methods.items():
            self.log.debug('Calling startup method %r', name)
            tasks.create(method(async_lib))
        self.log.info('Server startup complete.')
        if log_pv_names:
            self.log.info('PVs available:\n%s', '\n'.join(self.pvdb))

        try:
            await asyncio.gather(*tasks.tasks)
        except asyncio.CancelledError:
            self.log.info('Server task cancelled. Will shut down.')
            await tasks.cancel_all()
            await self.server_tasks.cancel_all()
            for circuit in self.circuits:
                await circuit.tasks.cancel_all()
            return
        except Exception:
            self.log.exception('Server error. Will shut down')
            raise
        finally:
            self.log.info('Server exiting....')
            shutdown_tasks = []
            async_lib = AsyncioAsyncLayer()
            for name, method in self.shutdown_methods.items():
                self.log.debug('Calling shutdown method %r', name)
                task = self.loop.create_task(method(async_lib))
                shutdown_tasks.append(task)
            await asyncio.gather(*shutdown_tasks)
            for sock in self.tcp_sockets.values():
                sock.close()
            for sock in self.udp_socks.values():
                sock.close()
            for _interface, sock in self.beacon_socks.values():
                sock.close()