Esempio n. 1
0
 async def send(self, bytes_to_send):
     """Sends data."""
     if self.address is None:
         raise ca.CaprotoNetworkError(
             "Cannot send on a disconnected UDP transport")
     try:
         self.transport.send(bytes_to_send)
     except OSError as exc:
         raise ca.CaprotoNetworkError(
             f"Failed to send to {self.address[0]}:{self.address[1]}"
         ) from exc
Esempio n. 2
0
 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
Esempio n. 3
0
 async def send(self, port, *commands):
     """
     Process a command and tranport it over the UDP socket.
     """
     bytes_to_send = self.broadcaster.send(*commands)
     tags = {
         'role': 'CLIENT',
         'our_address': self.broadcaster.client_address,
         'direction': '--->>>'
     }
     for host in ca.get_address_list():
         if ':' in host:
             host, _, port_as_str = host.partition(':')
             specified_port = int(port_as_str)
         else:
             specified_port = port
         tags['their_address'] = (host, specified_port)
         self.broadcaster.log.debug('%d commands %dB',
                                    len(commands),
                                    len(bytes_to_send),
                                    extra=tags)
         try:
             await self.udp_sock.sendto(bytes_to_send,
                                        (host, specified_port))
         except OSError as ex:
             raise ca.CaprotoNetworkError(
                 f'{ex} while sending {len(bytes_to_send)} bytes to '
                 f'{host}:{specified_port}') from ex
Esempio n. 4
0
 async def sendto(self, bytes_to_send, address):
     """Sends data to address."""
     try:
         self.transport.sendto(bytes_to_send, address)
     except OSError as exc:
         raise ca.CaprotoNetworkError(
             f"Failed to send to {self.address[0]}:{self.address[1]}"
         ) from exc
Esempio n. 5
0
 def send_search():
     for dest in client_address_list:
         tags['their_address'] = dest
         b.log.debug(
             '%d commands %dB',
             len(commands), len(bytes_to_send), extra=tags)
         try:
             udp_sock.sendto(bytes_to_send, dest)
         except OSError as exc:
             host, port = dest
             raise ca.CaprotoNetworkError(f"Failed to send to {host}:{port}") from exc
Esempio n. 6
0
 def send_search():
     for host in ca.get_address_list():
         if ':' in host:
             host, _, specified_port = host.partition(':')
             dest = (host, int(specified_port))
         else:
             dest = (host, CA_SERVER_PORT)
         try:
             udp_sock.sendto(bytes_to_send, dest)
         except OSError as exc:
             host, port = dest
             raise ca.CaprotoNetworkError(f"Failed to send to {host}:{port}") from exc
         logger.debug('Search request sent to %r.', dest)
Esempio n. 7
0
    async def send(self, bytes_to_send):
        """Sends data over a connected socket."""
        try:
            async with self.lock:
                self.writer.write(bytes_to_send)
                await self.writer.drain()
        except OSError as exc:
            try:
                host, port = self.getpeername()
            except Exception:
                destination = ''
            else:
                destination = f' to {host}:{port}'

            raise ca.CaprotoNetworkError(
                f"Failed to send{destination}") from exc
Esempio n. 8
0
 def send_search():
     for host in ca.get_address_list():
         if ':' in host:
             host, _, specified_port = host.partition(':')
             dest = (host, int(specified_port))
         else:
             dest = (host, CA_SERVER_PORT)
         tags['their_address'] = dest
         b.log.debug('%d commands %dB',
                     len(commands),
                     len(bytes_to_send),
                     extra=tags)
         try:
             udp_sock.sendto(bytes_to_send, dest)
         except OSError as exc:
             host, port = dest
             raise ca.CaprotoNetworkError(
                 f"Failed to send to {host}:{port}") from exc
Esempio n. 9
0
 async def send(self, port, *commands):
     """
     Process a command and tranport it over the UDP socket.
     """
     bytes_to_send = self.broadcaster.send(*commands)
     for host in ca.get_address_list():
         if ':' in host:
             host, _, port_as_str = host.partition(':')
             specified_port = int(port_as_str)
         else:
             specified_port = port
         self.broadcaster.log.debug('Sending %d bytes to %s:%d',
                                    len(bytes_to_send), host,
                                    specified_port)
         try:
             await self.udp_sock.sendto(bytes_to_send,
                                        (host, specified_port))
         except OSError as ex:
             raise ca.CaprotoNetworkError(
                 f'{ex} while sending {len(bytes_to_send)} bytes to '
                 f'{host}:{specified_port}') from ex
Esempio n. 10
0
def search(pv_name, udp_sock, timeout, *, max_retries=2):
    # Set Broadcaster log level to match our logger.
    b = ca.Broadcaster(our_role=ca.CLIENT)
    b.client_address = safe_getsockname(udp_sock)

    # Send registration request to the repeater
    logger.debug('Registering with the Channel Access repeater.')
    bytes_to_send = b.send(ca.RepeaterRegisterRequest())

    env = get_environment_variables()
    repeater_port = env['EPICS_CA_REPEATER_PORT']

    client_address_list = ca.get_client_address_list()
    local_address = ca.get_local_address()

    try:
        udp_sock.sendto(bytes_to_send, (local_address, repeater_port))
    except OSError as exc:
        raise ca.CaprotoNetworkError(
            f"Failed to send to {local_address}:{repeater_port}") from exc

    logger.debug("Searching for %r....", pv_name)
    commands = (ca.VersionRequest(0, ca.DEFAULT_PROTOCOL_VERSION),
                ca.SearchRequest(pv_name, 0, ca.DEFAULT_PROTOCOL_VERSION))
    bytes_to_send = b.send(*commands)
    tags = {
        'role': 'CLIENT',
        'our_address': b.client_address,
        'direction': '--->>>'
    }

    def send_search():
        for dest in client_address_list:
            tags['their_address'] = dest
            b.log.debug('%d commands %dB',
                        len(commands),
                        len(bytes_to_send),
                        extra=tags)
            try:
                udp_sock.sendto(bytes_to_send, dest)
            except OSError as exc:
                host, port = dest
                raise ca.CaprotoNetworkError(
                    f"Failed to send to {host}:{port}") from exc

    def check_timeout():
        nonlocal retry_at

        if time.monotonic() >= retry_at:
            send_search()
            retry_at = time.monotonic() + retry_timeout

        if time.monotonic() - t > timeout:
            raise CaprotoTimeoutError(
                f"Timed out while awaiting a response "
                f"from the search for {pv_name!r}. Search "
                f"requests were sent to this address list: "
                f"{ca.get_address_list()}.")

    # Initial search attempt
    send_search()

    # Await a search response, and keep track of registration status
    retry_timeout = timeout / max((max_retries, 1))
    t = time.monotonic()
    retry_at = t + retry_timeout

    try:
        orig_timeout = udp_sock.gettimeout()
        udp_sock.settimeout(retry_timeout)
        while True:
            try:
                bytes_received, address = udp_sock.recvfrom(ca.MAX_UDP_RECV)
            except socket.timeout:
                check_timeout()
                continue

            check_timeout()

            commands = b.recv(bytes_received, address)
            b.process_commands(commands)
            for command in commands:
                if isinstance(command, ca.SearchResponse) and command.cid == 0:
                    address = ca.extract_address(command)
                    logger.debug('Found %r at %s:%d', pv_name, *address)
                    return address
            else:
                # None of the commands we have seen are a reply to our request.
                # Receive more data.
                continue
    finally:
        udp_sock.settimeout(orig_timeout)
Esempio n. 11
0
def _run_repeater(server_sock, bind_addr):
    'Run the repeater using server_socket and bind_address'
    bind_host, bind_port = bind_addr

    servers = {}
    clients = {}
    broadcaster = caproto.Broadcaster(our_role=caproto.SERVER)

    logger.info("Repeater is listening on %s:%d", bind_host, bind_port)
    while True:
        msg, addr = server_sock.recvfrom(MAX_UDP_RECV)
        host, port = addr

        if port in clients and clients[port] != host:
            # broadcast only from one interface
            continue
        elif (port in servers and servers[port]['host'] != host):
            continue

        try:
            commands = broadcaster.recv(msg, addr)
            broadcaster.process_commands(commands)
        except Exception:
            logger.exception('Failed to process incoming datagram')
            continue

        if not commands:
            # NOTE: additional valid way of registration is an empty
            # message, according to broadcaster source
            if port not in clients:
                clients[port] = host
                logger.debug('New client %s (zero-length registration)', addr)
            continue

        to_forward = []
        for command in commands:
            if isinstance(command, caproto.Beacon):
                # Update our records of the last time each server checked in
                # (i.e. issued a heartbeat).
                servers[command.server_port] = dict(up_at=time.time(),
                                                    host=host)
                # The sender of this command may leave the IP field empty (0),
                # leaving it up to the repeater to fill in the address so that
                # the ultimate recipient knows the correct origin. By leaving
                # that up to the repeater, the sender avoids providing the
                # wrong return address (i.e. picking the wrong interface). It
                # is safer to let the repeater determine the return address
                # by inspection.
                if command.header.parameter2 == 0:
                    updated_ip = caproto.ipv4_to_int32(host)
                    command.header.parameter2 = updated_ip
                to_forward.append(command)
            elif isinstance(command, caproto.RepeaterRegisterRequest):
                if port not in clients:
                    clients[port] = host
                    logger.debug('New client %s', addr)

                confirmation_bytes = broadcaster.send(
                    caproto.RepeaterConfirmResponse(host))

                try:
                    server_sock.sendto(confirmation_bytes, (host, port))
                except OSError as exc:
                    raise caproto.CaprotoNetworkError(
                        f"Failed to send to {host}:{port}") from exc

                remove_clients = list(check_clients(clients, skip=port))
                _update_all(clients, servers, remove_clients=remove_clients)
                # Do not broadcast registration requests to other clients.
                # Omit it from `to_forward`.
            else:
                to_forward.append(command)
        bytes_to_broadcast = b''.join(bytes(cmd) for cmd in to_forward)
        to_remove = []
        for other_port, other_host in clients.items():
            if other_port != port:
                try:
                    server_sock.sendto(bytes_to_broadcast,
                                       (other_host, other_port))
                except Exception:
                    to_remove.append((other_host, other_port))

        if to_remove:
            _update_all(clients, servers, remove_clients=to_remove)
Esempio n. 12
0
 async def recv(self, nbytes=4096):
     """Receive from the socket."""
     try:
         return await self.reader.read(nbytes)
     except OSError as exc:
         raise ca.CaprotoNetworkError(f"Failed to receive: {exc}") from exc
Esempio n. 13
0
def search(pv_name, udp_sock, timeout, *, max_retries=2):
    # Set Broadcaster log level to match our logger.
    b = ca.Broadcaster(our_role=ca.CLIENT)

    # Send registration request to the repeater
    logger.debug('Registering with the Channel Access repeater.')
    bytes_to_send = b.send(ca.RepeaterRegisterRequest())

    repeater_port = os.environ.get('EPICS_CA_REPEATER_PORT', 5065)
    for host in ca.get_address_list():
        try:
            udp_sock.sendto(bytes_to_send, (host, repeater_port))
        except OSError as exc:
            raise ca.CaprotoNetworkError(
                f"Failed to send to {host}:{repeater_port}") from exc

    logger.debug("Searching for '%s'....", pv_name)
    bytes_to_send = b.send(
        ca.VersionRequest(0, ca.DEFAULT_PROTOCOL_VERSION),
        ca.SearchRequest(pv_name, 0, ca.DEFAULT_PROTOCOL_VERSION))

    def send_search():
        for host in ca.get_address_list():
            if ':' in host:
                host, _, specified_port = host.partition(':')
                dest = (host, int(specified_port))
            else:
                dest = (host, CA_SERVER_PORT)
            try:
                udp_sock.sendto(bytes_to_send, dest)
            except OSError as exc:
                host, port = dest
                raise ca.CaprotoNetworkError(
                    f"Failed to send to {host}:{port}") from exc
            logger.debug('Search request sent to %r.', dest)

    def check_timeout():
        nonlocal retry_at

        if time.monotonic() >= retry_at:
            send_search()
            retry_at = time.monotonic() + retry_timeout

        if time.monotonic() - t > timeout:
            raise CaprotoTimeoutError(
                f"Timed out while awaiting a response "
                f"from the search for {pv_name!r}. Search "
                f"requests were sent to this address list: "
                f"{ca.get_address_list()}.")

    # Initial search attempt
    send_search()

    # Await a search response, and keep track of registration status
    retry_timeout = timeout / max((max_retries, 1))
    t = time.monotonic()
    retry_at = t + retry_timeout

    try:
        orig_timeout = udp_sock.gettimeout()
        udp_sock.settimeout(retry_timeout)
        while True:
            try:
                bytes_received, address = udp_sock.recvfrom(ca.MAX_UDP_RECV)
            except socket.timeout:
                check_timeout()
                continue

            check_timeout()

            commands = b.recv(bytes_received, address)
            b.process_commands(commands)
            for command in commands:
                if isinstance(command, ca.SearchResponse) and command.cid == 0:
                    address = ca.extract_address(command)
                    logger.debug('Found %s at %s', pv_name, address)
                    return address
            else:
                # None of the commands we have seen are a reply to our request.
                # Receive more data.
                continue
    finally:
        udp_sock.settimeout(orig_timeout)