示例#1
0
    def __init__(self, *args, **kwargs):
        """
        Init: args, kwargs pass through
        Options come from 'config'
        """
        super(OpenvStorageEdgeBaseVD, self).__init__(*args, **kwargs)
        self.configuration.append_config_values(options)
        self.volume_backend_name = kwargs['host'].split('@')[1]
        self._ip = self.configuration.storage_ip
        self._port = self.configuration.edge_port
        self._protocol = self.configuration.edge_protocol

        # Client can be initialized at initialization time because client is internally re-established for each call
        contact = ClusterContact(self._ip, self.configuration.xml_rpc_port)
        self._sd_client = StorageRouterClient(self.configuration.vpool_guid,
                                              [contact])
    def __init__(self, *args, **kwargs):
        """
        Init: args, kwargs pass through
        Options come from 'config'
        """
        super(OpenvStorageEdgeBaseVD, self).__init__(*args, **kwargs)
        self.configuration.append_config_values(options)
        self.volume_backend_name = kwargs["host"].split("@")[1]
        self._ip = self.configuration.storage_ip
        self._port = self.configuration.edge_port
        self._protocol = self.configuration.edge_protocol

        # Client can be initialized at initialization time because client is internally re-established for each call
        contact = ClusterContact(self._ip, self.configuration.xml_rpc_port)
        self._sd_client = StorageRouterClient(self.configuration.vpool_guid, [contact])
class OpenvStorageEdgeBaseVD(driver.BaseVD):
    """
    Open vStorage Edge Volume Driver plugin for Cinder base functionality
    """

    VERSION = "0.0.1"
    OVS_FILE_NAME = "/{0}.raw"

    def __init__(self, *args, **kwargs):
        """
        Init: args, kwargs pass through
        Options come from 'config'
        """
        super(OpenvStorageEdgeBaseVD, self).__init__(*args, **kwargs)
        self.configuration.append_config_values(options)
        self.volume_backend_name = kwargs["host"].split("@")[1]
        self._ip = self.configuration.storage_ip
        self._port = self.configuration.edge_port
        self._protocol = self.configuration.edge_protocol

        # Client can be initialized at initialization time because client is internally re-established for each call
        contact = ClusterContact(self._ip, self.configuration.xml_rpc_port)
        self._sd_client = StorageRouterClient(self.configuration.vpool_guid, [contact])

    def create_volume(self, volume):
        """
        Creates a volume
        Called on "cinder create ..." or "nova volume-create ..."

        :param volume: Volume reference (sqlalchemy Model)
        :type volume: cinder.objects.volume.Volume
        :return: None
        """
        cleaned_name = OpenvStorageEdgeBaseVD._clean_display_name(name=volume.display_name)
        voldrv_path = OpenvStorageEdgeBaseVD.OVS_FILE_NAME.format(cleaned_name)
        # noinspection PyArgumentList
        if voldrv_path in self._sd_client.list_volumes_by_path():
            raise ValueError(
                "Volume already exists. Display name: {0}, cleaned name: {1}".format(volume.display_name, cleaned_name)
            )

        logger.debug("Creating volume. OpenStack name: {0}, OVS name: {1}".format(volume.display_name, cleaned_name))
        self._sd_client.create_volume(voldrv_path, None, "{0}B".format(volume.size * 1024 ** 3))

    def create_volume_from_snapshot(self, volume, snapshot):
        """
        Creates a volume from a snapshot.
        Called on "cinder create --snapshot-id ..."

        :param volume: Volume reference (sqlalchemy Model)
        :type volume: cinder.objects.volume.Volume
        :param snapshot: Snapshot reference (sqlalchemy Model)
        :type snapshot: cinder.objects.snapshot.Snapshot
        :return: None
        """
        # Gather information
        parent_volume_id = self._get_volume_id(volume=snapshot.volume)
        snapshots = self._sd_client.list_snapshots(parent_volume_id)
        cleaned_vol_name = OpenvStorageEdgeBaseVD._clean_display_name(name=volume.display_name)
        cleaned_snap_name = OpenvStorageEdgeBaseVD._clean_display_name(name=snapshot.display_name)
        voldrv_path = OpenvStorageEdgeBaseVD.OVS_FILE_NAME.format(cleaned_vol_name)

        # Validate information
        if cleaned_snap_name not in snapshots:
            raise ValueError(
                "Snapshot {0} does not belong to volume {1}".format(snapshot.display_name, volume.display_name)
            )
        # noinspection PyArgumentList
        if voldrv_path in self._sd_client.list_volumes_by_path():
            raise ValueError(
                "Volume already exists. Display name: {0}, cleaned name: {1}".format(
                    volume.display_name, cleaned_vol_name
                )
            )

        # Wait for specified snapshot to be synced to backend
        if len(snapshots) > 0:
            logger.debug("Verifying snapshot synced to backend")
            self._wait_for_snapshot(volume_id=parent_volume_id, snapshot_id=cleaned_snap_name)

        # Create volume from the snapshot
        logger.debug(
            "Creating volume from snapshot. OpenStack name: {0}, OVS name: {1}".format(
                volume.display_name, cleaned_vol_name
            )
        )
        self._sd_client.create_clone(voldrv_path, None, parent_volume_id, cleaned_snap_name)

    def delete_volume(self, volume):
        """
        Deletes a logical volume.
        Called on "cinder delete ... "

        :param volume: Volume reference (sqlalchemy Model)
        :type volume: cinder.objects.volume.Volume
        :return: None
        """
        volume_id = self._get_volume_id(volume=volume)
        snapshots = self._sd_client.list_snapshots(volume_id)
        if len(snapshots) > 0:
            raise RuntimeError("Volume {0} still has snapshots".format(volume_id))

        cleaned_name = OpenvStorageEdgeBaseVD._clean_display_name(name=volume.display_name)
        # noinspection PyArgumentList
        if OpenvStorageEdgeBaseVD.OVS_FILE_NAME.format(cleaned_name) in self._sd_client.list_volumes_by_path():
            logger.debug(
                "Deleting volume. OpenStack name: {0}, OVS name: {1}".format(volume.display_name, cleaned_name)
            )
            self._sd_client.unlink("/{0}.raw".format(cleaned_name))
        else:
            logger.debug(
                "Volume no longer exists in OVS. OpenStack name: {0}, supposedly OVS name: {1}".format(
                    volume.display_name, cleaned_name
                )
            )

    def create_snapshot(self, snapshot):
        """
        Creates a snapshot.
        Called on "nova image-create " or "cinder snapshot-create "

        :param snapshot: Snapshot reference (sqlalchemy Model)
        :type snapshot: cinder.objects.snapshot.Snapshot
        :return: None
        """
        # Gather information and validate
        volume_id = self._get_volume_id(volume=snapshot.volume)
        snapshots = self._sd_client.list_snapshots(volume_id)
        cleaned_snap_name = OpenvStorageEdgeBaseVD._clean_display_name(name=snapshot.display_name)
        if cleaned_snap_name in snapshots:
            raise ValueError(
                "Snapshot already exists. OpenStack name: {0}, OVS name: {1}".format(
                    snapshot.display_name, cleaned_snap_name
                )
            )

        # Wait for last snapshot to be synced to backend
        if len(snapshots) > 0:
            logger.debug("Verifying last snapshot synced to backend")
            self._wait_for_snapshot(volume_id=volume_id, snapshot_id=snapshots[-1])

        # Create the snapshot
        logger.debug(
            "Creating new snapshot. OpenStack name: {0}, OVS name: {1}".format(snapshot.display_name, cleaned_snap_name)
        )
        self._sd_client.create_snapshot(volume_id, str(cleaned_snap_name))

    def delete_snapshot(self, snapshot):
        """
        Deletes a snapshot.

        :param snapshot: Snapshot reference (sqlalchemy Model)
        :type snapshot: cinder.objects.snapshot.Snapshot
        :return: None
        """
        volume_id = self._get_volume_id(volume=snapshot.volume)
        cleaned_snap_name = OpenvStorageEdgeBaseVD._clean_display_name(name=snapshot.display_name)
        if cleaned_snap_name in self._sd_client.list_snapshots(volume_id):
            logger.debug(
                "Deleting snapshot. OpenStack name: {0}, OVS name: {1}".format(snapshot.display_name, cleaned_snap_name)
            )
            self._sd_client.delete_snapshot(volume_id, cleaned_snap_name)
        else:
            logger.debug(
                "Snapshot no longer exists in OVS. OpenStack name: {0}, supposedly OVS name: {1}".format(
                    snapshot.display_name, cleaned_snap_name
                )
            )

    def get_volume_stats(self, refresh=False):
        """
        Get volume stats.

        :param refresh: Refresh the stats (Not implemented)
        :type refresh: bool
        :return: Volume statistics
        :rtype: dict
        """
        _ = refresh
        return {
            "volume_backend_name": self.volume_backend_name,
            "vendor_name": "Open vStorage",
            "driver_version": OpenvStorageEdgeBaseVD.VERSION,
            "storage_protocol": "openvstorage_edge",
            "total_capacity_gb": "unknown",
            "free_capacity_gb": "unknown",
            "reserved_percentage": 0,
            "QoS_support": False,
        }

    def copy_image_to_volume(self, context, volume, image_service, image_id):
        """
        Fetch the image from image_service and write it to the volume.
        Called on "nova volume-create --image-id ..." or "cinder create --image-id"
        Downloads image from glance server into volume

        :param context: Context object
        :type context: cinder.context
        :param volume: Volume reference (sqlalchemy Model)
        :type volume: cinder.objects.volume.Volume
        :param image_service: Image service reference
        :type image_service: glance.cmd.replicator.ImageService
        :param image_id: ID of the image
        :type image_id: str
        :return: None
        """
        cleaned_name = OpenvStorageEdgeBaseVD._clean_display_name(name=volume.display_name)
        location = "openvstorage+{0}:{1}:{2}/{3}".format(self._protocol, self._ip, self._port, cleaned_name)
        image_path = os.path.join("/tmp", image_id)
        if not os.path.exists(image_path):
            logger.debug("Downloading image to temporary location {0}".format(image_path))
            image_utils.fetch_to_raw(
                context=context,
                image_service=image_service,
                image_id=image_id,
                dest=image_path,
                blocksize="1M",
                size=volume.size,
            )
        logger.debug("Creating volume from image at location: {0}".format(location))
        OpenvStorageEdgeBaseVD._execute_qemu_command(parameters=["convert", "-n", "-O", "raw", image_path, location])

    def extend_volume(self, volume, new_size):
        """
        Extend volume to the new size.
        """
        raise NotImplementedError('Method "extend_volume" not implemented')

    def copy_volume_to_image(self, context, volume, image_service, image_meta):
        """
        Copy the volume to the specified image.
        """
        raise NotImplementedError('Method "copy_volume_to_image" not implemented')

    # Prevent NotImplementedError being raised
    # Not actually implemented, these actions do not make sense for this driver
    def check_for_setup_error(self):
        """
        Check for setup errors
        """
        pass

    def do_setup(self, context):
        """
        Any initialization the volume driver does while starting.
        """
        pass

    def initialize_connection(self, volume, connector, initiator_data=None):
        """
        Allow connection to connector and return connection info.
        """
        return

    def create_export(self, context, volume, connector=None):
        """
        Exports the volume.
        """
        pass

    def remove_export(self, context, volume):
        """
        Removes an export for a volume.
        """
        return

    def ensure_export(self, context, volume):
        """
        Synchronously recreates an export for a volume.
        """
        pass

    def terminate_connection(self, volume, connector, **kwargs):
        """
        Disallow connection from connector.
        """
        return

    def attach_volume(self, context, volume, instance_uuid, host_name, mountpoint):
        """
        Callback for volume attached to instance or host.
        """
        pass

    def detach_volume(self, context, volume, attachment=None):
        """
        Callback for volume detached.
        """
        pass

    ##################
    # HELPER METHODS #
    ##################
    def _get_volume_id(self, volume):
        """
        Retrieve the volume ID used by the OpenvStorage StorageDriver
        :param volume: The OpenStack volume object
        :type volume: cinder.objects.volume.Volume
        :return: The volume ID known by the StorageDriver
        :rtype: str
        """
        cleaned_name = OpenvStorageEdgeBaseVD._clean_display_name(name=volume.display_name)
        ovs_path = OpenvStorageEdgeBaseVD.OVS_FILE_NAME.format(cleaned_name)
        volume_id = self._sd_client.get_volume_id(ovs_path)
        if volume_id is None:
            raise ValueError("Volume with path {0} could not be found in OVS".format(ovs_path))
        return volume_id

    def _wait_for_snapshot(self, volume_id, snapshot_id):
        """
        Waits for a given snapshot to be available in the backend

        :param volume_id: ID of the volume to check
        :type volume_id: str
        :param snapshot_id: ID of the snapshot to check
        :type snapshot_id: str
        :return: True if the snapshot has been synced to backend, false otherwise
        :rtype: bool
        """
        tries = 60
        while tries > 0:
            if self._sd_client.is_volume_synced_up_to_snapshot(volume_id, snapshot_id) is True:
                return True
            tries -= 1
            time_to_sleep = 60 - tries
            logger.debug(
                "Snapshot {0} for volume {1} not yet synced to backend, sleeping {2} seconds".format(
                    snapshot_id, volume_id, time_to_sleep
                )
            )
            time.sleep(time_to_sleep)
        if tries == 0:
            raise RuntimeError("Snapshot {0} did not become available on the backend in due time".format(snapshot_id))

    @staticmethod
    def _clean_display_name(name):
        """
        Change the name to be allowed to be used by OpenvStorage
        :param name: Name to clean
        :type name: str
        :return: Cleaned volume name
        :rtype: str
        """
        name = str(name.strip("/").replace(" ", "_").lower())
        while "//" in name:
            name = name.replace("//", "/")
        name = re.compile("[^/\w\-.]+").sub("", name)
        match = re.compile("([/\w\-.]+)\.[a-z]{3,4}$").search(name)
        if match is None:
            name = name.rstrip(".")
        else:
            name = match.groups()[0]
        return name

    @staticmethod
    def _execute_qemu_command(parameters):
        """
        Executes qemu-img command wrapper

        :param parameters: Parameters to pass on the command-line to the QEMU-IMG command
        :type parameters: list
        :return: Exit code of the command
        :rtype: int
        """
        command = ["env", "LC_ALL=C", "LANG=C", "qemu-img"] + parameters
        logger.debug("Executing command: {0}".format(command))
        try:
            channel = Popen(command, stdout=PIPE, stderr=PIPE)
        except OSError as ose:
            logger.error(ose)
            raise
        stdout, stderr = channel.communicate()
        exit_code = channel.returncode
        if exit_code != 0:
            raise Exception("Command {0} failed with error message {1}".format(command, stderr))

        logger.debug("Output of command: {0}".format(stdout.strip()))
        return stdout.strip()
示例#4
0
class OpenvStorageEdgeBaseVD(driver.BaseVD):
    """
    Open vStorage Edge Volume Driver plugin for Cinder base functionality
    """
    VERSION = '0.0.1'
    OVS_FILE_NAME = '/{0}.raw'

    def __init__(self, *args, **kwargs):
        """
        Init: args, kwargs pass through
        Options come from 'config'
        """
        super(OpenvStorageEdgeBaseVD, self).__init__(*args, **kwargs)
        self.configuration.append_config_values(options)
        self.volume_backend_name = kwargs['host'].split('@')[1]
        self._ip = self.configuration.storage_ip
        self._port = self.configuration.edge_port
        self._protocol = self.configuration.edge_protocol

        # Client can be initialized at initialization time because client is internally re-established for each call
        contact = ClusterContact(self._ip, self.configuration.xml_rpc_port)
        self._sd_client = StorageRouterClient(self.configuration.vpool_guid,
                                              [contact])

    def create_volume(self, volume):
        """
        Creates a volume
        Called on "cinder create ..." or "nova volume-create ..."

        :param volume: Volume reference (sqlalchemy Model)
        :type volume: cinder.objects.volume.Volume
        :return: None
        """
        cleaned_name = OpenvStorageEdgeBaseVD._clean_display_name(
            name=volume.display_name)
        voldrv_path = OpenvStorageEdgeBaseVD.OVS_FILE_NAME.format(cleaned_name)
        # noinspection PyArgumentList
        if voldrv_path in self._sd_client.list_volumes_by_path():
            raise ValueError(
                'Volume already exists. Display name: {0}, cleaned name: {1}'.
                format(volume.display_name, cleaned_name))

        logger.debug(
            'Creating volume. OpenStack name: {0}, OVS name: {1}'.format(
                volume.display_name, cleaned_name))
        self._sd_client.create_volume(voldrv_path, None,
                                      '{0}B'.format(volume.size * 1024**3))

    def create_volume_from_snapshot(self, volume, snapshot):
        """
        Creates a volume from a snapshot.
        Called on "cinder create --snapshot-id ..."

        :param volume: Volume reference (sqlalchemy Model)
        :type volume: cinder.objects.volume.Volume
        :param snapshot: Snapshot reference (sqlalchemy Model)
        :type snapshot: cinder.objects.snapshot.Snapshot
        :return: None
        """
        # Gather information
        parent_volume_id = self._get_volume_id(volume=snapshot.volume)
        snapshots = self._sd_client.list_snapshots(parent_volume_id)
        cleaned_vol_name = OpenvStorageEdgeBaseVD._clean_display_name(
            name=volume.display_name)
        cleaned_snap_name = OpenvStorageEdgeBaseVD._clean_display_name(
            name=snapshot.display_name)
        voldrv_path = OpenvStorageEdgeBaseVD.OVS_FILE_NAME.format(
            cleaned_vol_name)

        # Validate information
        if cleaned_snap_name not in snapshots:
            raise ValueError(
                'Snapshot {0} does not belong to volume {1}'.format(
                    snapshot.display_name, volume.display_name))
        # noinspection PyArgumentList
        if voldrv_path in self._sd_client.list_volumes_by_path():
            raise ValueError(
                'Volume already exists. Display name: {0}, cleaned name: {1}'.
                format(volume.display_name, cleaned_vol_name))

        # Wait for specified snapshot to be synced to backend
        if len(snapshots) > 0:
            logger.debug('Verifying snapshot synced to backend')
            self._wait_for_snapshot(volume_id=parent_volume_id,
                                    snapshot_id=cleaned_snap_name)

        # Create volume from the snapshot
        logger.debug(
            'Creating volume from snapshot. OpenStack name: {0}, OVS name: {1}'
            .format(volume.display_name, cleaned_vol_name))
        self._sd_client.create_clone(voldrv_path, None, parent_volume_id,
                                     cleaned_snap_name)

    def delete_volume(self, volume):
        """
        Deletes a logical volume.
        Called on "cinder delete ... "

        :param volume: Volume reference (sqlalchemy Model)
        :type volume: cinder.objects.volume.Volume
        :return: None
        """
        volume_id = self._get_volume_id(volume=volume)
        snapshots = self._sd_client.list_snapshots(volume_id)
        if len(snapshots) > 0:
            raise RuntimeError(
                'Volume {0} still has snapshots'.format(volume_id))

        cleaned_name = OpenvStorageEdgeBaseVD._clean_display_name(
            name=volume.display_name)
        # noinspection PyArgumentList
        if OpenvStorageEdgeBaseVD.OVS_FILE_NAME.format(
                cleaned_name) in self._sd_client.list_volumes_by_path():
            logger.debug(
                'Deleting volume. OpenStack name: {0}, OVS name: {1}'.format(
                    volume.display_name, cleaned_name))
            self._sd_client.unlink('/{0}.raw'.format(cleaned_name))
        else:
            logger.debug(
                'Volume no longer exists in OVS. OpenStack name: {0}, supposedly OVS name: {1}'
                .format(volume.display_name, cleaned_name))

    def create_snapshot(self, snapshot):
        """
        Creates a snapshot.
        Called on "nova image-create " or "cinder snapshot-create "

        :param snapshot: Snapshot reference (sqlalchemy Model)
        :type snapshot: cinder.objects.snapshot.Snapshot
        :return: None
        """
        # Gather information and validate
        volume_id = self._get_volume_id(volume=snapshot.volume)
        snapshots = self._sd_client.list_snapshots(volume_id)
        cleaned_snap_name = OpenvStorageEdgeBaseVD._clean_display_name(
            name=snapshot.display_name)
        if cleaned_snap_name in snapshots:
            raise ValueError(
                'Snapshot already exists. OpenStack name: {0}, OVS name: {1}'.
                format(snapshot.display_name, cleaned_snap_name))

        # Wait for last snapshot to be synced to backend
        if len(snapshots) > 0:
            logger.debug('Verifying last snapshot synced to backend')
            self._wait_for_snapshot(volume_id=volume_id,
                                    snapshot_id=snapshots[-1])

        # Create the snapshot
        logger.debug(
            'Creating new snapshot. OpenStack name: {0}, OVS name: {1}'.format(
                snapshot.display_name, cleaned_snap_name))
        self._sd_client.create_snapshot(volume_id, str(cleaned_snap_name))

    def delete_snapshot(self, snapshot):
        """
        Deletes a snapshot.

        :param snapshot: Snapshot reference (sqlalchemy Model)
        :type snapshot: cinder.objects.snapshot.Snapshot
        :return: None
        """
        volume_id = self._get_volume_id(volume=snapshot.volume)
        cleaned_snap_name = OpenvStorageEdgeBaseVD._clean_display_name(
            name=snapshot.display_name)
        if cleaned_snap_name in self._sd_client.list_snapshots(volume_id):
            logger.debug(
                'Deleting snapshot. OpenStack name: {0}, OVS name: {1}'.format(
                    snapshot.display_name, cleaned_snap_name))
            self._sd_client.delete_snapshot(volume_id, cleaned_snap_name)
        else:
            logger.debug(
                'Snapshot no longer exists in OVS. OpenStack name: {0}, supposedly OVS name: {1}'
                .format(snapshot.display_name, cleaned_snap_name))

    def get_volume_stats(self, refresh=False):
        """
        Get volume stats.

        :param refresh: Refresh the stats (Not implemented)
        :type refresh: bool
        :return: Volume statistics
        :rtype: dict
        """
        _ = refresh
        return {
            'volume_backend_name': self.volume_backend_name,
            'vendor_name': 'Open vStorage',
            'driver_version': OpenvStorageEdgeBaseVD.VERSION,
            'storage_protocol': 'openvstorage_edge',
            'total_capacity_gb': 'unknown',
            'free_capacity_gb': 'unknown',
            'reserved_percentage': 0,
            'QoS_support': False
        }

    def copy_image_to_volume(self, context, volume, image_service, image_id):
        """
        Fetch the image from image_service and write it to the volume.
        Called on "nova volume-create --image-id ..." or "cinder create --image-id"
        Downloads image from glance server into volume

        :param context: Context object
        :type context: cinder.context
        :param volume: Volume reference (sqlalchemy Model)
        :type volume: cinder.objects.volume.Volume
        :param image_service: Image service reference
        :type image_service: glance.cmd.replicator.ImageService
        :param image_id: ID of the image
        :type image_id: str
        :return: None
        """
        cleaned_name = OpenvStorageEdgeBaseVD._clean_display_name(
            name=volume.display_name)
        location = 'openvstorage+{0}:{1}:{2}/{3}'.format(
            self._protocol, self._ip, self._port, cleaned_name)
        image_path = os.path.join('/tmp', image_id)
        if not os.path.exists(image_path):
            logger.debug('Downloading image to temporary location {0}'.format(
                image_path))
            image_utils.fetch_to_raw(context=context,
                                     image_service=image_service,
                                     image_id=image_id,
                                     dest=image_path,
                                     blocksize='1M',
                                     size=volume.size)
        logger.debug(
            'Creating volume from image at location: {0}'.format(location))
        OpenvStorageEdgeBaseVD._execute_qemu_command(
            parameters=['convert', '-n', '-O', 'raw', image_path, location])

    def extend_volume(self, volume, new_size):
        """
        Extend volume to the new size.
        """
        raise NotImplementedError('Method "extend_volume" not implemented')

    def copy_volume_to_image(self, context, volume, image_service, image_meta):
        """
        Copy the volume to the specified image.
        """
        raise NotImplementedError(
            'Method "copy_volume_to_image" not implemented')

    # Prevent NotImplementedError being raised
    # Not actually implemented, these actions do not make sense for this driver
    def check_for_setup_error(self):
        """
        Check for setup errors
        """
        pass

    def do_setup(self, context):
        """
        Any initialization the volume driver does while starting.
        """
        pass

    def initialize_connection(self, volume, connector, initiator_data=None):
        """
        Allow connection to connector and return connection info.
        """
        return

    def create_export(self, context, volume, connector=None):
        """
        Exports the volume.
        """
        pass

    def remove_export(self, context, volume):
        """
        Removes an export for a volume.
        """
        return

    def ensure_export(self, context, volume):
        """
        Synchronously recreates an export for a volume.
        """
        pass

    def terminate_connection(self, volume, connector, **kwargs):
        """
        Disallow connection from connector.
        """
        return

    def attach_volume(self, context, volume, instance_uuid, host_name,
                      mountpoint):
        """
        Callback for volume attached to instance or host.
        """
        pass

    def detach_volume(self, context, volume, attachment=None):
        """
        Callback for volume detached.
        """
        pass

    ##################
    # HELPER METHODS #
    ##################
    def _get_volume_id(self, volume):
        """
        Retrieve the volume ID used by the OpenvStorage StorageDriver
        :param volume: The OpenStack volume object
        :type volume: cinder.objects.volume.Volume
        :return: The volume ID known by the StorageDriver
        :rtype: str
        """
        cleaned_name = OpenvStorageEdgeBaseVD._clean_display_name(
            name=volume.display_name)
        ovs_path = OpenvStorageEdgeBaseVD.OVS_FILE_NAME.format(cleaned_name)
        volume_id = self._sd_client.get_volume_id(ovs_path)
        if volume_id is None:
            raise ValueError(
                'Volume with path {0} could not be found in OVS'.format(
                    ovs_path))
        return volume_id

    def _wait_for_snapshot(self, volume_id, snapshot_id):
        """
        Waits for a given snapshot to be available in the backend

        :param volume_id: ID of the volume to check
        :type volume_id: str
        :param snapshot_id: ID of the snapshot to check
        :type snapshot_id: str
        :return: True if the snapshot has been synced to backend, false otherwise
        :rtype: bool
        """
        tries = 60
        while tries > 0:
            if self._sd_client.is_volume_synced_up_to_snapshot(
                    volume_id, snapshot_id) is True:
                return True
            tries -= 1
            time_to_sleep = 60 - tries
            logger.debug(
                'Snapshot {0} for volume {1} not yet synced to backend, sleeping {2} seconds'
                .format(snapshot_id, volume_id, time_to_sleep))
            time.sleep(time_to_sleep)
        if tries == 0:
            raise RuntimeError(
                'Snapshot {0} did not become available on the backend in due time'
                .format(snapshot_id))

    @staticmethod
    def _clean_display_name(name):
        """
        Change the name to be allowed to be used by OpenvStorage
        :param name: Name to clean
        :type name: str
        :return: Cleaned volume name
        :rtype: str
        """
        name = str(name.strip('/').replace(' ', '_').lower())
        while '//' in name:
            name = name.replace('//', '/')
        name = re.compile('[^/\w\-.]+').sub('', name)
        match = re.compile('([/\w\-.]+)\.[a-z]{3,4}$').search(name)
        if match is None:
            name = name.rstrip('.')
        else:
            name = match.groups()[0]
        return name

    @staticmethod
    def _execute_qemu_command(parameters):
        """
        Executes qemu-img command wrapper

        :param parameters: Parameters to pass on the command-line to the QEMU-IMG command
        :type parameters: list
        :return: Exit code of the command
        :rtype: int
        """
        command = ['env', 'LC_ALL=C', 'LANG=C', 'qemu-img'] + parameters
        logger.debug('Executing command: {0}'.format(command))
        try:
            channel = Popen(command, stdout=PIPE, stderr=PIPE)
        except OSError as ose:
            logger.error(ose)
            raise
        stdout, stderr = channel.communicate()
        exit_code = channel.returncode
        if exit_code != 0:
            raise Exception('Command {0} failed with error message {1}'.format(
                command, stderr))

        logger.debug('Output of command: {0}'.format(stdout.strip()))
        return stdout.strip()