def _handle_existing_vol_src_snap(self, volume, src_snapshot_id, context):
     """
     Args:
         volume             : volume fetched or created in CreateVolume
         src_snapshot_id    : id of snapshot we should copy to vol or None if volume should not be copied
         context            : CreateVolume response context
     Returns:
         If volume exists and is a copy of specified snapshot - 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.
     """
     vol_name = volume.volume_name
     vol_copy_src_object_id = volume.copy_src_object_id
     if not src_snapshot_id or not vol_copy_src_object_id:
         return None
     if vol_copy_src_object_id == src_snapshot_id:
         logger.debug(
             "Volume {0} exists and it is a copy of snapshot {1}.".format(
                 vol_name, src_snapshot_id))
         context.set_code(grpc.StatusCode.OK)
         return csi_pb2.CreateVolumeResponse()
     else:
         logger.debug(
             "Volume {0} exists but it is not a copy of snapshot {1}.".
             format(vol_name, src_snapshot_id))
         context.set_details(
             "Volume already exists but it was created from a different source."
         )
         context.set_code(grpc.StatusCode.INTERNAL)
         return csi_pb2.CreateVolumeResponse()
Пример #2
0
def generate_csi_create_volume_response(new_volume, system_id=None, source_type=None):
    logger.debug("creating volume response for volume : {0}".format(new_volume))

    volume_context = {"volume_name": new_volume.name,
                      "array_address": ",".join(
                          new_volume.array_address if isinstance(new_volume.array_address, list) else [
                              new_volume.array_address]),
                      "pool_name": new_volume.pool,
                      "storage_type": new_volume.array_type
                      }
    content_source = None
    if new_volume.copy_source_id:
        if source_type == config.SNAPSHOT_TYPE_NAME:
            snapshot_source = csi_pb2.VolumeContentSource.SnapshotSource(snapshot_id=new_volume.copy_source_id)
            content_source = csi_pb2.VolumeContentSource(snapshot=snapshot_source)
        else:
            volume_source = csi_pb2.VolumeContentSource.VolumeSource(volume_id=new_volume.copy_source_id)
            content_source = csi_pb2.VolumeContentSource(volume=volume_source)

    res = csi_pb2.CreateVolumeResponse(volume=csi_pb2.Volume(
        capacity_bytes=new_volume.capacity_bytes,
        volume_id=get_volume_id(new_volume, system_id),
        content_source=content_source,
        volume_context=volume_context))

    logger.debug("finished creating volume response : {0}".format(res))
    return res
Пример #3
0
def generate_csi_create_volume_response(new_vol):
    logger.debug("creating volume response for vol : {0}".format(new_vol))

    vol_context = {
        "volume_name":
        new_vol.volume_name,
        "array_address":
        ",".join(new_vol.array_address if isinstance(
            new_vol.array_address, list) else [new_vol.array_address]),
        "pool_name":
        new_vol.pool_name,
        "storage_type":
        new_vol.array_type
    }
    content_source = None
    if new_vol.copy_src_object_id:
        snapshot_source = csi_pb2.VolumeContentSource.SnapshotSource(
            snapshot_id=new_vol.copy_src_object_id)
        content_source = csi_pb2.VolumeContentSource(snapshot=snapshot_source)

    res = csi_pb2.CreateVolumeResponse(
        volume=csi_pb2.Volume(capacity_bytes=new_vol.capacity_bytes,
                              volume_id=get_vol_id(new_vol),
                              content_source=content_source,
                              volume_context=vol_context))

    logger.debug("finished creating volume response : {0}".format(res))
    return res
Пример #4
0
def generate_csi_create_volume_response(new_volume,
                                        system_id=None,
                                        source_type=None):
    logger.debug(
        "creating create volume response for volume : {0}".format(new_volume))

    volume_context = _get_context_from_volume(new_volume)

    content_source = None
    if new_volume.copy_source_id:
        if source_type == config.SNAPSHOT_TYPE_NAME:
            snapshot_source = csi_pb2.VolumeContentSource.SnapshotSource(
                snapshot_id=new_volume.copy_source_id)
            content_source = csi_pb2.VolumeContentSource(
                snapshot=snapshot_source)
        else:
            volume_source = csi_pb2.VolumeContentSource.VolumeSource(
                volume_id=new_volume.copy_source_id)
            content_source = csi_pb2.VolumeContentSource(volume=volume_source)

    res = csi_pb2.CreateVolumeResponse(
        volume=csi_pb2.Volume(capacity_bytes=new_volume.capacity_bytes,
                              volume_id=get_volume_id(new_volume, system_id),
                              content_source=content_source,
                              volume_context=volume_context))

    logger.debug("finished creating volume response : {0}".format(res))
    return res
Пример #5
0
 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 test_create_volume_with_empty_name(self, a_enter, a_exit):
     a_enter.return_value = self.mediator
     self.request.name = ""
     context = utils.FakeContext()
     res = self.servicer.CreateVolume(self.request, context)
     self.assertEqual(context.code, grpc.StatusCode.INVALID_ARGUMENT)
     self.assertTrue("name" in context.details)
     self.assertEqual(res, csi_pb2.CreateVolumeResponse())
 def _handle_volume_exists_with_different_source(self, context, source_id,
                                                 source_type, volume_name):
     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()
Пример #8
0
def generate_csi_create_volume_response(new_vol):
    logger.debug("creating volume response for vol : {0}".format(new_vol))

    vol_context = {"volume_name": new_vol.volume_name,
                   "array_address": ",".join(new_vol.array_address if isinstance(new_vol.array_address, list) else [new_vol.array_address]),
                   "pool_name": new_vol.pool_name,
                   "storage_type": new_vol.array_type
                   }

    res = csi_pb2.CreateVolumeResponse(volume=csi_pb2.Volume(
        capacity_bytes=new_vol.capacity_bytes,
        volume_id=get_vol_id(new_vol),
        volume_context=vol_context))

    logger.debug("finished creating volume response : {0}".format(res))
    return res
    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()
    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:
            logger.exception(ex)
            context.set_details(ex.message)
            context.set_code(grpc.StatusCode.NOT_FOUND)
            return 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)
                    logger.error(message)
                    context.set_details(message)
                    context.set_code(grpc.StatusCode.OUT_OF_RANGE)
                    return 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:
                        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_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, pool)
                    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:
            handle_exception(ex, context, grpc.StatusCode.INVALID_ARGUMENT,
                             csi_pb2.CreateVolumeResponse)
        except array_errors.VolumeAlreadyExists as ex:
            handle_exception(ex, context, grpc.StatusCode.ALREADY_EXISTS,
                             csi_pb2.CreateVolumeResponse)