def create(self, link_type, interface_a, interface_b, port_a, port_b,
               **kwargs):
        from peering_coord.api.client_connection import (ClientRegistry,
                                                         create_link_update)

        link = super().create(link_type=link_type,
                              interface_a=interface_a,
                              interface_b=interface_b,
                              port_a=port_a,
                              port_b=port_b,
                              **kwargs)

        update = create_link_update(LinkUpdate.Type.CREATE,
                                    link_type=link_type,
                                    local_interface=interface_a,
                                    local_port=port_a,
                                    remote_interface=interface_b,
                                    remote_port=port_b)
        ClientRegistry.send_link_update(interface_a.peering_client.asys.asn,
                                        update)

        update = create_link_update(LinkUpdate.Type.CREATE,
                                    link_type=link_type,
                                    local_interface=interface_b,
                                    local_port=port_b,
                                    remote_interface=interface_a,
                                    remote_port=port_a)
        ClientRegistry.send_link_update(interface_b.peering_client.asys.asn,
                                        update)

        return link
 def is_connected(self) -> bool:
     """Returns whether the client is connected to the coordinator."""
     from peering_coord.api.client_connection import ClientRegistry
     connections = ClientRegistry.get_clients(self.asys.asn)
     if connections is None:
         return False
     else:
         return self.name in connections.connections
Esempio n. 3
0
def _create_links(vlan: VLAN, as_a: AS, as_b: AS):
    """Create links between all interfaces of `as_a` and `as_b` in `vlan`.

    The link type is determined from the AS types.
    """
    # Figure out which link type to use.
    if as_a.is_core and as_b.is_core:
        link_type = Link.Type.CORE
    elif not as_a.is_core and not as_b.is_core:
        link_type = Link.Type.PEERING
    elif as_a.isd == as_b.isd:
        link_type = Link.Type.PROVIDER
        if not as_a.is_core and as_b.is_core:
            as_a, as_b = as_b, as_a
    else:
        error = AsyncError()
        error.code = AsyncError.Code.LINK_CREATION_FAILED
        error.message = "Cannot create a link between ASes {} and {} of incompatible type.".format(
            as_a, as_b)
        ClientRegistry.send_async_error(as_a.asn, error)
        ClientRegistry.send_async_error(as_b.asn, error)
        return

    for interface_a in as_a.query_interfaces().filter(vlan=vlan).all():
        for interface_b in as_b.query_interfaces().filter(vlan=vlan).all():
            port_a = port_b = None

            try:
                port_a = interface_a.get_unused_port()
            except Interface.NoUnusedPorts:
                error = AsyncError()
                error.code = AsyncError.Code.LINK_CREATION_FAILED
                error.message = "Allocated port range is exhausted on interface {}.".format(
                    interface_a)
                ClientRegistry.send_async_error(as_a.asn, error)

            try:
                port_b = interface_b.get_unused_port()
            except Interface.NoUnusedPorts:
                error = AsyncError()
                error.code = AsyncError.Code.LINK_CREATION_FAILED
                error.message = "Allocated port range is exhausted on interface {}.".format(
                    interface_b)
                ClientRegistry.send_async_error(as_b.asn, error)

            if port_a and port_b:
                Link.objects.create(link_type,
                                    interface_a=interface_a,
                                    interface_b=interface_b,
                                    port_a=port_a,
                                    port_b=port_b)
def delete_link_hook(sender, instance, using, **kwargs):
    from peering_coord.api.client_connection import (ClientRegistry,
                                                     create_link_update)

    update = create_link_update(LinkUpdate.Type.DESTROY,
                                link_type=instance.link_type,
                                local_interface=instance.interface_a,
                                local_port=instance.port_a,
                                remote_interface=instance.interface_b,
                                remote_port=instance.port_b)
    ClientRegistry.send_link_update(
        instance.interface_a.peering_client.asys.asn, update)

    update = create_link_update(LinkUpdate.Type.DESTROY,
                                link_type=instance.link_type,
                                local_interface=instance.interface_b,
                                local_port=instance.port_b,
                                remote_interface=instance.interface_a,
                                remote_port=instance.port_a)
    ClientRegistry.send_link_update(
        instance.interface_b.peering_client.asys.asn, update)
Esempio n. 5
0
def _assert_policy_write_permission(context,
                                    asn: ASN,
                                    client: str,
                                    vlan: Optional[str] = None):
    """Helper function for checking whether a client is allowed to alter the pering policies.
    Triggers an exception to abort the RPC if the client does not have sufficient permissions.

    :param context: gRPC service context
    :param asn: AS the client belongs to.
    :param client: Peering client name.
    :param vlan: VLAN for which write permissions are checked. If None, checks if the client has
                 write access to every VLAN.
    """
    try:
        if not ClientRegistry.has_policy_write_permissions(asn, client, vlan):
            context.abort(grpc.StatusCode.PERMISSION_DENIED,
                          "Insufficient permissions")
    except:
        context.abort(grpc.StatusCode.PERMISSION_DENIED,
                      "Insufficient permissions")
Esempio n. 6
0
    def StreamChannel(self, request_iterator, context):
        """Server side of the persistent bidirectional gRPC stream peering clients maintain with the
        coordinator.

        Since bidirectional gRPC streams in Python use blocking generators for sending and
        receiving, an additional thread just for reading request from the stream is created for
        every connection. The request listener threads forwards received requests to the main thread
        handling the connection via the associated ClientConnection object.
        """
        asn, client_name = get_client_from_metadata(
            context.invocation_metadata())
        asn = ASN(asn)

        # Register the connection
        try:
            conn = ClientRegistry.createConnection(asn, client_name)
        except KeyError as e:
            context.abort(grpc.StatusCode.NOT_FOUND, str(e))
        except ClientConnections.AlreadyConnected as e:
            context.abort(grpc.StatusCode.ALREADY_EXISTS, str(e))
        except:
            context.abort(grpc.StatusCode.INTERNAL, "Internal error")

        # Enqueue link create messages for all existing links.
        try:
            client = PeeringClient.objects.get(asys__asn=asn, name=client_name)
            for interface in Interface.objects.filter(
                    peering_client=client).all():
                for link in interface.query_links().all():
                    if link.interface_a == interface:
                        update = create_link_update(
                            peering_pb2.LinkUpdate.Type.CREATE,
                            link_type=link.link_type,
                            local_interface=link.interface_a,
                            local_port=link.port_a,
                            remote_interface=link.interface_b,
                            remote_port=link.port_b)
                    else:
                        update = create_link_update(
                            peering_pb2.LinkUpdate.Type.CREATE,
                            link_type=link.link_type,
                            local_interface=link.interface_b,
                            local_port=link.port_b,
                            remote_interface=link.interface_a,
                            remote_port=link.port_a)
                    conn.send_link_update(update)
        except:
            ClientRegistry.destroyConnection(conn)
            raise

        # Launch a new thread to listen for requests from the client.
        def stream_listener():
            try:
                for request in request_iterator:
                    conn.stream_request_received(request)
            except grpc.RpcError:
                pass
            finally:
                conn.request_stream_closed()

        listener = threading.Thread(
            target=stream_listener,
            name="gRPC stream listener for {}-{}".format(asn, client_name))
        listener.start()

        # Run the event loop to process requests and generate responses.
        try:
            for response in conn.run():
                yield response
        finally:
            ClientRegistry.destroyConnection(conn)
            listener.join()
 def count_connected_clients(self) -> int:
     """Returns the number of active peering clients."""
     from peering_coord.api.client_connection import ClientRegistry
     return len(ClientRegistry.get_clients(self.asn) or [])
    def testArbitration(self):
        stub = peering_pb2_grpc.PeeringStub(self.channel)
        default_call_cred = [(ASN_HEADER_KEY, "ff00:0:0"),
                             (CLIENT_NAME_HEADER_KEY, "default")]
        backup_call_cred = [(ASN_HEADER_KEY, "ff00:0:0"),
                            (CLIENT_NAME_HEADER_KEY, "backup")]

        default_request_queue = queue.Queue()
        backup_request_queue = queue.Queue()
        self.assertIsNone(ClientRegistry.get_clients(ASN("ff00:0:0")))

        # Connect the backup client
        backup_channel = stub.StreamChannel(iter(backup_request_queue.get,
                                                 None),
                                            metadata=backup_call_cred)

        request = peering_pb2.StreamMessageRequest()
        request.arbitration.election_id = 0
        backup_request_queue.put(request)

        response = next(backup_channel)
        self.assertEqual(response.arbitration.vlan, "prod")
        self.assertEqual(response.arbitration.status,
                         peering_pb2.ArbitrationUpdate.Status.PRIMARY)
        clients = ClientRegistry.get_clients(ASN("ff00:0:0"))
        self.assertEqual(len(clients.connections), 1)
        self.assertTrue(clients.is_primary_client("backup", "prod"))
        self.assertFalse(clients.is_primary_client("default", "test"))

        # Connect the default client
        default_channel = stub.StreamChannel(iter(default_request_queue.get,
                                                  None),
                                             metadata=default_call_cred)

        request = peering_pb2.StreamMessageRequest()
        request.arbitration.election_id = 100
        default_request_queue.put(request)

        response = next(default_channel)
        self.assertEqual(response.arbitration.vlan, "prod")
        self.assertEqual(response.arbitration.status,
                         peering_pb2.ArbitrationUpdate.Status.PRIMARY)
        response = next(default_channel)
        self.assertEqual(response.arbitration.vlan, "test")
        self.assertEqual(response.arbitration.status,
                         peering_pb2.ArbitrationUpdate.Status.PRIMARY)

        response = next(backup_channel)
        self.assertEqual(response.arbitration.vlan, "prod")
        self.assertEqual(response.arbitration.status,
                         peering_pb2.ArbitrationUpdate.Status.NOT_PRIMARY)
        self.assertEqual(len(clients.connections), 2)
        self.assertFalse(clients.is_primary_client("backup", "prod"))
        self.assertTrue(clients.is_primary_client("default", "prod"))
        self.assertTrue(clients.is_primary_client("default", "test"))

        # Disconnect the default client
        default_request_queue.put(None)
        for response in default_channel:
            self.assertTrue(False, "Unexpected additional messages on stream")

        response = next(backup_channel)
        self.assertEqual(response.arbitration.status,
                         peering_pb2.ArbitrationUpdate.Status.PRIMARY)
        self.assertEqual(len(clients.connections), 1)
        self.assertTrue(clients.is_primary_client("backup", "prod"))
        self.assertFalse(clients.is_primary_client("default", "test"))

        # Disconnect the backup client
        backup_request_queue.put(None)
        for response in backup_channel:
            self.assertTrue(False, "Unexpected additional messages on stream")

        self.assertIsNone(ClientRegistry.get_clients(ASN("ff00:0:0")))
def delete_interface_hook(sender, instance, using, **kwargs):
    from peering_coord.api.client_connection import ClientRegistry

    ClientRegistry.remove_interface(instance.peering_client.asys.asn,
                                    instance.peering_client.name,
                                    instance.vlan.name)
Esempio n. 10
0
def delete_peering_client_hook(sender, instance, using, **kwargs):
    from peering_coord.api.client_connection import ClientRegistry
    ClientRegistry.remove_client(instance.asn.asys, instance.name)