def _volume_snapshot_delete(self, context, instance, volume_id, snapshot_id, delete_info=None):
    "Note:\n            if file being merged into == active image:\n                do a blockRebase (pull) operation\n            else:\n                do a blockCommit operation\n            Files must be adjacent in snap chain.\n\n        :param instance: instance object reference\n        :param volume_id: volume UUID\n        :param snapshot_id: snapshot UUID (unused currently)\n        :param delete_info: {\n            'type':              'qcow2',\n            'file_to_merge':     'a.img',\n            'merge_target_file': 'b.img' or None (if merging file_to_merge into\n                                                  active image)\n          }\n\n\n        Libvirt blockjob handling required for this method is broken\n        in versions of libvirt that do not contain:\n        http://libvirt.org/git/?p=libvirt.git;h=0f9e67bfad (1.1.1)\n        (Patch is pending in 1.0.5-maint branch as well, but we cannot detect\n        libvirt 1.0.5.5 vs. 1.0.5.6 here.)\n        "
    if (not self._has_min_version(MIN_LIBVIRT_BLOCKJOBINFO_VERSION)):
        ver = '.'.join([str(x) for x in MIN_LIBVIRT_BLOCKJOBINFO_VERSION])
        msg = (_("Libvirt '%s' or later is required for online deletion of volume snapshots.") % ver)
        raise exception.Invalid(msg)
    LOG.debug('volume_snapshot_delete: delete_info: %s', delete_info)
    if (delete_info['type'] != 'qcow2'):
        msg = (_('Unknown delete_info type %s') % delete_info['type'])
        raise exception.NovaException(msg)
    try:
        virt_dom = self._lookup_by_name(instance.name)
    except exception.InstanceNotFound:
        raise exception.InstanceNotRunning(instance_id=instance.uuid)
    my_dev = None
    active_disk = None
    xml = virt_dom.XMLDesc(0)
    xml_doc = etree.fromstring(xml)
    device_info = vconfig.LibvirtConfigGuest()
    device_info.parse_dom(xml_doc)
    active_disk_object = None
    for guest_disk in device_info.devices:
        if (guest_disk.root_name != 'disk'):
            continue
        if ((guest_disk.target_dev is None) or (guest_disk.serial is None)):
            continue
        if (guest_disk.serial == volume_id):
            my_dev = guest_disk.target_dev
            active_disk = guest_disk.source_path
            active_protocol = guest_disk.source_protocol
            active_disk_object = guest_disk
            break
    if ((my_dev is None) or ((active_disk is None) and (active_protocol is None))):
        msg = (_('Disk with id: %s not found attached to instance.') % volume_id)
        LOG.debug('Domain XML: %s', xml)
        raise exception.NovaException(msg)
    LOG.debug('found device at %s', my_dev)

    def _get_snap_dev(filename, backing_store):
        if (filename is None):
            msg = _('filename cannot be None')
            raise exception.NovaException(msg)
        LOG.debug(('XML: %s' % xml))
        LOG.debug(('active disk object: %s' % active_disk_object))
        filename_to_merge = filename
        matched_name = None
        b = backing_store
        index = None
        current_filename = active_disk_object.source_name.split('/')[1]
        if (current_filename == filename_to_merge):
            return (my_dev + '[0]')
        while (b is not None):
            source_filename = b.source_name.split('/')[1]
            if (source_filename == filename_to_merge):
                LOG.debug(('found match: %s' % b.source_name))
                matched_name = b.source_name
                index = b.index
                break
            b = b.backing_store
        if (matched_name is None):
            msg = (_('no match found for %s') % filename_to_merge)
            raise exception.NovaException(msg)
        LOG.debug(('index of match (%s) is %s' % (b.source_name, index)))
        my_snap_dev = ('%s[%s]' % (my_dev, index))
        return my_snap_dev
    if (delete_info['merge_target_file'] is None):
        rebase_disk = my_dev
        rebase_flags = 0
        rebase_base = delete_info['file_to_merge']
        if (active_protocol is not None):
            rebase_base = _get_snap_dev(delete_info['file_to_merge'], active_disk_object.backing_store)
        rebase_bw = 0
        LOG.debug('disk: %(disk)s, base: %(base)s, bw: %(bw)s, flags: %(flags)s', {'disk': rebase_disk, 'base': rebase_base, 'bw': rebase_bw, 'flags': rebase_flags})
        result = virt_dom.blockRebase(rebase_disk, rebase_base, rebase_bw, rebase_flags)
        if (result == 0):
            LOG.debug('blockRebase started successfully')
        while self._wait_for_block_job(virt_dom, my_dev, abort_on_error=True):
            LOG.debug('waiting for blockRebase job completion')
            time.sleep(0.5)
    else:
        my_snap_base = None
        my_snap_top = None
        commit_disk = my_dev
        commit_flags = 0
        if (active_protocol is not None):
            my_snap_base = _get_snap_dev(delete_info['merge_target_file'], active_disk_object.backing_store)
            my_snap_top = _get_snap_dev(delete_info['file_to_merge'], active_disk_object.backing_store)
            try:
                commit_flags |= libvirt.VIR_DOMAIN_BLOCK_COMMIT_RELATIVE
            except AttributeError:
                ver = '.'.join([str(x) for x in MIN_LIBVIRT_BLOCKCOMMIT_RELATIVE_VERSION])
                msg = (_("Relative blockcommit support was not detected. Libvirt '%s' or later is required for online deletion of network storage-backed volume snapshots.") % ver)
                raise exception.Invalid(msg)
        commit_base = (my_snap_base or delete_info['merge_target_file'])
        commit_top = (my_snap_top or delete_info['file_to_merge'])
        bandwidth = 0
        LOG.debug(('will call blockCommit with commit_disk=%(commit_disk)s commit_base=%(commit_base)s commit_top=%(commit_top)s ' % {'commit_disk': commit_disk, 'commit_base': commit_base, 'commit_top': commit_top}))
        result = virt_dom.blockCommit(commit_disk, commit_base, commit_top, bandwidth, commit_flags)
        if (result == 0):
            LOG.debug('blockCommit started successfully')
        while self._wait_for_block_job(virt_dom, my_dev, abort_on_error=True):
            LOG.debug('waiting for blockCommit job completion')
            time.sleep(0.5)
Note:
    if file being merged into == active image:
        do a blockRebase (pull) operation
    else:
        do a blockCommit operation
    Files must be adjacent in snap chain.

:param instance: instance object reference
:param volume_id: volume UUID
:param snapshot_id: snapshot UUID (unused currently)
:param delete_info: {
    'type':              'qcow2',
    'file_to_merge':     'a.img',
    'merge_target_file': 'b.img' or None (if merging file_to_merge into
                                          active image)
  }


Libvirt blockjob handling required for this method is broken
in versions of libvirt that do not contain:
http://libvirt.org/git/?p=libvirt.git;h=0f9e67bfad (1.1.1)
(Patch is pending in 1.0.5-maint branch as well, but we cannot detect
libvirt 1.0.5.5 vs. 1.0.5.6 here.)
def _volume_snapshot_delete(self, context, instance, volume_id, snapshot_id, delete_info=None):
    "Note:\n            if file being merged into == active image:\n                do a blockRebase (pull) operation\n            else:\n                do a blockCommit operation\n            Files must be adjacent in snap chain.\n\n        :param instance: instance object reference\n        :param volume_id: volume UUID\n        :param snapshot_id: snapshot UUID (unused currently)\n        :param delete_info: {\n            'type':              'qcow2',\n            'file_to_merge':     'a.img',\n            'merge_target_file': 'b.img' or None (if merging file_to_merge into\n                                                  active image)\n          }\n\n\n        Libvirt blockjob handling required for this method is broken\n        in versions of libvirt that do not contain:\n        http://libvirt.org/git/?p=libvirt.git;h=0f9e67bfad (1.1.1)\n        (Patch is pending in 1.0.5-maint branch as well, but we cannot detect\n        libvirt 1.0.5.5 vs. 1.0.5.6 here.)\n        "
    if (not self._has_min_version(MIN_LIBVIRT_BLOCKJOBINFO_VERSION)):
        ver = '.'.join([str(x) for x in MIN_LIBVIRT_BLOCKJOBINFO_VERSION])
        msg = (_("Libvirt '%s' or later is required for online deletion of volume snapshots.") % ver)
        raise exception.Invalid(msg)
    LOG.debug('volume_snapshot_delete: delete_info: %s', delete_info)
    if (delete_info['type'] != 'qcow2'):
        msg = (_('Unknown delete_info type %s') % delete_info['type'])
        raise exception.NovaException(msg)
    try:
        virt_dom = self._lookup_by_name(instance.name)
    except exception.InstanceNotFound:
        raise exception.InstanceNotRunning(instance_id=instance.uuid)
    my_dev = None
    active_disk = None
    xml = virt_dom.XMLDesc(0)
    xml_doc = etree.fromstring(xml)
    device_info = vconfig.LibvirtConfigGuest()
    device_info.parse_dom(xml_doc)
    active_disk_object = None
    for guest_disk in device_info.devices:
        if (guest_disk.root_name != 'disk'):
            continue
        if ((guest_disk.target_dev is None) or (guest_disk.serial is None)):
            continue
        if (guest_disk.serial == volume_id):
            my_dev = guest_disk.target_dev
            active_disk = guest_disk.source_path
            active_protocol = guest_disk.source_protocol
            active_disk_object = guest_disk
            break
    if ((my_dev is None) or ((active_disk is None) and (active_protocol is None))):
        msg = (_('Disk with id: %s not found attached to instance.') % volume_id)
        LOG.debug('Domain XML: %s', xml)
        raise exception.NovaException(msg)
    LOG.debug('found device at %s', my_dev)

    def _get_snap_dev(filename, backing_store):
        if (filename is None):
            msg = _('filename cannot be None')
            raise exception.NovaException(msg)
        LOG.debug(('XML: %s' % xml))
        LOG.debug(('active disk object: %s' % active_disk_object))
        filename_to_merge = filename
        matched_name = None
        b = backing_store
        index = None
        current_filename = active_disk_object.source_name.split('/')[1]
        if (current_filename == filename_to_merge):
            return (my_dev + '[0]')
        while (b is not None):
            source_filename = b.source_name.split('/')[1]
            if (source_filename == filename_to_merge):
                LOG.debug(('found match: %s' % b.source_name))
                matched_name = b.source_name
                index = b.index
                break
            b = b.backing_store
        if (matched_name is None):
            msg = (_('no match found for %s') % filename_to_merge)
            raise exception.NovaException(msg)
        LOG.debug(('index of match (%s) is %s' % (b.source_name, index)))
        my_snap_dev = ('%s[%s]' % (my_dev, index))
        return my_snap_dev
    if (delete_info['merge_target_file'] is None):
        rebase_disk = my_dev
        rebase_flags = 0
        rebase_base = delete_info['file_to_merge']
        if (active_protocol is not None):
            rebase_base = _get_snap_dev(delete_info['file_to_merge'], active_disk_object.backing_store)
        rebase_bw = 0
        LOG.debug('disk: %(disk)s, base: %(base)s, bw: %(bw)s, flags: %(flags)s', {'disk': rebase_disk, 'base': rebase_base, 'bw': rebase_bw, 'flags': rebase_flags})
        result = virt_dom.blockRebase(rebase_disk, rebase_base, rebase_bw, rebase_flags)
        if (result == 0):
            LOG.debug('blockRebase started successfully')
        while self._wait_for_block_job(virt_dom, my_dev, abort_on_error=True):
            LOG.debug('waiting for blockRebase job completion')
            time.sleep(0.5)
    else:
        my_snap_base = None
        my_snap_top = None
        commit_disk = my_dev
        commit_flags = 0
        if (active_protocol is not None):
            my_snap_base = _get_snap_dev(delete_info['merge_target_file'], active_disk_object.backing_store)
            my_snap_top = _get_snap_dev(delete_info['file_to_merge'], active_disk_object.backing_store)
            try:
                commit_flags |= libvirt.VIR_DOMAIN_BLOCK_COMMIT_RELATIVE
            except AttributeError:
                ver = '.'.join([str(x) for x in MIN_LIBVIRT_BLOCKCOMMIT_RELATIVE_VERSION])
                msg = (_("Relative blockcommit support was not detected. Libvirt '%s' or later is required for online deletion of network storage-backed volume snapshots.") % ver)
                raise exception.Invalid(msg)
        commit_base = (my_snap_base or delete_info['merge_target_file'])
        commit_top = (my_snap_top or delete_info['file_to_merge'])
        bandwidth = 0
        LOG.debug(('will call blockCommit with commit_disk=%(commit_disk)s commit_base=%(commit_base)s commit_top=%(commit_top)s ' % {'commit_disk': commit_disk, 'commit_base': commit_base, 'commit_top': commit_top}))
        result = virt_dom.blockCommit(commit_disk, commit_base, commit_top, bandwidth, commit_flags)
        if (result == 0):
            LOG.debug('blockCommit started successfully')
        while self._wait_for_block_job(virt_dom, my_dev, abort_on_error=True):
            LOG.debug('waiting for blockCommit job completion')
            time.sleep(0.5)