def _handle_existing_volume_source(self, volume, source_id, source_type, context): """ Args: volume : volume fetched or created in CreateVolume source_id : id of object we should copy to vol or None if volume should not be copied source_type: : the object type of the source - volume or snapshot context : CreateVolume response context Returns: If volume exists and is a copy of specified object - set context status to OK and return CreateVolumeResponse. If volume is a copy of another source - set context status to INTERNAL and return CreateVolumeResponse. In any other case return None. """ volume_name = volume.name volume_copy_source_id = volume.copy_source_id if not source_id and not volume_copy_source_id: return None if volume_copy_source_id == source_id: logger.debug( "Volume {0} exists and it is a copy of {1} {2}.".format( volume_name, source_type, source_id)) context.set_code(grpc.StatusCode.OK) return utils.generate_csi_create_volume_response( volume, source_type) else: logger.debug( "Volume {0} exists but it is not a copy of {1} {2}.".format( volume_name, source_type, source_id)) context.set_details( "Volume already exists but it was created from a different source." ) context.set_code(grpc.StatusCode.ALREADY_EXISTS) return csi_pb2.CreateVolumeResponse()
def _handle_volume_exists_with_same_source(self, context, source_id, source_type, volume, system_id): logger.debug("Volume {0} exists and it is a copy of {1} {2}.".format( volume.name, source_type, source_id)) context.set_code(grpc.StatusCode.OK) return utils.generate_csi_create_volume_response( volume, system_id, source_type)
def test_get_create_volume_response(self, get_vol_id): new_vol = Mock() new_vol.volume_name = "name" new_vol.array_address = ["fqdn1", "fqdn2"] new_vol.pool_name = "pool" new_vol.array_type = "a9k" new_vol.capacity_bytes = 10 get_vol_id.return_value = "a9k:name" res = utils.generate_csi_create_volume_response(new_vol) self.assertEqual(10, res.volume.capacity_bytes) get_vol_id.side_effect = [Exception("err")] with self.assertRaises(Exception): utils.generate_csi_create_volume_response(new_vol)
def test_get_create_volume_response_with_single_IP(self, get_vol_id): new_vol = Mock() new_vol.volume_name = "name" new_vol.array_address = "9.1.1.1" new_vol.pool_name = "pool" new_vol.array_type = "svc" new_vol.capacity_bytes = 10 get_vol_id.return_value = "svc:name" res = utils.generate_csi_create_volume_response(new_vol) self.assertEqual(10, res.volume.capacity_bytes) self.assertEqual("9.1.1.1", res.volume.volume_context['array_address'])
def test_get_create_volume_response_with_Multiple_IP(self, get_volume_id): new_volume = Mock() new_volume.name = "name" new_volume.array_address = ["9.1.1.1", "9.1.1.2"] new_volume.pool = pool new_volume.array_type = "svc" new_volume.capacity_bytes = 10 new_volume.copy_source_id = None get_volume_id.return_value = "svc:name" res = utils.generate_csi_create_volume_response(new_volume) self.assertEqual(10, res.volume.capacity_bytes) self.assertEqual("9.1.1.1,9.1.1.2", res.volume.volume_context['array_address'])
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 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 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()