Пример #1
0
def raise_error_if_unsupported_payload_version(payload_version: str):
    input_rde_version = def_constants.MAP_INPUT_PAYLOAD_VERSION_TO_RDE_VERSION.get(
        payload_version, def_constants.PayloadKey.UNKNOWN)  # noqa: E501
    runtime_rde_version = server_utils.get_rde_version_in_use()
    if input_rde_version == def_constants.PayloadKey.UNKNOWN or semantic_version.Version(
            input_rde_version) > semantic_version.Version(
                runtime_rde_version):  # noqa: E501
        raise exceptions.BadRequestError(
            f"Unsupported payload version: {payload_version}")  # noqa: E501
Пример #2
0
 def exception_handler_wrapper(*args, **kwargs):
     try:
         result = func(*args, **kwargs)
     except (KeyError, TypeError, ValueError) as error:
         LOGGER.error(error)
         raise cse_exception.BadRequestError(error_message=str(error))
     except Exception as error:
         LOGGER.error(error)
         raise error
     return result
Пример #3
0
    def update_status(self, server_action: shared_constants.ServerAction):
        def graceful_shutdown():
            message = 'Shutting down CSE'
            n = self.active_requests_count()
            if n > 0:
                message += f" CSE will finish processing {n} requests."
            self._state = ServerState.STOPPING
            return message

        if self._state == ServerState.RUNNING:
            if server_action == shared_constants.ServerAction.ENABLE:
                return 'CSE is already enabled and running.'
            if server_action == shared_constants.ServerAction.DISABLE:
                self._state = ServerState.DISABLED
                return 'CSE has been disabled.'
            if server_action == shared_constants.ServerAction.STOP:
                raise cse_exception.BadRequestError(
                    error_message='CSE must be disabled before '
                    'it can be stopped.')
            raise cse_exception.BadRequestError(
                error_message=f"Invalid server action: '{server_action}'")
        if self._state == ServerState.DISABLED:
            if server_action == shared_constants.ServerAction.ENABLE:
                self._state = ServerState.RUNNING
                return 'CSE has been enabled and is running.'
            if server_action == shared_constants.ServerAction.DISABLE:
                return 'CSE is already disabled.'
            if server_action == shared_constants.ServerAction.STOP:
                return graceful_shutdown()
        if self._state == ServerState.STOPPING:
            if server_action == shared_constants.ServerAction.ENABLE:
                raise cse_exception.BadRequestError(
                    error_message='Cannot enable CSE while it is being'
                    'stopped.')
            if server_action == shared_constants.ServerAction.DISABLE:
                raise cse_exception.BadRequestError(
                    error_message='Cannot disable CSE while it is being'
                    ' stopped.')
            if server_action == shared_constants.ServerAction.STOP:
                return graceful_shutdown()

        raise cse_exception.CseServerError(
            f"Invalid server state: '{self._state}'")  # noqa: E501
Пример #4
0
def _raise_error_if_unsupported_payload_version(payload_version: str):
    input_rde_version = rde_constants.MAP_INPUT_PAYLOAD_VERSION_TO_RDE_VERSION.get(
        payload_version,
        rde_constants.RDEVersion.RDE_1_0_0.value)  # noqa: E501
    supported_rde_version = rde_utils.get_rde_version_introduced_at_api_version(
        _SUPPORTED_API_VERSION)  # noqa: E501
    if semantic_version.Version(input_rde_version) > semantic_version.Version(
            supported_rde_version):  # noqa: E501
        raise exceptions.BadRequestError(
            f"Unsupported payload version: {payload_version}")  # noqa: E501
Пример #5
0
 def exception_handler_wrapper(*args, **kwargs):
     try:
         result = func(*args, **kwargs)
     except (KeyError, TypeError, ValueError) as error:
         LOGGER.error(error)
         raise cse_exception.BadRequestError(error_message=str(error))
     except Exception as error:
         LOGGER.error(error)
         if not isinstance(error, cse_exception.CseRequestError):
             raise cse_exception.InternalServerRequestError(
                 error_message=str(error))  # noqa: E501
         raise error
     return result
def ovdc_compute_policy_update(request_data, op_ctx: ctx.OperationContext):
    """Request handler for ovdc compute-policy update operation.

    Required data: ovdc_id, compute_policy_action, compute_policy_names

    :return: Dictionary with task href.
    """
    required = [
        RequestKey.OVDC_ID, RequestKey.COMPUTE_POLICY_ACTION,
        RequestKey.COMPUTE_POLICY_NAME
    ]
    defaults = {
        RequestKey.REMOVE_COMPUTE_POLICY_FROM_VMS: False,
    }
    validated_data = {**defaults, **request_data}
    req_utils.validate_payload(validated_data, required)

    action = validated_data[RequestKey.COMPUTE_POLICY_ACTION]
    cp_name = validated_data[RequestKey.COMPUTE_POLICY_NAME]
    ovdc_id = validated_data[RequestKey.OVDC_ID]
    remove_compute_policy_from_vms = validated_data[
        RequestKey.REMOVE_COMPUTE_POLICY_FROM_VMS]  # noqa: E501
    try:
        config = server_utils.get_server_runtime_config()
        cpm = compute_policy_manager.ComputePolicyManager(
            op_ctx.sysadmin_client,
            log_wire=utils.str_to_bool(
                config['service'].get('log_wire')))  # noqa: E501
        cp_href = None
        cp_id = None
        if cp_name == SYSTEM_DEFAULT_COMPUTE_POLICY_NAME:
            for _cp in cpm.list_compute_policies_on_vdc(ovdc_id):
                if _cp['name'] == cp_name:
                    cp_href = _cp['href']
                    cp_id = _cp['id']
        else:
            try:
                _cp = compute_policy_manager.get_cse_vdc_compute_policy(
                    cpm, cp_name)  # noqa: E501
                cp_href = _cp['href']
                cp_id = _cp['id']
            except vcd_e.EntityNotFoundException:
                pass

        if cp_href is None:
            raise e.BadRequestError(f"Compute policy '{cp_name}' not found.")

        if action == ComputePolicyAction.ADD:
            cpm.add_compute_policy_to_vdc(ovdc_id, cp_href)
            # Record telemetry data
            record_user_action(CseOperation.OVDC_COMPUTE_POLICY_ADD)
            return f"Added compute policy '{cp_name}' ({cp_id}) to ovdc " \
                   f"({ovdc_id})"

        if action == ComputePolicyAction.REMOVE:
            # TODO: fix remove_compute_policy by implementing a proper way
            # for calling async methods without having to pass op_ctx
            # outside handlers.
            op_ctx.is_async = True
            response = cpm.remove_vdc_compute_policy_from_vdc(
                ovdc_id, cp_href, force=remove_compute_policy_from_vms)
            # Follow task_href to completion in a different thread and end
            # operation context
            _follow_task(op_ctx, response['task_href'], ovdc_id)
            # Record telemetry data
            record_user_action(CseOperation.OVDC_COMPUTE_POLICY_REMOVE)
            return response

        raise e.BadRequestError("Unsupported compute policy action")

    except Exception as err:
        # Record telemetry data failure`
        if action == ComputePolicyAction.ADD:
            record_user_action(CseOperation.OVDC_COMPUTE_POLICY_ADD,
                               status=OperationStatus.FAILED)
        elif action == ComputePolicyAction.REMOVE:
            record_user_action(CseOperation.OVDC_COMPUTE_POLICY_REMOVE,
                               status=OperationStatus.FAILED)
        raise err
Пример #7
0
def process_request(message):
    """
    Determine the correct api handler to invoke and invoke it.

    The request URI, api version and HTTP verb are used to determine the
    request operation and the corresponding handler.

    Additionally support for payload verification, query param verification
    will be added in a later point of time.

    URL template matching is also performed to compute values of url template
    parameters. These computed values, request body and query params are all
    sent to handlers in form of a dictionary.

    :param dict message: message received over AMQP/MQTT bus representing the
        incoming REST request.

    :returns: response computed by the handler after processing the request
    """
    LOGGER.debug(f"Incoming request message: {json.dumps(message)}")

    api_version_header = _parse_accept_header(
        accept_header=message['headers'].get('Accept'))
    api_version = _get_api_version_from_accept_header(
        api_version_header=api_version_header)

    # Convert to upper case for matching the ENUM values
    method = RequestMethod(message['method'].upper())

    url = message['requestUri']
    url_tokens = url.split("/")
    # ignore the vcd host and /api in the url
    if len(url_tokens) > 2:
        url_tokens = url_tokens[2:]

    query_params = None
    if message['queryString']:
        query_params = dict(parse_qsl(message['queryString']))

    request_body = None
    # Should we do a content-type check? and allow only application/json content?  # noqa: E501
    # Process request body only for requests with HTTP verbs that allow body
    if method in [RequestMethod.POST,
                  RequestMethod.PUT,
                  RequestMethod.DELETE] and \
            len(message['body']) > 0:
        raw_body = base64.b64decode(message['body']).decode(
            sys.getfilesystemencoding())  # noqa: E501
        request_body = json.loads(raw_body)

    import container_service_extension.server.service as cse_service
    server_config = cse_service.Service().get_service_config()

    found = False
    operation = None
    handler_method = None
    url_data = {}
    for entry in CSE_REQUEST_DISPATCHER_LIST:
        if found:
            break
        if len(entry['url_tokens']) != len(url_tokens):
            continue

        url_matched = True
        for i in range(0, len(url_tokens)):
            token = entry['url_tokens'][i]
            if token.startswith("$"):
                url_data[token[1:]] = url_tokens[i]
            elif token.lower() != url_tokens[i].lower():
                url_matched = False
                url_data.clear()
                break

        if not url_matched:
            continue

        if method not in entry.keys():
            raise cse_exception.MethodNotAllowedRequestError()

        handlers = entry[method]
        matched_handler = None
        supported_api_versions = []
        for versions in handlers.keys():
            supported_api_versions.extend(list(versions))
            if api_version in versions or '*' in versions:
                matched_handler = handlers[versions]
                break

        if not matched_handler:
            raise cse_exception.NotAcceptableRequestError(
                error_message="Invalid api version specified. Expected "
                f"api version '{supported_api_versions}'.")

        required_feature_flags = matched_handler.get('feature_flags', {})
        feature_flags_satisfied = True
        for feature_flag in required_feature_flags:
            value = server_config['feature_flags'].get(feature_flag, False)
            if not value:
                LOGGER.debug("Url matched but failed to satisfy feature "
                             f"flag {feature_flag}")
                feature_flags_satisfied = False
                break

        if feature_flags_satisfied:
            operation = matched_handler['operation']
            handler_method = matched_handler['handler']

            # ToDo: Extra validation based on allowed query params, content type etc.  # noqa: E501
            found = True
        else:
            break

    if not found:
        raise cse_exception.NotFoundRequestError()

    # /system operations are excluded from these checks
    if operation not in (CseOperation.SYSTEM_INFO, CseOperation.SYSTEM_UPDATE):
        if not cse_service.Service().is_running():
            raise cse_exception.BadRequestError(
                error_message='CSE service is disabled. '
                'Contact the System Administrator.')

    # create request data dict from incoming message data
    request_data = {}

    # update request data dict with query params data
    if query_params:
        request_data[RequestKey.QUERY_PARAMS] = query_params
        LOGGER.debug(f"query parameters: {query_params}")

    # update request_data with request_body
    if request_body:
        request_data[RequestKey.INPUT_SPEC] = request_body
        LOGGER.debug(f"request body: {request_body}")

    # update request_data with url template param key-values
    request_data.update(url_data)
    request_data['url'] = url

    # extract out the authorization token
    tenant_auth_token = message['headers'].get('x-vcloud-authorization')
    is_jwt_token = False
    auth_header = message['headers'].get('Authorization')
    if auth_header:
        tokens = auth_header.split(" ")
        if len(tokens) == 2 and tokens[0].lower() == 'bearer':
            tenant_auth_token = tokens[1]
            is_jwt_token = True

    # create operation context
    operation_ctx = ctx.OperationContext(tenant_auth_token,
                                         is_jwt=is_jwt_token,
                                         request_id=message['id'])

    try:
        body_content = handler_method(request_data, operation_ctx)
    finally:
        if not operation_ctx.is_async:
            operation_ctx.end()

    if not isinstance(body_content, (list, dict)):
        body_content = {RESPONSE_MESSAGE_KEY: str(body_content)}
    response = {
        'status_code': operation.ideal_response_code,
        'body': body_content,
    }
    LOGGER.debug(f"Outgoing response: {str(response)}")
    return response
def process_request(message):
    from container_service_extension.server.service import Service
    LOGGER.debug(f"Incoming request message: {json.dumps(message)}")

    api_version_header = _parse_accept_header(
        accept_header=message['headers'].get('Accept'))
    api_version = _get_api_version_from_accept_header(
        api_version_header=api_version_header)
    url_data = _get_url_data(method=message['method'],
                             url=message['requestUri'],
                             api_version=api_version)  # noqa: E501
    operation = url_data[_OPERATION_KEY]

    # Check api version and if server is disabled or not
    # /system operations are excluded from these checks
    if operation not in (CseOperation.SYSTEM_INFO, CseOperation.SYSTEM_UPDATE):
        if not Service().is_running():
            raise cse_exception.BadRequestError(
                error_message='CSE service is disabled. '
                'Contact the System Administrator.')
        else:
            server_api_version = server_utils.get_server_api_version()
            if api_version != server_api_version:
                raise cse_exception.NotAcceptableRequestError(
                    error_message="Invalid api version specified. Expected "
                    f"api version '{server_api_version}'.")

    # create request data dict from incoming message data
    request_data = {}
    is_cse_3_0_request = _is_cse_3_0_endpoint(message['requestUri'])
    if len(message['body']) > 0:
        raw_body = base64.b64decode(message['body']).decode(
            sys.getfilesystemencoding())  # noqa: E501
        request_body = json.loads(raw_body)
        if is_cse_3_0_request:
            request_data[shared_constants.RequestKey.INPUT_SPEC] = request_body
        else:
            request_data.update(request_body)
        LOGGER.debug(f"request body: {request_data}")
    # update request data dict with query params data
    if message['queryString']:
        query_params = dict(parse_qsl(message['queryString']))
        if is_cse_3_0_request:
            request_data[shared_constants.RequestKey.V35_QUERY] = query_params
        else:
            request_data.update(query_params)
        LOGGER.debug(f"query parameters: {query_params}")
    # update request spec with operation specific data in the url
    request_data.update(url_data)
    # remove None values from request payload
    data = {k: v for k, v in request_data.items() if v is not None}
    # extract out the authorization token
    tenant_auth_token = message['headers'].get('x-vcloud-authorization')
    is_jwt_token = False
    auth_header = message['headers'].get('Authorization')
    if auth_header:
        tokens = auth_header.split(" ")
        if len(tokens) == 2 and tokens[0].lower() == 'bearer':
            tenant_auth_token = tokens[1]
            is_jwt_token = True

    # create operation context
    operation_ctx = ctx.OperationContext(tenant_auth_token,
                                         is_jwt=is_jwt_token,
                                         request_id=message['id'])

    try:
        body_content = OPERATION_TO_HANDLER[operation](data, operation_ctx)
    finally:
        if not operation_ctx.is_async:
            operation_ctx.end()

    if not isinstance(body_content, (list, dict)):
        body_content = \
            {shared_constants.RESPONSE_MESSAGE_KEY: str(body_content)}
    response = {
        'status_code': operation.ideal_response_code,
        'body': body_content,
    }
    LOGGER.debug(f"Outgoing response: {str(response)}")
    return response
def raise_error_if_tkgm_cluster_operation(cluster_kind: str):
    if cluster_kind and cluster_kind == shared_constants.ClusterEntityKind.TKG_M.value:  # noqa: E501
        raise exceptions.BadRequestError(
            f"This operation is not supported for {cluster_kind} clusters"
        )  # noqa: E501