def ControllerUnpublishVolume(self, request, context):
        set_current_thread_name(request.volume_id)
        logger.info("ControllerUnpublishVolume")
        try:
            try:
                utils.validate_unpublish_volume_request(request)
            except ValidationException as ex:
                logger.exception(ex)
                context.set_details(ex.message)
                context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
                return csi_pb2.ControllerUnpublishVolumeResponse()

            volume_id_info = utils.get_volume_id_info(request.volume_id)
            system_id = volume_id_info.system_id
            array_type = volume_id_info.array_type
            volume_id = volume_id_info.object_id
            node_id_info = NodeIdInfo(request.node_id)
            node_name = node_id_info.node_name
            initiators = node_id_info.initiators
            logger.debug(
                "node name for this unpublish operation is : {0}".format(
                    node_name))

            array_connection_info = utils.get_array_connection_info_from_secrets(
                request.secrets, system_id=system_id)

            with get_agent(array_connection_info,
                           array_type).get_mediator() as array_mediator:
                array_mediator.unmap_volume_by_initiators(
                    volume_id, initiators)

            logger.info("finished ControllerUnpublishVolume")
            return csi_pb2.ControllerUnpublishVolumeResponse()

        except array_errors.VolumeAlreadyUnmappedError:
            logger.debug("Idempotent case. volume is already unmapped.")
            return csi_pb2.ControllerUnpublishVolumeResponse()

        except array_errors.ObjectNotFoundError as ex:
            logger.debug("Idempotent case. volume is already deleted.")
            return csi_pb2.ControllerUnpublishVolumeResponse()

        except array_errors.PermissionDeniedError as ex:
            context.set_code(grpc.StatusCode.PERMISSION_DENIED)
            context.set_details(ex.message)
            return csi_pb2.ControllerUnpublishVolumeResponse()

        except array_errors.HostNotFoundError as ex:
            logger.exception(ex)
            context.set_details(ex.message)
            context.set_code(grpc.StatusCode.NOT_FOUND)
            return csi_pb2.ControllerUnpublishVolumeResponse()

        except Exception as ex:
            logger.debug("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.ControllerUnpublishVolumeResponse()
    def ControllerUnpublishVolume(self, request, context):
        set_current_thread_name(request.volume_id)
        logger.info("ControllerUnpublishVolume")
        try:
            try:
                utils.validate_unpublish_volume_request(request)
            except ValidationException as ex:
                logger.exception(ex)
                context.set_details(ex.message)
                context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
                return csi_pb2.ControllerUnpublishVolumeResponse()

            array_type, vol_id = utils.get_volume_id_info(request.volume_id)

            node_id_info = NodeIdInfo(request.node_id)
            node_name = node_id_info.node_name
            initiators = node_id_info.initiators
            logger.debug("node name for this unpublish operation is : {0}".format(node_name))

            user, password, array_addresses = utils.get_array_connection_info_from_secret(request.secrets)

            unmap_volume(user, password, array_addresses, array_type, vol_id, initiators)

            logger.info("finished ControllerUnpublishVolume")
            return csi_pb2.ControllerUnpublishVolumeResponse()

        except controller_errors.VolumeAlreadyUnmappedError as ex:
            logger.debug("Idempotent case. volume is already unmapped.")
            return csi_pb2.ControllerUnpublishVolumeResponse()

        except controller_errors.PermissionDeniedError as ex:
            context.set_code(grpc.StatusCode.PERMISSION_DENIED)
            context.set_details(ex)
            return csi_pb2.ControllerPublishVolumeResponse()

        except (controller_errors.HostNotFoundError, controller_errors.VolumeNotFoundError) as ex:
            logger.exception(ex)
            context.set_details(ex.message)
            context.set_code(grpc.StatusCode.NOT_FOUND)
            return csi_pb2.ControllerUnpublishVolumeResponse()

        except Exception as ex:
            logger.debug("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.ControllerUnpublishVolumeResponse()
    def ControllerUnpublishVolume(self, request, context):
        set_current_thread_name(request.volume_id)
        logger.info("ControllerUnpublishVolume")
        utils.validate_unpublish_volume_request(request)
        try:
            volume_id_info = utils.get_volume_id_info(request.volume_id)
            system_id = volume_id_info.system_id
            array_type = volume_id_info.array_type
            volume_id = volume_id_info.object_id
            node_id_info = NodeIdInfo(request.node_id)
            node_name = node_id_info.node_name
            initiators = node_id_info.initiators
            logger.debug(
                "node name for this unpublish operation is : {0}".format(
                    node_name))

            array_connection_info = utils.get_array_connection_info_from_secrets(
                request.secrets, system_id=system_id)

            with get_agent(array_connection_info,
                           array_type).get_mediator() as array_mediator:
                array_mediator.unmap_volume_by_initiators(
                    volume_id, initiators)

            logger.info("finished ControllerUnpublishVolume")
            return csi_pb2.ControllerUnpublishVolumeResponse()
        except ObjectIdError as ex:
            return handle_exception(ex, context,
                                    grpc.StatusCode.INVALID_ARGUMENT,
                                    array_errors.VolumeAlreadyUnmappedError)
        except array_errors.VolumeAlreadyUnmappedError:
            logger.debug("Idempotent case. volume is already unmapped.")
            return csi_pb2.ControllerUnpublishVolumeResponse()
        except array_errors.ObjectNotFoundError:
            logger.debug("Idempotent case. volume is already deleted.")
            return csi_pb2.ControllerUnpublishVolumeResponse()
    def test_validate_unpublish_volume_request(self, validate_secrets):
        request = Mock()
        request.volume_id = "somebadvolumename"

        with self.assertRaises(ValidationException) as ex:
            utils.validate_unpublish_volume_request(request)
            self.assertTrue("volume" in str(ex))

        request.volume_id = "xiv:volume"

        request.secrets = ["secrets"]
        validate_secrets.side_effect = [ValidationException("msg2")]
        with self.assertRaises(ValidationException) as ex:
            utils.validate_unpublish_volume_request(request)
            self.assertTrue("msg2" in str(ex))

        validate_secrets.side_effect = None

        utils.validate_unpublish_volume_request(request)

        request.volume_id = "xiv:u2:volume"

        utils.validate_unpublish_volume_request(request)