#!/usr/bin/env python

# Copyright 2010 OpenStack LLC.
# All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

"""
XenAPI Plugin for transfering data between host nodes
"""

import cPickle as pickle
import os
import os.path
import shlex
import shutil
import subprocess

import XenAPIPlugin

from pluginlib_nova import *
configure_logging('migration')


def move_file(item, src, dst):
    """Move file with logging."""
    #NOTE(markwash): shutil.move can be less efficient than it should be if
    #                dst is a directory. See http://bugs.python.org/issue1577.
    if os.path.isdir(dst):
        dst = os.path.join(dst, os.path.basename(src))
    logging.debug('Moving %(item)s: %(src)s -> %(dst)s' % locals())
    shutil.move(src, dst)


def move_vhds_into_sr(session, args):
    """Moves the VHDs from their copied location to the SR"""
    params = pickle.loads(exists(args, 'params'))
    instance_uuid = params['instance_uuid']

    sr_path = params['sr_path']
    sr_temp_path = "%s/tmp" % sr_path
    temp_vhd_path = "%s/instance%s" % (sr_temp_path, instance_uuid)

    logging.debug('Creating temporary SR path %s' % temp_vhd_path)
    os.makedirs(temp_vhd_path)

    # Discover the copied VHDs locally, and then set up paths to copy
    # them to under the SR
    source_image_path = "/images/instance%s" % instance_uuid

    old_base_copy_uuid = params['old_base_copy_uuid']
    new_base_copy_uuid = params['new_base_copy_uuid']
    source_base_copy_path = "%s/%s.vhd" % (source_image_path,
            old_base_copy_uuid)
    new_base_copy_path = "%s/%s.vhd" % (temp_vhd_path, new_base_copy_uuid)

    move_file('base', source_base_copy_path, new_base_copy_path)

    if 'old_cow_uuid' in params:
        old_cow_uuid = params['old_cow_uuid']
        new_cow_uuid = params['new_cow_uuid']

        source_cow_path = "%s/%s.vhd" % (source_image_path, old_cow_uuid)
        new_cow_path = "%s/%s.vhd" % (temp_vhd_path, new_cow_uuid)

        move_file('COW', source_cow_path, new_cow_path)

        # Link the COW to the base copy
        logging.debug('Attaching COW to the base %s -> %s' %
                      (new_cow_path, new_base_copy_path))
        subprocess.call(['/usr/sbin/vhd-util', 'modify',
                         '-n', new_cow_path, '-p', new_base_copy_path])

        # NOTE(sirp): COW should be copied before base_copy to avoid
        # snapwatchd GC'ing an unreferenced base copy VDI
        move_file('COW', new_cow_path, sr_path)

    move_file('base', new_base_copy_path, sr_path)

    logging.debug('Cleaning up source path %s' % source_image_path)
    os.rmdir(source_image_path)

    logging.debug('Cleaning up temporary SR path %s' % temp_vhd_path)
    os.rmdir(temp_vhd_path)
    return ""


def transfer_vhd(session, args):
    """Rsyncs a VHD to an adjacent host"""
    params = pickle.loads(exists(args, 'params'))
    instance_uuid = params['instance_uuid']
    host = params['host']
    vdi_uuid = params['vdi_uuid']
    sr_path = params['sr_path']
    vhd_path = "%s.vhd" % vdi_uuid

    source_path = "%s/%s" % (sr_path, vhd_path)
    dest_path = '%s:/images/instance%s/' % (host, instance_uuid)

    logging.debug("Preparing to transmit %s to %s" % (source_path,
            dest_path))

    ssh_cmd = '\"ssh -o StrictHostKeyChecking=no\"'

    # NOTE(dprince): shlex python 2.4 doesn't like unicode so we
    # explicitly convert to ascii
    rsync_args = shlex.split(('nohup /usr/bin/rsync -av -e %s %s %s'
            % (ssh_cmd, source_path, dest_path)).encode('ascii'))

    logging.debug('rsync %s' % (' '.join(rsync_args, )))

    rsync_proc = subprocess.Popen(rsync_args, stdout=subprocess.PIPE)
    logging.debug('Rsync output: \n %s' % rsync_proc.communicate()[0])
    logging.debug('Rsync return: %d' % rsync_proc.returncode)
    if rsync_proc.returncode != 0:
        raise Exception("Unexpected VHD transfer failure")
    return ""


if __name__ == '__main__':
    XenAPIPlugin.dispatch({'transfer_vhd': transfer_vhd,
            'move_vhds_into_sr': move_vhds_into_sr, })
