def _get_v35_plus_ovdc_url_data(method: str, tokens: list): """Parse tokens from url and http method to get v35 ovdc specific data. Returns a dictionary with operation and url data. :param shared_constants.RequestMethod method: http verb :param str[] tokens: http url :rtype: dict """ num_tokens = len(tokens) if num_tokens == 5: if method == shared_constants.RequestMethod.GET: return {_OPERATION_KEY: CseOperation.V35_OVDC_LIST} raise cse_exception.MethodNotAllowedRequestError() if num_tokens == 6: if method == shared_constants.RequestMethod.PUT: return { _OPERATION_KEY: CseOperation.V35_OVDC_UPDATE, shared_constants.RequestKey.OVDC_ID: tokens[5] } if method == shared_constants.RequestMethod.GET: return { _OPERATION_KEY: CseOperation.V35_OVDC_INFO, shared_constants.RequestKey.OVDC_ID: tokens[5] } raise cse_exception.MethodNotAllowedRequestError()
def _get_v35_plus_native_cluster_url_data(method: str, tokens: list): """Parse tokens from url and http method to get v35 native cluster data. Returns a dictionary with operation and url data. :param RequestMethod method: http verb :param str[] tokens: http url :rtype: dict """ num_tokens = len(tokens) if num_tokens == 5: if method == shared_constants.RequestMethod.GET: return {_OPERATION_KEY: CseOperation.V35_NATIVE_CLUSTER_LIST} raise cse_exception.MethodNotAllowedRequestError()
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 _get_legacy_url_data(method: str, url: str, api_version: str): tokens = url.split('/') num_tokens = len(tokens) operation_type = tokens[3].lower() if operation_type.endswith('s'): operation_type = operation_type[:-1] if operation_type == OperationType.NATIVE_CLUSTER: if api_version not in (VcdApiVersion.VERSION_33.value, VcdApiVersion.VERSION_35.value): raise cse_exception.NotFoundRequestError() if num_tokens == 4: if method == shared_constants.RequestMethod.GET: return {_OPERATION_KEY: CseOperation.NATIVE_CLUSTER_LIST} raise cse_exception.MethodNotAllowedRequestError() if operation_type == OperationType.CLUSTER: if api_version not in (VcdApiVersion.VERSION_33.value, VcdApiVersion.VERSION_34.value): raise cse_exception.NotFoundRequestError() if num_tokens == 4: if method == shared_constants.RequestMethod.GET: return {_OPERATION_KEY: CseOperation.CLUSTER_LIST} if method == shared_constants.RequestMethod.POST: return {_OPERATION_KEY: CseOperation.CLUSTER_CREATE} raise cse_exception.MethodNotAllowedRequestError() if num_tokens == 5: if method == shared_constants.RequestMethod.GET: return { _OPERATION_KEY: CseOperation.CLUSTER_INFO, shared_constants.RequestKey.CLUSTER_NAME: tokens[4] } if method == shared_constants.RequestMethod.PUT: return { _OPERATION_KEY: CseOperation.CLUSTER_RESIZE, shared_constants.RequestKey.CLUSTER_NAME: tokens[4] } if method == shared_constants.RequestMethod.DELETE: return { _OPERATION_KEY: CseOperation.CLUSTER_DELETE, shared_constants.RequestKey.CLUSTER_NAME: tokens[4] } raise cse_exception.MethodNotAllowedRequestError() if num_tokens == 6: if method == shared_constants.RequestMethod.GET: if tokens[5] == 'config': return { _OPERATION_KEY: CseOperation.CLUSTER_CONFIG, shared_constants.RequestKey.CLUSTER_NAME: tokens[4] } if tokens[5] == 'upgrade-plan': return { _OPERATION_KEY: CseOperation.CLUSTER_UPGRADE_PLAN, shared_constants.RequestKey.CLUSTER_NAME: tokens[4] } raise cse_exception.MethodNotAllowedRequestError() if num_tokens == 7: if method == shared_constants.RequestMethod.POST: if tokens[5] == 'action' and tokens[6] == 'upgrade': return { _OPERATION_KEY: CseOperation.CLUSTER_UPGRADE, shared_constants.RequestKey.CLUSTER_NAME: tokens[4] } raise cse_exception.MethodNotAllowedRequestError() elif operation_type == OperationType.NODE: if api_version not in (VcdApiVersion.VERSION_33.value, VcdApiVersion.VERSION_34.value): raise cse_exception.NotFoundRequestError() if num_tokens == 4: if method == shared_constants.RequestMethod.POST: return {_OPERATION_KEY: CseOperation.NODE_CREATE} if method == shared_constants.RequestMethod.DELETE: return {_OPERATION_KEY: CseOperation.NODE_DELETE} raise cse_exception.MethodNotAllowedRequestError() if num_tokens == 5: if method == shared_constants.RequestMethod.GET: return { _OPERATION_KEY: CseOperation.NODE_INFO, shared_constants.RequestKey.NODE_NAME: tokens[4] } raise cse_exception.MethodNotAllowedRequestError() elif operation_type == OperationType.OVDC: if api_version not in (VcdApiVersion.VERSION_33.value, VcdApiVersion.VERSION_34.value): raise cse_exception.NotFoundRequestError() if num_tokens == 4: if method == shared_constants.RequestMethod.GET: return {_OPERATION_KEY: CseOperation.OVDC_LIST} raise cse_exception.MethodNotAllowedRequestError() if num_tokens == 5: if method == shared_constants.RequestMethod.GET: return { _OPERATION_KEY: CseOperation.OVDC_INFO, shared_constants.RequestKey.OVDC_ID: tokens[4] } if method == shared_constants.RequestMethod.PUT: return { _OPERATION_KEY: CseOperation.OVDC_UPDATE, shared_constants.RequestKey.OVDC_ID: tokens[4] } raise cse_exception.MethodNotAllowedRequestError() if num_tokens == 6 and tokens[5] == 'compute-policies': if method == shared_constants.RequestMethod.GET: return { _OPERATION_KEY: CseOperation.OVDC_COMPUTE_POLICY_LIST, shared_constants.RequestKey.OVDC_ID: tokens[4] } if method == shared_constants.RequestMethod.PUT: return { _OPERATION_KEY: CseOperation.OVDC_COMPUTE_POLICY_UPDATE, shared_constants.RequestKey.OVDC_ID: tokens[4] } raise cse_exception.MethodNotAllowedRequestError() elif operation_type == OperationType.ORG_VDCS: if api_version not in (VcdApiVersion.VERSION_33.value, VcdApiVersion.VERSION_34.value): raise cse_exception.NotFoundRequestError() if num_tokens == 4: if method == shared_constants.RequestMethod.GET: return {_OPERATION_KEY: CseOperation.ORG_VDC_LIST} raise cse_exception.NotFoundRequestError() elif operation_type == OperationType.SYSTEM: if num_tokens == 4: if method == shared_constants.RequestMethod.GET: return {_OPERATION_KEY: CseOperation.SYSTEM_INFO} if method == shared_constants.RequestMethod.PUT: return {_OPERATION_KEY: CseOperation.SYSTEM_UPDATE} raise cse_exception.MethodNotAllowedRequestError() elif operation_type == OperationType.TEMPLATE: if num_tokens == 4: if method == shared_constants.RequestMethod.GET: return {_OPERATION_KEY: CseOperation.TEMPLATE_LIST} raise cse_exception.MethodNotAllowedRequestError() raise cse_exception.NotFoundRequestError()
def _get_v35_plus_cluster_url_data(method: str, tokens: list, api_version: str) -> dict: # noqa: E501 """Parse tokens from url and http method to get v35 cluster specific data. Returns a dictionary with operation and url data. :param RequestMethod method: http verb :param str[] tokens: http url :param str api_version: API version of the request :rtype: dict """ num_tokens = len(tokens) if num_tokens == 5: if method == shared_constants.RequestMethod.GET: return {_OPERATION_KEY: CseOperation.V35_CLUSTER_LIST} if method == shared_constants.RequestMethod.POST: return {_OPERATION_KEY: CseOperation.V35_CLUSTER_CREATE} raise cse_exception.MethodNotAllowedRequestError() if num_tokens == 6: if method == shared_constants.RequestMethod.GET: return { _OPERATION_KEY: CseOperation.V35_CLUSTER_INFO, shared_constants.RequestKey.CLUSTER_ID: tokens[5] } if method == shared_constants.RequestMethod.PUT: url_data = {shared_constants.RequestKey.CLUSTER_ID: tokens[5]} if api_version == VcdApiVersion.VERSION_35.value: url_data[_OPERATION_KEY] = CseOperation.V35_CLUSTER_RESIZE else: url_data[_OPERATION_KEY] = CseOperation.V36_CLUSTER_UPDATE return url_data if method == shared_constants.RequestMethod.DELETE: return { _OPERATION_KEY: CseOperation.V35_CLUSTER_DELETE, shared_constants.RequestKey.CLUSTER_ID: tokens[5] } raise cse_exception.MethodNotAllowedRequestError() if num_tokens == 7: if method == shared_constants.RequestMethod.GET: if tokens[6] == 'config': return { _OPERATION_KEY: CseOperation.V35_CLUSTER_CONFIG, shared_constants.RequestKey.CLUSTER_ID: tokens[5] } if tokens[6] == 'upgrade-plan': return { _OPERATION_KEY: CseOperation.V35_CLUSTER_UPGRADE_PLAN, # noqa: E501 shared_constants.RequestKey.CLUSTER_ID: tokens[5] } if tokens[6] == 'acl': return { _OPERATION_KEY: CseOperation.V35_CLUSTER_ACL_LIST, shared_constants.RequestKey.CLUSTER_ID: tokens[5] } if method == shared_constants.RequestMethod.PUT: if tokens[6] == 'acl': return { _OPERATION_KEY: CseOperation.V35_CLUSTER_ACL_UPDATE, shared_constants.RequestKey.CLUSTER_ID: tokens[5] } raise cse_exception.MethodNotAllowedRequestError() if num_tokens == 8: if method == shared_constants.RequestMethod.POST: if tokens[6] == 'action' and tokens[7] == 'upgrade': # TODO create a constant in pyvcloud. if api_version == '36.0': raise cse_exception.MethodNotAllowedRequestError() return { _OPERATION_KEY: CseOperation.V35_CLUSTER_UPGRADE, shared_constants.RequestKey.CLUSTER_ID: tokens[5] } if method == shared_constants.RequestMethod.DELETE: if tokens[6] == 'nfs': return { _OPERATION_KEY: CseOperation.V35_NODE_DELETE, shared_constants.RequestKey.CLUSTER_ID: tokens[5], shared_constants.RequestKey.NODE_NAME: tokens[7] } raise cse_exception.MethodNotAllowedRequestError()
def _get_pks_url_data(method: str, url: str): """Parse url and http method to get desired PKS operation and url data. Returns a data dictionary with 'operation' key and also any relevant url data. :param shared_constants.RequestMethod method: :param str url: :rtype: dict """ tokens = url.split('/') num_tokens = len(tokens) operation_type = tokens[3].lower() if operation_type.endswith('s'): operation_type = operation_type[:-1] if operation_type == OperationType.CLUSTER: if num_tokens == 4: if method == shared_constants.RequestMethod.GET: return {_OPERATION_KEY: CseOperation.PKS_CLUSTER_LIST} if method == shared_constants.RequestMethod.POST: return {_OPERATION_KEY: CseOperation.PKS_CLUSTER_CREATE} raise cse_exception.MethodNotAllowedRequestError() if num_tokens == 5: if method == shared_constants.RequestMethod.GET: return { _OPERATION_KEY: CseOperation.PKS_CLUSTER_INFO, shared_constants.RequestKey.CLUSTER_NAME: tokens[4] } if method == shared_constants.RequestMethod.PUT: return { _OPERATION_KEY: CseOperation.PKS_CLUSTER_RESIZE, shared_constants.RequestKey.CLUSTER_NAME: tokens[4] } if method == shared_constants.RequestMethod.DELETE: return { _OPERATION_KEY: CseOperation.PKS_CLUSTER_DELETE, shared_constants.RequestKey.CLUSTER_NAME: tokens[4] } raise cse_exception.MethodNotAllowedRequestError() if num_tokens == 6: if method == shared_constants.RequestMethod.GET: if tokens[5] == 'config': return { _OPERATION_KEY: CseOperation.PKS_CLUSTER_CONFIG, shared_constants.RequestKey.CLUSTER_NAME: tokens[4] } raise cse_exception.MethodNotAllowedRequestError() elif operation_type == OperationType.OVDC: if num_tokens == 4: if method == shared_constants.RequestMethod.GET: return {_OPERATION_KEY: CseOperation.PKS_OVDC_LIST} raise cse_exception.MethodNotAllowedRequestError() if num_tokens == 5: if method == shared_constants.RequestMethod.GET: return { _OPERATION_KEY: CseOperation.PKS_OVDC_INFO, shared_constants.RequestKey.OVDC_ID: tokens[4] } if method == shared_constants.RequestMethod.PUT: return { _OPERATION_KEY: CseOperation.PKS_OVDC_UPDATE, shared_constants.RequestKey.OVDC_ID: tokens[4] } raise cse_exception.MethodNotAllowedRequestError() elif operation_type == OperationType.ORG_VDCS: if num_tokens == 4: if method == shared_constants.RequestMethod.GET: return {_OPERATION_KEY: CseOperation.PKS_ORG_VDC_LIST} raise cse_exception.MethodNotAllowedRequestError() raise cse_exception.NotFoundRequestError()