cinder/cinder/volume/drivers/windows.py

247 lines
8.9 KiB
Python

# 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 oslo.config import cfg
from cinder import exception
from cinder import flags
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(__name__)
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, *args, **kwargs):
super(WindowsDriver, self).__init__(*args, **kwargs)
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, **kwargs):
"""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']
provider_location = volume['provider_location']
#DesAssigning target to initiators
wt_idmethod = self._conn_wmi.WT_IDMethod(HostName=provider_location,
Method=4,
Value=initiator_name)[0]
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']
q = self._conn_wmi.WT_Disk(Description=vol_name)
if not len(q):
LOG.debug(_('Disk not found: %s'), vol_name)
return None
wt_disk = q[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_()
def copy_image_to_volume(self, context, volume, image_service, image_id):
"""Fetch the image from image_service and write it to the volume."""
raise NotImplementedError()
def copy_volume_to_image(self, context, volume, image_service, image_meta):
"""Copy the volume to the specified image."""
raise NotImplementedError()