# vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright (c) 2010 Citrix Systems, Inc.
#
#    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.

"""
Management class for Storage-related functions (attach, detach, etc).
"""

from nova import exception
from nova.openstack.common import log as logging
from nova.virt.xenapi import vm_utils
from nova.virt.xenapi import volume_utils


LOG = logging.getLogger(__name__)


class VolumeOps(object):
    """
    Management class for Volume-related tasks
    """

    def __init__(self, session):
        self._session = session

    def create_volume_for_sm(self, volume, sr_uuid):
        LOG.debug("Creating volume for Storage Manager")

        sm_vol_rec = {}
        try:
            sr_ref = self._session.call_xenapi("SR.get_by_uuid", sr_uuid)
        except self._session.XenAPI.Failure, exc:
            LOG.exception(exc)
            raise volume_utils.StorageError(_('Unable to get SR using uuid'))
        #Create VDI
        label = 'vol-' + volume['id']
        desc = 'xensm volume for ' + volume['id']
        # size presented to xenapi is in bytes, while euca api is in GB
        vdi_size = volume['size'] * 1024 * 1024 * 1024
        vdi_ref = vm_utils.create_vdi(self._session, sr_ref,
                                      None, label, desc,
                                      vdi_size, False)
        vdi_rec = self._session.call_xenapi("VDI.get_record", vdi_ref)
        sm_vol_rec['vdi_uuid'] = vdi_rec['uuid']
        return sm_vol_rec

    def delete_volume_for_sm(self, vdi_uuid):
        vdi_ref = self._session.call_xenapi("VDI.get_by_uuid", vdi_uuid)
        if vdi_ref is None:
            raise exception.NovaException(_('Could not find VDI ref'))

        vm_utils.destroy_vdi(self._session, vdi_ref)

    def create_sr(self, label, params):
        LOG.debug(_("Creating SR %s") % label)
        sr_ref = volume_utils.create_sr(self._session, label, params)
        if sr_ref is None:
            raise exception.NovaException(_('Could not create SR'))
        sr_rec = self._session.call_xenapi("SR.get_record", sr_ref)
        if sr_rec is None:
            raise exception.NovaException(_('Could not retrieve SR record'))
        return sr_rec['uuid']

    # Checks if sr has already been introduced to this host
    def introduce_sr(self, sr_uuid, label, params):
        LOG.debug(_("Introducing SR %s") % label)
        sr_ref = volume_utils.find_sr_by_uuid(self._session, sr_uuid)
        if sr_ref:
            LOG.debug(_('SR found in xapi database. No need to introduce'))
            return sr_ref
        sr_ref = volume_utils.introduce_sr(self._session, sr_uuid, label,
                                           params)
        if sr_ref is None:
            raise exception.NovaException(_('Could not introduce SR'))
        return sr_ref

    def is_sr_on_host(self, sr_uuid):
        LOG.debug(_('Checking for SR %s') % sr_uuid)
        sr_ref = volume_utils.find_sr_by_uuid(self._session, sr_uuid)
        if sr_ref:
            return True
        return False

    # Checks if sr has been introduced
    def forget_sr(self, sr_uuid):
        sr_ref = volume_utils.find_sr_by_uuid(self._session, sr_uuid)
        if sr_ref is None:
            LOG.INFO(_('SR %s not found in the xapi database') % sr_uuid)
            return
        try:
            volume_utils.forget_sr(self._session, sr_uuid)
        except volume_utils.StorageError, exc:
            LOG.exception(exc)
            raise exception.NovaException(_('Could not forget SR'))

    def attach_volume(self, connection_info, instance_name, mountpoint):
        """Attach volume storage to VM instance"""

        vm_ref = vm_utils.vm_ref_or_raise(self._session, instance_name)

        # NOTE: No Resource Pool concept so far
        LOG.debug(_("Attach_volume: %(connection_info)s, %(instance_name)s,"
                " %(mountpoint)s") % locals())
        driver_type = connection_info['driver_volume_type']
        if driver_type not in ['iscsi', 'xensm']:
            raise exception.VolumeDriverNotFound(driver_type=driver_type)

        connection_data = connection_info['data']
        dev_number = volume_utils.get_device_number(mountpoint)

        if 'name_label' not in connection_data:
            label = 'tempSR-%s' % connection_data['volume_id']
        else:
            label = connection_data['name_label']
            del connection_data['name_label']

        if 'name_description' not in connection_data:
            desc = 'Disk-for:%s' % instance_name
        else:
            desc = connection_data['name_description']

        LOG.debug(connection_info)
        sr_params = {}
        if u'sr_uuid' not in connection_data:
            sr_params = volume_utils.parse_volume_info(connection_data)
            uuid = "FA15E-D15C-" + str(sr_params['id'])
            sr_params['sr_type'] = 'iscsi'
        else:
            uuid = connection_data['sr_uuid']
            for k in connection_data['introduce_sr_keys']:
                sr_params[k] = connection_data[k]

        sr_params['name_description'] = desc

        # Introduce SR
        try:
            sr_ref = self.introduce_sr(uuid, label, sr_params)
            LOG.debug(_('Introduced %(label)s as %(sr_ref)s.') % locals())
        except self._session.XenAPI.Failure, exc:
            LOG.exception(exc)
            raise volume_utils.StorageError(
                                _('Unable to introduce Storage Repository'))

        vdi_uuid = None
        target_lun = None
        if 'vdi_uuid' in connection_data:
            vdi_uuid = connection_data['vdi_uuid']
        elif 'target_lun' in connection_data:
            target_lun = connection_data['target_lun']
        else:
            vdi_uuid = None

        # Introduce VDI  and attach VBD to VM
        try:
            vdi_ref = volume_utils.introduce_vdi(self._session, sr_ref,
                                                 vdi_uuid, target_lun)
        except volume_utils.StorageError, exc:
            LOG.exception(exc)
            self.forget_sr(uuid)
            raise Exception(_('Unable to create VDI on SR %(sr_ref)s for'
                    ' instance %(instance_name)s') % locals())

        try:
            vbd_ref = vm_utils.create_vbd(self._session, vm_ref, vdi_ref,
                                          dev_number, bootable=False,
                                          osvol=True)
        except self._session.XenAPI.Failure, exc:
            LOG.exception(exc)
            self.forget_sr(uuid)
            raise Exception(_('Unable to use SR %(sr_ref)s for'
                              ' instance %(instance_name)s') % locals())

        try:
            self._session.call_xenapi("VBD.plug", vbd_ref)
        except self._session.XenAPI.Failure, exc:
            LOG.exception(exc)
            self.forget_sr(uuid)
            raise Exception(_('Unable to attach volume to instance %s')
                            % instance_name)

        LOG.info(_('Mountpoint %(mountpoint)s attached to'
                ' instance %(instance_name)s') % locals())

    def detach_volume(self, connection_info, instance_name, mountpoint):
        """Detach volume storage to VM instance"""

        vm_ref = vm_utils.vm_ref_or_raise(self._session, instance_name)

        # Detach VBD from VM
        LOG.debug(_("Detach_volume: %(instance_name)s, %(mountpoint)s")
                % locals())
        device_number = volume_utils.mountpoint_to_number(mountpoint)
        try:
            vbd_ref = vm_utils.find_vbd_by_number(self._session, vm_ref,
                                                  device_number)
        except volume_utils.StorageError, exc:
            LOG.exception(exc)
            raise Exception(_('Unable to locate volume %s') % mountpoint)

        try:
            vm_rec = self._session.call_xenapi("VM.get_record", vm_ref)
            sr_ref = volume_utils.find_sr_from_vbd(self._session, vbd_ref)
            if vm_rec['power_state'] != 'Halted':
                vm_utils.unplug_vbd(self._session, vbd_ref)
        except volume_utils.StorageError, exc:
            LOG.exception(exc)
            raise Exception(_('Unable to detach volume %s') % mountpoint)
        try:
            vm_utils.destroy_vbd(self._session, vbd_ref)
        except volume_utils.StorageError, exc:
            LOG.exception(exc)
            raise Exception(_('Unable to destroy vbd %s') % mountpoint)

        # Forget SR only if no other volumes on this host are using it
        try:
            volume_utils.purge_sr(self._session, sr_ref)
        except volume_utils.StorageError, exc:
            LOG.exception(exc)
            raise Exception(_('Error purging SR %s') % sr_ref)

        LOG.info(_('Mountpoint %(mountpoint)s detached from'
                ' instance %(instance_name)s') % locals())
