'Swap volume attached to an instance.'

@check_instance_lock
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.PAUSED, vm_states.SUSPENDED, vm_states.STOPPED, vm_states.RESIZED, vm_states.SOFT_DELETED])
def swap_volume(self, context, instance, old_volume, new_volume):

    if (not old_volume.get('attachments', {}).get(instance.uuid)):
        msg = _('Old    volume    is    attached    to    a    different    instance.')
        raise exception.InvalidVolume(reason=msg)
    if (new_volume['attach_status'] == 'attached'):
        msg = _('New    volume    must    be    detached    in    order    to    swap.')
        raise exception.InvalidVolume(reason=msg)
    if (int(new_volume['size']) < int(old_volume['size'])):
        msg = _('New    volume    must    be    the    same    size    or    larger.')
        raise exception.InvalidVolume(reason=msg)
    self.volume_api.check_availability_zone(context, new_volume, instance=instance)
    try:
        self.volume_api.begin_detaching(context, old_volume['id'])
    except exception.InvalidInput as exc:
        raise exception.InvalidVolume(reason=exc.format_message())
    bdm = objects.BlockDeviceMapping.get_by_volume_and_instance(context, old_volume['id'], instance.uuid)
    new_attachment_id = None
    if (bdm.attachment_id is None):
        self.volume_api.reserve_volume(context, new_volume['id'])
    else:
        new_attachment_id = self.volume_api.attachment_create(context, new_volume['id'], instance.uuid)['id']
    try:
        self.compute_rpcapi.swap_volume(context, instance=instance, old_volume_id=old_volume['id'], new_volume_id=new_volume['id'], new_attachment_id=new_attachment_id)
    except Exception:
        with excutils.save_and_reraise_exception():
            self.volume_api.roll_detaching(context, old_volume['id'])
            if (new_attachment_id is None):
                self.volume_api.unreserve_volume(context, new_volume['id'])
            else:
                self.volume_api.attachment_delete(context, new_attachment_id)
