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

# Copyright 2012 Pedro Navarro Perez
# 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.
"""
Volume driver for Windows Server 2012

This driver requires ISCSI target role installed

"""
import os
import sys

from cinder import exception
from cinder import flags
from cinder.openstack.common import cfg
from cinder.openstack.common import log as logging
from cinder.volume import driver

# Check needed for unit testing on Unix
if os.name == 'nt':
    import wmi


LOG = logging.getLogger("cinder.volume.windows.volume")

FLAGS = flags.FLAGS

windows_opts = [
    cfg.StrOpt('windows_iscsi_lun_path',
              default='C:\iSCSIVirtualDisks',
              help='Path to store VHD backed volumes'),
]

FLAGS.register_opts(windows_opts)


class WindowsDriver(driver.ISCSIDriver):
    """Executes volume driver commands on Windows Storage server."""

    def __init__(self):
        super(WindowsDriver, self).__init__()

    def do_setup(self, context):
        """Setup the Windows Volume driver.

        Called one time by the manager after the driver is loaded.
        Validate the flags we care about
        """
        #Set the flags
        self._conn_wmi = wmi.WMI(moniker='//./root/wmi')
        self._conn_cimv2 = wmi.WMI(moniker='//./root/cimv2')

    def check_for_setup_error(self):
        """Check that the driver is working and can communicate.
        """
        #Invoking the portal an checking that is listening
        wt_portal = self._conn_wmi.WT_Portal()[0]
        listen = wt_portal.Listen
        if not listen:
            raise exception.VolumeBackendAPIException()

    def initialize_connection(self, volume, connector):
        """Driver entry point to attach a volume to an instance.
        """
        initiator_name = connector['initiator']
        target_name = volume['provider_location']

        cl = self._conn_wmi.__getattr__("WT_IDMethod")
        wt_idmethod = cl.new()
        wt_idmethod.HostName = target_name
        wt_idmethod.Method = 4
        wt_idmethod.Value = initiator_name
        wt_idmethod.put()
        #Getting the portal and port information
        wt_portal = self._conn_wmi.WT_Portal()[0]
        (address, port) = (wt_portal.Address, wt_portal.Port)
        #Getting the host information
        hosts = self._conn_wmi.WT_Host(Hostname=target_name)
        host = hosts[0]

        properties = {}
        properties['target_discovered'] = False
        properties['target_portal'] = '%s:%s' % (address, port)
        properties['target_iqn'] = host.TargetIQN
        properties['target_lun'] = 0
        properties['volume_id'] = volume['id']

        auth = volume['provider_auth']
        if auth:
            (auth_method, auth_username, auth_secret) = auth.split()

            properties['auth_method'] = auth_method
            properties['auth_username'] = auth_username
            properties['auth_password'] = auth_secret

        return {
            'driver_volume_type': 'iscsi',
            'data': properties,
        }

    def terminate_connection(self, volume, connector):
        """Driver entry point to unattach a volume from an instance.

        Unmask the LUN on the storage system so the given intiator can no
        longer access it.
        """
        initiator_name = connector['initiator']
        #DesAssigning target to initiators
        wt_idmethod = self._conn_wmi.WT_IDMethod(HostName=volume['name'],
                                                 Method=4,
                                                 Value=initiator_name)
        wt_idmethod.Delete_()

    def create_volume(self, volume):
        """Driver entry point for creating a new volume."""
        vhd_path = self._get_vhd_path(volume)
        vol_name = volume['name']
        #The WMI procedure returns a Generic failure
        cl = self._conn_wmi.__getattr__("WT_Disk")
        cl.NewWTDisk(DevicePath=vhd_path,
                     Description=vol_name,
                     SizeInMB=volume['size'] * 1024)

    def _get_vhd_path(self, volume):
        base_vhd_folder = FLAGS.windows_iscsi_lun_path
        if not os.path.exists(base_vhd_folder):
                LOG.debug(_('Creating folder %s '), base_vhd_folder)
                os.makedirs(base_vhd_folder)
        return os.path.join(base_vhd_folder, str(volume['name']) + ".vhd")

    def delete_volume(self, volume):
        """Driver entry point for destroying existing volumes."""
        vol_name = volume['name']
        wt_disk = self._conn_wmi.WT_Disk(Description=vol_name)[0]
        wt_disk.Delete_()
        vhdfiles = self._conn_cimv2.query(
        "Select * from CIM_DataFile where Name = '" +
        self._get_vhd_path(volume) + "'")
        if len(vhdfiles) > 0:
            vhdfiles[0].Delete()

    def create_snapshot(self, snapshot):
        """Driver entry point for creating a snapshot.
        """
        #Getting WT_Snapshot class
        vol_name = snapshot['volume_name']
        snapshot_name = snapshot['name']

        wt_disk = self._conn_wmi.WT_Disk(Description=vol_name)[0]
        #API Calls gets Generic Failure
        cl = self._conn_wmi.__getattr__("WT_Snapshot")
        disk_id = wt_disk.WTD
        out = cl.Create(WTD=disk_id)
        #Setting description since it used as a KEY
        wt_snapshot_created = self._conn_wmi.WT_Snapshot(Id=out[0])[0]
        wt_snapshot_created.Description = snapshot_name
        wt_snapshot_created.put()

    def create_volume_from_snapshot(self, volume, snapshot):
        """Driver entry point for exporting snapshots as volumes."""
        snapshot_name = snapshot['name']
        wt_snapshot = self._conn_wmi.WT_Snapshot(Description=snapshot_name)[0]
        disk_id = wt_snapshot.Export()[0]
        wt_disk = self._conn_wmi.WT_Disk(WTD=disk_id)[0]
        wt_disk.Description = volume['name']
        wt_disk.put()

    def delete_snapshot(self, snapshot):
        """Driver entry point for deleting a snapshot."""
        snapshot_name = snapshot['name']
        wt_snapshot = self._conn_wmi.WT_Snapshot(Description=snapshot_name)[0]
        wt_snapshot.Delete_()

    def _do_export(self, _ctx, volume, ensure=False):
        """Do all steps to get disk exported as LUN 0 at separate target.

        :param volume: reference of volume to be exported
        :param ensure: if True, ignore errors caused by already existing
            resources
        :return: iscsiadm-formatted provider location string
        """
        target_name = "%s%s" % (FLAGS.iscsi_target_prefix, volume['name'])
        #ISCSI target creation
        try:
            cl = self._conn_wmi.__getattr__("WT_Host")
            cl.NewHost(HostName=target_name)
        except Exception as exc:
            excep_info = exc.com_error.excepinfo[2]
            if not ensure or excep_info.find(u'The file exists') == -1:
                raise
            else:
                LOG.info(_('Ignored target creation error "%s"'
                                             ' while ensuring export'), exc)
        #Get the disk to add
        vol_name = volume['name']
        wt_disk = self._conn_wmi.WT_Disk(Description=vol_name)[0]
        wt_host = self._conn_wmi.WT_Host(HostName=target_name)[0]
        wt_host.AddWTDisk(wt_disk.WTD)

        return target_name

    def ensure_export(self, context, volume):
        """Driver entry point to get the export info for an existing volume."""
        self._do_export(context, volume, ensure=True)

    def create_export(self, context, volume):
        """Driver entry point to get the export info for a new volume."""
        loc = self._do_export(context, volume, ensure=False)
        return {'provider_location': loc}

    def remove_export(self, context, volume):
        """Driver exntry point to remove an export for a volume.
        """
        target_name = "%s%s" % (FLAGS.iscsi_target_prefix, volume['name'])

        #Get ISCSI target
        wt_host = self._conn_wmi.WT_Host(HostName=target_name)[0]
        wt_host.RemoveAllWTDisks()
        wt_host.Delete_()
