def CreateVolume(self, request, context): set_current_thread_name(request.name) logger.info("create volume") try: utils.validate_create_volume_request(request) except ObjectIdError as ex: return handle_exception(ex, context, grpc.StatusCode.NOT_FOUND, csi_pb2.CreateVolumeResponse) logger.debug("volume name : {}".format(request.name)) source_type, source_id = self._get_source_type_and_id(request) logger.debug("Source {0} id : {1}".format(source_type, source_id)) topologies = utils.get_volume_topologies(request) secrets = request.secrets try: array_connection_info = utils.get_array_connection_info_from_secrets( secrets=secrets, topologies=topologies) volume_parameters = utils.get_volume_parameters( parameters=request.parameters, system_id=array_connection_info.system_id) pool = volume_parameters.pool if not pool: raise ValidationException( controller_messages.pool_should_not_be_empty_message) space_efficiency = volume_parameters.space_efficiency # TODO : pass multiple array addresses array_type = detect_array_type( array_connection_info.array_addresses) with get_agent(array_connection_info, array_type).get_mediator() as array_mediator: logger.debug(array_mediator) volume_final_name = self._get_volume_final_name( volume_parameters, request.name, array_mediator) required_bytes = request.capacity_range.required_bytes max_size = array_mediator.maximal_volume_size_in_bytes min_size = array_mediator.minimal_volume_size_in_bytes if required_bytes > max_size: message = messages.SizeOutOfRangeError_message.format( required_bytes, max_size) return build_error_response(message, context, grpc.StatusCode.OUT_OF_RANGE, csi_pb2.CreateVolumeResponse) if required_bytes == 0: required_bytes = min_size logger.debug( "requested size is 0 so the default size will be used : {0} " .format(required_bytes)) try: volume = array_mediator.get_volume( volume_final_name, pool=pool, ) except array_errors.ObjectNotFoundError: logger.debug( "volume was not found. creating a new volume with parameters: {0}" .format(request.parameters)) array_mediator.validate_supported_space_efficiency( space_efficiency) volume = array_mediator.create_volume( volume_final_name, required_bytes, space_efficiency, pool) else: logger.debug("volume found : {}".format(volume)) if not source_id and volume.capacity_bytes != request.capacity_range.required_bytes: message = "Volume was already created with different size." return build_error_response( message, context, grpc.StatusCode.ALREADY_EXISTS, csi_pb2.CreateVolumeResponse) copy_source_res = self._handle_existing_volume_source( volume, source_id, source_type, array_connection_info.system_id, context) if copy_source_res: return copy_source_res if source_id: self._copy_to_existing_volume_from_source( volume, source_id, source_type, required_bytes, array_mediator) volume.copy_source_id = source_id res = utils.generate_csi_create_volume_response( volume, array_connection_info.system_id, source_type) logger.info("finished create volume") return res except array_errors.InvalidArgumentError as ex: return handle_exception(ex, context, grpc.StatusCode.INVALID_ARGUMENT, csi_pb2.CreateVolumeResponse) except array_errors.VolumeAlreadyExists as ex: return handle_exception(ex, context, grpc.StatusCode.ALREADY_EXISTS, csi_pb2.CreateVolumeResponse)
def test_validate_create_volume_request(self, valiate_capabilities, validate_secret): request = Mock() request.name = "" with self.assertRaises(ValidationException) as ex: utils.validate_create_volume_request(request) self.assertTrue("name" in ex.message) request.name = "name" request.capacity_range.required_bytes = -1 with self.assertRaises(ValidationException) as ex: utils.validate_create_volume_request(request) self.assertTrue("size" in ex.message) request.capacity_range.required_bytes = 10 valiate_capabilities.side_effect = ValidationException("msg") with self.assertRaises(ValidationException) as ex: utils.validate_create_volume_request(request) self.assertTrue("msg" in ex.message) valiate_capabilities.side_effect = None validate_secret.side_effect = ValidationException(" other msg") with self.assertRaises(ValidationException) as ex: utils.validate_create_volume_request(request) self.assertTrue("other msg" in ex.message) validate_secret.side_effect = None request.parameters = {"capabilities": ""} with self.assertRaises(ValidationException) as ex: utils.validate_create_volume_request(request) self.assertTrue("parameters" in ex.message) request.parameters = {} with self.assertRaises(ValidationException) as ex: utils.validate_create_volume_request(request) self.assertTrue("parameters" in ex.message) request.parameters = None with self.assertRaises(ValidationException) as ex: utils.validate_create_volume_request(request) self.assertTrue("parameters" in ex.message) request.parameters = {"pool": "pool1", "SpaceEfficiency": "thin "} request.volume_content_source = None utils.validate_create_volume_request(request) request.parameters = {"pool": "pool1"} utils.validate_create_volume_request(request) request.capacity_range.required_bytes = 0 utils.validate_create_volume_request(request)
def CreateVolume(self, request, context): # pylint: disable=too-many-branches set_current_thread_name(request.name) logger.info("create volume") try: utils.validate_create_volume_request(request) except ValidationException as ex: logger.error("failed request validation") logger.exception(ex) context.set_details(ex.message) context.set_code(grpc.StatusCode.INVALID_ARGUMENT) return csi_pb2.CreateVolumeResponse() except ObjectIdError as ex: logger.exception(ex) context.set_details(ex.message) context.set_code(grpc.StatusCode.NOT_FOUND) return csi_pb2.CreateVolumeResponse() volume_name = request.name logger.debug("volume name : {}".format(volume_name)) src_snapshot_id = self._get_src_snapshot_id(request) logger.debug("Source snapshot id : {}".format(src_snapshot_id)) secrets = request.secrets user, password, array_addresses = utils.get_array_connection_info_from_secret( secrets) pool = request.parameters[config.PARAMETERS_POOL] capabilities = { key: value for key, value in request.parameters.items() if key in [ config.PARAMETERS_CAPABILITIES_SPACEEFFICIENCY, ] } try: # TODO : pass multiple array addresses array_type = detect_array_type(array_addresses) with get_agent(user, password, array_addresses, array_type).get_mediator() as array_mediator: logger.debug(array_mediator) # TODO: CSI-1358 - remove try/except try: volume_full_name, volume_prefix = self._get_volume_name_and_prefix( request, array_mediator) except controller_errors.IllegalObjectName as ex: context.set_details(ex.message) context.set_code(grpc.StatusCode.INVALID_ARGUMENT) return csi_pb2.CreateSnapshotResponse() size = request.capacity_range.required_bytes if size == 0: size = array_mediator.minimal_volume_size_in_bytes logger.debug( "requested size is 0 so the default size will be used : {0} " .format(size)) try: vol = array_mediator.get_volume( volume_full_name, volume_context=request.parameters, volume_prefix=volume_prefix, ) except controller_errors.VolumeNotFoundError: logger.debug( "volume was not found. creating a new volume with parameters: {0}" .format(request.parameters)) array_mediator.validate_supported_capabilities( capabilities) vol = array_mediator.create_volume(volume_full_name, size, capabilities, pool, volume_prefix) else: logger.debug("volume found : {}".format(vol)) if not src_snapshot_id and vol.capacity_bytes != request.capacity_range.required_bytes: context.set_details( "Volume was already created with different size.") context.set_code(grpc.StatusCode.ALREADY_EXISTS) return csi_pb2.CreateVolumeResponse() copy_source_res = self._handle_existing_vol_src_snap( vol, src_snapshot_id, context) if copy_source_res: return copy_source_res if src_snapshot_id: self._copy_to_existing_volume_from_snapshot( vol, src_snapshot_id, size, array_mediator, pool) vol.copy_src_object_id = src_snapshot_id logger.debug("generating create volume response") res = utils.generate_csi_create_volume_response(vol) logger.info("finished create volume") return res except (controller_errors.IllegalObjectName, controller_errors.StorageClassCapabilityNotSupported, controller_errors.PoolDoesNotExist, controller_errors.PoolDoesNotMatchCapabilities) as ex: logger.exception(ex) context.set_details(ex.message) context.set_code(grpc.StatusCode.INVALID_ARGUMENT) return csi_pb2.CreateVolumeResponse() except controller_errors.PermissionDeniedError as ex: context.set_code(grpc.StatusCode.PERMISSION_DENIED) context.set_details(ex) return csi_pb2.CreateVolumeResponse() except controller_errors.VolumeAlreadyExists as ex: context.set_details(ex.message) context.set_code(grpc.StatusCode.ALREADY_EXISTS) return csi_pb2.CreateVolumeResponse() except Exception as ex: logger.error("an internal exception occurred") logger.exception(ex) context.set_code(grpc.StatusCode.INTERNAL) context.set_details( 'an internal exception occurred : {}'.format(ex)) return csi_pb2.CreateVolumeResponse()
def CreateVolume(self, request, context): set_current_thread_name(request.name) logger.info("create volume") try: utils.validate_create_volume_request(request) except ValidationException as ex: logger.error("failed request validation") logger.exception(ex) context.set_details(ex.message) context.set_code(grpc.StatusCode.INVALID_ARGUMENT) return csi_pb2.CreateVolumeResponse() volume_name = request.name logger.debug("volume name : {}".format(volume_name)) if config.PARAMETERS_PREFIX in request.parameters: volume_prefix = request.parameters[config.PARAMETERS_PREFIX] volume_full_name = volume_prefix + settings.NAME_PREFIX_SEPARATOR + volume_name else: volume_prefix = "" volume_full_name = volume_name secrets = request.secrets user, password, array_addresses = utils.get_array_connection_info_from_secret( secrets) pool = request.parameters[config.PARAMETERS_POOL] capabilities = { key: value for key, value in request.parameters.items() if key in [ config.PARAMETERS_CAPABILITIES_SPACEEFFICIENCY, ] } try: # TODO : pass multiple array addresses with ArrayConnectionManager(user, password, array_addresses) as array_mediator: logger.debug(array_mediator) if len(volume_prefix ) > array_mediator.max_volume_prefix_length: raise controller_errors.IllegalObjectName( "The volume name prefix {} is too long, max allowed length is {}" .format( volume_prefix, array_mediator.max_volume_prefix_length, )) if len(volume_full_name) > array_mediator.max_vol_name_length: raise controller_errors.IllegalObjectName( "The volume name {} is too long, max allowed length is {}" .format( volume_full_name, array_mediator.max_vol_name_length, )) size = request.capacity_range.required_bytes if size == 0: size = array_mediator.minimal_volume_size_in_bytes logger.debug( "requested size is 0 so the default size will be used : {0} " .format(size)) try: vol = array_mediator.get_volume( volume_full_name, volume_context=request.parameters, volume_prefix=volume_prefix, ) except controller_errors.VolumeNotFoundError as ex: logger.debug( "volume was not found. creating a new volume with parameters: {0}" .format(request.parameters)) array_mediator.validate_supported_capabilities( capabilities) vol = array_mediator.create_volume(volume_full_name, size, capabilities, pool, volume_prefix) else: logger.debug("volume found : {}".format(vol)) if not (vol.capacity_bytes == request.capacity_range.required_bytes): context.set_details( "Volume was already created with different size.") context.set_code(grpc.StatusCode.ALREADY_EXISTS) return csi_pb2.CreateVolumeResponse() logger.debug("generating create volume response") res = utils.generate_csi_create_volume_response(vol) logger.info("finished create volume") return res except (controller_errors.IllegalObjectName, controller_errors.StorageClassCapabilityNotSupported, controller_errors.PoolDoesNotExist, controller_errors.PoolDoesNotMatchCapabilities) as ex: context.set_details(ex.message) context.set_code(grpc.StatusCode.INVALID_ARGUMENT) return csi_pb2.CreateVolumeResponse() except controller_errors.PermissionDeniedError as ex: context.set_code(grpc.StatusCode.PERMISSION_DENIED) context.set_details(ex) return csi_pb2.CreateVolumeResponse() except controller_errors.VolumeAlreadyExists as ex: context.set_details(ex.message) context.set_code(grpc.StatusCode.ALREADY_EXISTS) return csi_pb2.CreateVolumeResponse() except Exception as ex: logger.error("an internal exception occurred") logger.exception(ex) context.set_code(grpc.StatusCode.INTERNAL) context.set_details( 'an internal exception occurred : {}'.format(ex)) return csi_pb2.CreateVolumeResponse()
def test_validate_create_volume_request(self, valiate_capabilities, validate_secrets): request = Mock() request.name = "" with self.assertRaises(ValidationException) as ex: utils.validate_create_volume_request(request) self.assertTrue("name" in str(ex)) request.name = "name" request.capacity_range.required_bytes = -1 with self.assertRaises(ValidationException) as ex: utils.validate_create_volume_request(request) self.assertTrue("size" in str(ex)) request.capacity_range.required_bytes = 10 valiate_capabilities.side_effect = ValidationException("msg") with self.assertRaises(ValidationException) as ex: utils.validate_create_volume_request(request) self.assertTrue("msg" in str(ex)) valiate_capabilities.side_effect = None validate_secrets.side_effect = ValidationException(" other msg") with self.assertRaises(ValidationException) as ex: utils.validate_create_volume_request(request) self.assertTrue("other msg" in str(ex)) validate_secrets.side_effect = None request.parameters = {"capabilities": ""} with self.assertRaises(ValidationException) as ex: utils.validate_create_volume_request(request) self.assertTrue("parameters" in str(ex)) request.parameters = {} with self.assertRaises(ValidationException) as ex: utils.validate_create_volume_request(request) self.assertTrue("parameters" in str(ex)) request.parameters = None with self.assertRaises(ValidationException) as ex: utils.validate_create_volume_request(request) self.assertTrue("parameters" in str(ex)) request.parameters = {config.PARAMETERS_POOL: pool, config.PARAMETERS_SPACE_EFFICIENCY: "thin "} request.volume_content_source = None utils.validate_create_volume_request(request) request.parameters = {config.PARAMETERS_POOL: pool} utils.validate_create_volume_request(request) request.capacity_range.required_bytes = 0 utils.validate_create_volume_request(request)