Esempio n. 1
0
def fetch_keyset_from_source(  # noqa: C901
    client, source, token, wait_timeout=180.0, allow_exists=False
):  # noqa
    """fetch_keyset_from_source Put keyset by providing source controller to download keyset. This
    contains logic that handles whether or not fetching from the source fails, typically due
    to a firewall or routing issue in the underlay network (e.g. security groups and route tables).

    Pseudo-logic:
        PUT new keyset request to fetch from remote controller
        if keyset exists or already in progress, fail immediately as its unexpected
        if PUT succees:
            wait:
                if a new successful put returns: that indicates failure to download from source. return 400
                if timeout: that indicates controller is rebooting, return wait for keyset
                if keyset already exists, wait to ensure keyset  exists, then return keyset details

    Arguments:
        source {str} - host controller to fetch keyset from
        token {str} - secret token used when generating keyset
        wait_timeout {float} - timeout for waiting for keyset and while polling for download failure (default: 1 min)
        allow_exists {bool} - If true and keyset already exists, DONT throw exception

    Raises:
        e: ApiException or CohesiveSDKException

    Returns:
        KeysetDetail
    """
    sleep_time = 2.0
    failure_error_str = (
        "Failed to fetch keyset for source. This typically due to a misconfigured "
        "firewall or routing issue between source and client controllers."
    )

    try:
        put_response = client.config.put_keyset(**{"source": source, "token": token})
    except ApiException as e:
        if allow_exists and ("keyset already exists" in e.get_error_message().lower()):
            Logger.info("Keyset already exists.", host=client.host_uri)
            return client.config.try_get_keyset()

        Logger.info(
            "Failed to fetch keyset: %s" % e.get_error_message(),
            host=client.host_uri,
        )
        raise e
    except UrlLib3ConnExceptions:
        raise ApiException(
            status=HTTPStatus.SERVICE_UNAVAILABLE,
            reason="Controller unavailable. It is likely rebooting. Try client.sys_admin.wait_for_api().",
        )

    if not put_response.response:
        keyset_data = client.config.get_keyset()
        if keyset_data.response and keyset_data.response.keyset_present:
            raise ApiException(status=400, reason="Keyset already exists.")
        raise ApiException(status=500, reason="Put keyset returned None.")

    start_time = put_response.response.started_at_i
    Logger.info(message="Keyset downloading from source.", start_time=start_time)
    polling_start = time.time()
    while time.time() - polling_start <= wait_timeout:
        try:
            duplicate_call_resp = client.config.put_keyset(
                **{"source": source, "token": token}
            )
        except UrlLib3ConnExceptions:
            Logger.info(
                "API call timeout. Controller is likely rebooting. Waiting for keyset.",
                wait_timeout=wait_timeout,
                source=source,
            )
            client.sys_admin.wait_for_api(timeout=wait_timeout, wait_for_reboot=True)
            return client.config.wait_for_keyset(timeout=wait_timeout)
        except ApiException as e:
            duplicate_call_error = e.get_error_message()

            if duplicate_call_error == "Keyset already exists.":
                keyset_data = client.config.try_get_keyset()
                if not keyset_data:
                    Logger.info(
                        "Keyset exists. Waiting for reboot.",
                        wait_timeout=wait_timeout,
                        source=source,
                    )
                    client.sys_admin.wait_for_api(
                        timeout=wait_timeout, wait_for_reboot=True
                    )
                    return client.config.wait_for_keyset()
                return keyset_data

            if duplicate_call_error == "Keyset setup in progress.":
                # this means download is in progress, but might fail. Wait and retry
                time.sleep(sleep_time)
                continue

            # Unexpected ApiException
            raise e

        # If there is a new start time for keyset generation, that indicates a failure
        new_start_resp = duplicate_call_resp.response
        new_start = new_start_resp.started_at_i if new_start_resp else None
        if (new_start and start_time) and (new_start != start_time):
            Logger.error(failure_error_str, source=source)
            raise ApiException(status=HTTPStatus.BAD_REQUEST, reason=failure_error_str)

        time.sleep(sleep_time)
    raise CohesiveSDKException("Timeout while waiting for keyset.")
Esempio n. 2
0
def peer_mesh(
    clients,
    peer_address_map=None,
    address_type=VNS3Attr.primary_private_ip,
    delay_configure=False,
    mtu=None,
):
    """peer_mesh Create a peering mesh by adding each client as peer for other clients.
       The order of the list of clients is the assumed peering id, i.e. client at clients[0]
       has peering id of 1, clients[1] has peering id of 2. Each TLS connection between peers
       is then automatically negotiated.

    Arguments:
        clients {List[VNS3Client]}

    Keyword Arguments:
        peer_address_map {Dict} - Optional map for peering addresses {
            [from_peer_id: str]: {
                [to_peer_id_1: str]: [peer_address_1: str],
                [to_peer_id_2: str]: [peer_address_2: str],
                ...
            }
        }
        address_type {str} - which address to use. Options: primary_private_ip, secondary_private_ip, public_ip or public_dns
        delay_configure {bool} -- delay automatic negotiation of peer (default: False)
        mtu {int} -- Override MTU for the peering TLS connection. VNS3 defaults to 1500. (default: {None})

    Raises:
        CohesiveSDKException

    Returns:
        data_types.BulkOperationResult
    """
    # fetch peer ids and set on clients
    ensure_peer_ids_result = fetch_state_attribute(clients, VNS3Attr.peer_id)
    if api_ops.bulk_operation_failed(ensure_peer_ids_result):
        errors_str = api_ops.stringify_bulk_result_exception(
            ensure_peer_ids_result)
        Logger.error("Failed to fetch peering Ids for all clients",
                     errors=errors_str)
        raise CohesiveSDKException(
            "Failed to fetch peering Ids for all clients: %s" % errors_str)

    # constructu peer address mapping
    if peer_address_map is not None:
        Logger.debug("Using address map passed for peering mesh.")
        peer_id_to_client = {
            c.query_state(VNS3Attr.peer_id): c
            for c in clients
        }
        peer_address_mapping_tuples = [
            (peer_id_to_client[from_peer_id], to_peers_map)
            for from_peer_id, to_peers_map in peer_address_map.items()
        ]
    else:
        Logger.debug("Constructing peering mesh")
        peer_address_mapping_tuples = _construct_peer_address_mapping(
            clients, address_type)

    common_peer_kwargs = {}
    if delay_configure:
        common_peer_kwargs.update(force=False)
    if mtu:
        common_peer_kwargs.update(overlay_mtu=mtu)

    def create_all_peers_for_client(client, post_peer_kwargs):
        return [
            client.peering.post_create_peer(
                **dict(peering_request, **common_peer_kwargs))
            for peering_request in post_peer_kwargs
        ]

    run_peering_funcs = []
    # bind api function calls for peer creations
    for vns3_client, peer_mapping in peer_address_mapping_tuples:
        run_peering_funcs.append(
            bind(
                create_all_peers_for_client,
                vns3_client,
                [{
                    "id": peer_id,
                    "name": peer_address
                } for peer_id, peer_address in peer_mapping.items()],
            ))

    Logger.debug("Creating %d-way peering mesh." % len(clients))
    return api_ops.__bulk_call_api(run_peering_funcs, parallelize=True)