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
def create_cluster(behavior_ctx: BehaviorRequestContext): entity_id: str = behavior_ctx.entity_id entity: dict = behavior_ctx.entity cloudapi_client: CloudApiClient = behavior_ctx.op_ctx.cloudapi_client api_version: float = behavior_ctx.api_version # Validate the input rde_version_introduced_at_api_version = rde_utils.get_rde_version_introduced_at_api_version( api_version) # noqa: E501 rde_validator_factory.get_validator( rde_version=rde_version_introduced_at_api_version). \ validate(cloudapi_client=cloudapi_client, entity=entity) # TODO Based on the rde_in_use, convert the entity if necessary. # Call the backend to initiate the cluster creation. rde_in_use = server_utils.get_rde_version_in_use() svc = cluster_service_factory.ClusterServiceFactory( behavior_ctx).get_cluster_service(rde_in_use) # noqa: E501 NativeEntityClass: AbstractNativeEntity = rde_factory.get_rde_model( rde_in_use) # noqa: E501 input_entity: AbstractNativeEntity = NativeEntityClass(**entity) return svc.create_cluster(entity_id=entity_id, input_native_entity=input_entity) # noqa: E501
def validate(self, cloudapi_client: CloudApiClient, entity_id: str = None, entity: dict = None, operation: BehaviorOperation = None) -> bool: # noqa: E501 """Validate the input request. This method performs 1. Basic validation of the entity by simply casting the input entity dict to the model class dictated by the api_version specified in the request. This is usually performed for the "create" operation. 2. Operation (create, update, delete) specific validation. - create: "entity" is the only required parameter. - update: both "entity" and "entity_id" are required parameters. - delete: "entity_id" is the only required parameter. - kubeconfig: "entity_id" is the only required parameter. :param cloudapi_client: cloud api client :param dict entity: dict form of the native entity to be validated :param entity_id: entity id to be validated :param BehaviorOperation operation: CSE operation key :return: is validation successful or failure :rtype: bool """ if not entity_id and not entity: raise ValueError( 'Either entity_id or entity is required to validate.' ) # noqa: E501 entity_svc = DefEntityService(cloudapi_client=cloudapi_client) api_version: str = cloudapi_client.get_api_version() rde_version_introduced_at_api_version: str = rde_utils.get_rde_version_introduced_at_api_version( api_version) # noqa: E501 # TODO Reject the request if payload_version does not match with # either rde_in_use (or) rde_version_introduced_at_api_version # Cast the entity to the model class based on the user-specified # api_version. This can be considered as a basic request validation. # Any operation specific validation is handled further down NativeEntityClass: AbstractNativeEntity = rde_factory. \ get_rde_model(rde_version_introduced_at_api_version) input_entity = None if entity: try: input_entity: AbstractNativeEntity = NativeEntityClass.from_dict( entity) # noqa: E501 except Exception as err: msg = f"Failed to parse request body: {err}" raise BadRequestError(msg) # Return True if the operation is not specified. if not operation: return True # TODO: validators for rest of the CSE operations in V36 will be # implemented as and when v36/def_cluster_handler.py get other handler # functions if operation == BehaviorOperation.UPDATE_CLUSTER: if not entity_id or not entity: raise ValueError( 'Both entity_id and entity are required to validate the Update operation.' ) # noqa: E501 current_entity: AbstractNativeEntity = entity_svc.get_entity( entity_id).entity # noqa: E501 input_entity_spec: rde_2_0_0.ClusterSpec = input_entity.spec current_entity_status: rde_2_0_0.Status = current_entity.status current_entity_spec = \ rde_utils.construct_cluster_spec_from_entity_status( current_entity_status, rde_constants.RDEVersion.RDE_2_0_0.value) # noqa: E501 return validate_cluster_update_request_and_check_cluster_upgrade( input_entity_spec, current_entity_spec) # TODO check the reason why there was an unreachable raise statement raise NotImplementedError(f"Validator for {operation.name} not found")
def validate( self, cloudapi_client: CloudApiClient, sysadmin_client: Client, entity_id: str = None, entity: dict = None, operation: BehaviorOperation = BehaviorOperation.CREATE_CLUSTER, **kwargs) -> bool: """Validate the input request. This method performs 1. Basic validation of the entity by simply casting the input entity dict to the model class dictated by the api_version specified in the request. This is usually performed for the "create" operation. 2. Operation (create, update, delete) specific validation. - create: "entity" is the only required parameter. - update: both "entity" and "entity_id" are required parameters. - delete: "entity_id" is the only required parameter. - kubeconfig: "entity_id" is the only required parameter. :param cloudapi_client: cloud api client :param sysadmin_client: :param dict entity: dict form of the native entity to be validated :param entity_id: entity id to be validated :param BehaviorOperation operation: CSE operation key :return: is validation successful or failure :rtype: bool """ is_tkgm_cluster = kwargs.get('is_tkgm_cluster', False) if not entity_id and not entity: raise ValueError( 'Either entity_id or entity is required to validate.' ) # noqa: E501 entity_svc = DefEntityService(cloudapi_client=cloudapi_client) api_version: str = cloudapi_client.get_api_version() rde_version_introduced_at_api_version: str = rde_utils.get_rde_version_introduced_at_api_version( api_version) # noqa: E501 # TODO Reject the request if payload_version does not match with # either rde_in_use (or) rde_version_introduced_at_api_version # Cast the entity to the model class based on the user-specified # api_version. This can be considered as a basic request validation. # Any operation specific validation is handled further down native_entity_class: AbstractNativeEntity = rde_factory. \ get_rde_model(rde_version_introduced_at_api_version) input_entity = None if entity: try: input_entity: AbstractNativeEntity = native_entity_class.from_dict( entity) # noqa: E501 except Exception as err: msg = f"Failed to parse request body: {err}" raise BadRequestError(msg) # Need to ensure that sizing class along with cpu/memory is not # present in the request if isinstance(input_entity, rde_2_1_0.NativeEntity): # cpu and mem are properties of only rde 2.0.0 bad_request_msg = "" if input_entity.spec.topology.workers.sizing_class and \ (input_entity.spec.topology.workers.cpu or input_entity.spec.topology.workers.memory): # noqa: E501 bad_request_msg = "Cannot specify both sizing class and cpu/memory for Workers nodes." # noqa: E501 if input_entity.spec.topology.control_plane.sizing_class and ( input_entity.spec.topology.control_plane.cpu or input_entity.spec.topology.control_plane.memory ): # noqa: E501 bad_request_msg = "Cannot specify both sizing class and cpu/memory for Control Plane nodes." # noqa: E501 if bad_request_msg: raise BadRequestError(bad_request_msg) # Return True if the operation is not specified. if operation == BehaviorOperation.CREATE_CLUSTER: return True # TODO: validators for rest of the CSE operations in V36 will be # implemented as and when v36/def_cluster_handler.py get other handler # functions if operation == BehaviorOperation.UPDATE_CLUSTER: if not entity_id or not entity: raise ValueError( 'Both entity_id and entity are required to validate the Update operation.' ) # noqa: E501 current_entity: AbstractNativeEntity = entity_svc.get_entity( entity_id).entity # noqa: E501 input_entity_spec: rde_2_1_0.ClusterSpec = input_entity.spec current_entity_status: rde_2_1_0.Status = current_entity.status is_tkgm_with_default_sizing_in_control_plane = False is_tkgm_with_default_sizing_in_workers = False if is_tkgm_cluster: # NOTE: Since for TKGm cluster, if cluster is created without # a sizing class, default sizing class is assigned by VCD, # If we find the default sizing policy in the status section, # validate cpu/memory and sizing policy. # Also note that at this point in code, we are sure that only # one of sizing class or cpu/mem will be associated with # control plane and workers. vdc: VDC = get_vdc( sysadmin_client, vdc_name=current_entity_status.cloud_properties. virtual_data_center_name, # noqa: E501 org_name=current_entity_status.cloud_properties.org_name) vdc_resource = vdc.get_resource_admin() default_cp_name = vdc_resource.DefaultComputePolicy.get('name') control_plane_sizing_class = current_entity_status.nodes.control_plane.sizing_class # noqa: E501 is_tkgm_with_default_sizing_in_control_plane = \ (control_plane_sizing_class == default_cp_name) is_tkgm_with_default_sizing_in_workers = \ (len(current_entity_status.nodes.workers) > 0 and current_entity_status.nodes.workers[0].sizing_class == default_cp_name) # noqa: E501 current_entity_spec = \ rde_utils.construct_cluster_spec_from_entity_status( current_entity_status, rde_constants.RDEVersion.RDE_2_1_0.value, is_tkgm_with_default_sizing_in_control_plane=is_tkgm_with_default_sizing_in_control_plane, # noqa: E501 is_tkgm_with_default_sizing_in_workers=is_tkgm_with_default_sizing_in_workers) # noqa: E501 return validate_cluster_update_request_and_check_cluster_upgrade( input_entity_spec, current_entity_spec, is_tkgm_cluster) # TODO check the reason why there was an unreachable raise statement raise NotImplementedError(f"Validator for {operation.name} not found")