'Swap volume for an instance.'

@wrap_exception()
@wrap_instance_fault
def swap_volume(self, context, old_volume_id, new_volume_id, instance, new_attachment_id=None):

    context = context.elevated()
    compute_utils.notify_about_volume_swap(context, instance, self.host, fields.NotificationAction.VOLUME_SWAP, fields.NotificationPhase.START, old_volume_id, new_volume_id)
    bdm = objects.BlockDeviceMapping.get_by_volume_and_instance(context, old_volume_id, instance.uuid)
    connector = self.driver.get_volume_connector(instance)
    resize_to = 0
    old_volume = self.volume_api.get(context, old_volume_id)
    is_cinder_migration = (True if (old_volume['status'] in ('retyping', 'migrating')) else False)
    old_vol_size = old_volume['size']
    new_vol_size = self.volume_api.get(context, new_volume_id)['size']
    if (new_vol_size > old_vol_size):
        resize_to = new_vol_size
    LOG.info('Swapping    volume    %(old_volume)s    for    %(new_volume)s', {'old_volume': old_volume_id, 'new_volume': new_volume_id}, instance=instance)
    (comp_ret, new_cinfo) = self._swap_volume(context, instance, bdm, connector, old_volume_id, new_volume_id, resize_to, new_attachment_id, is_cinder_migration)
    save_volume_id = comp_ret['save_volume_id']
    new_cinfo['serial'] = save_volume_id
    values = {'connection_info': jsonutils.dumps(new_cinfo), 'source_type': 'volume', 'destination_type': 'volume', 'snapshot_id': None, 'volume_id': save_volume_id, 'no_device': None}
    if resize_to:
        values['volume_size'] = resize_to
    if (new_attachment_id is not None):
        values['attachment_id'] = new_attachment_id
    LOG.debug('swap_volume:    Updating    volume    %(volume_id)s    BDM    record    with    %(updates)s', {'volume_id': bdm.volume_id, 'updates': values}, instance=instance)
    bdm.update(values)
    bdm.save()
    compute_utils.notify_about_volume_swap(context, instance, self.host, fields.NotificationAction.VOLUME_SWAP, fields.NotificationPhase.END, old_volume_id, new_volume_id)
