diff options
| author | Jenkins <jenkins@review.openstack.org> | 2012-12-05 19:59:50 +0000 |
|---|---|---|
| committer | Gerrit Code Review <review@openstack.org> | 2012-12-05 19:59:50 +0000 |
| commit | 729263a0bb03e09e664f7f006d78dc8a4a4c83cb (patch) | |
| tree | 91cfba6ac73f4dfccaad909c05e6f23d2ba548ef /nova/virt | |
| parent | 2804b01a6bb404d3e120289680a3cfa4e4c3e380 (diff) | |
| parent | 92792423bf5a62397d961e2cc8bc09e5b6710188 (diff) | |
| download | nova-729263a0bb03e09e664f7f006d78dc8a4a4c83cb.tar.gz nova-729263a0bb03e09e664f7f006d78dc8a4a4c83cb.tar.xz nova-729263a0bb03e09e664f7f006d78dc8a4a4c83cb.zip | |
Merge "Add support for new WMI iSCSI initiator API"
Diffstat (limited to 'nova/virt')
| -rw-r--r-- | nova/virt/hyperv/baseops.py | 8 | ||||
| -rw-r--r-- | nova/virt/hyperv/basevolumeutils.py | 80 | ||||
| -rw-r--r-- | nova/virt/hyperv/volumeops.py | 29 | ||||
| -rw-r--r-- | nova/virt/hyperv/volumeutils.py | 72 | ||||
| -rw-r--r-- | nova/virt/hyperv/volumeutilsV2.py | 70 |
5 files changed, 198 insertions, 61 deletions
diff --git a/nova/virt/hyperv/baseops.py b/nova/virt/hyperv/baseops.py index 3d941a854..5b617f898 100644 --- a/nova/virt/hyperv/baseops.py +++ b/nova/virt/hyperv/baseops.py @@ -35,6 +35,7 @@ class BaseOps(object): self.__conn_v2 = None self.__conn_cimv2 = None self.__conn_wmi = None + self.__conn_storage = None @property def _conn(self): @@ -59,3 +60,10 @@ class BaseOps(object): if self.__conn_wmi is None: self.__conn_wmi = wmi.WMI(moniker='//./root/wmi') return self.__conn_wmi + + @property + def _conn_storage(self): + if self.__conn_storage is None: + storage_namespace = '//./Root/Microsoft/Windows/Storage' + self.__conn_storage = wmi.WMI(moniker=storage_namespace) + return self.__conn_storage diff --git a/nova/virt/hyperv/basevolumeutils.py b/nova/virt/hyperv/basevolumeutils.py new file mode 100644 index 000000000..f62ac28b4 --- /dev/null +++ b/nova/virt/hyperv/basevolumeutils.py @@ -0,0 +1,80 @@ +# 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. + +""" +Helper methods for operations related to the management of volumes, +and storage repositories +""" + +import sys + +from nova import block_device +from nova.openstack.common import cfg +from nova.openstack.common import log as logging +from nova.virt import driver + +# Check needed for unit testing on Unix +if sys.platform == 'win32': + import _winreg + +LOG = logging.getLogger(__name__) +CONF = cfg.CONF +CONF.import_opt('my_ip', 'nova.config') + + +class BaseVolumeUtils(object): + + def get_iscsi_initiator(self, cim_conn): + """Get iscsi initiator name for this machine""" + + computer_system = cim_conn.Win32_ComputerSystem()[0] + hostname = computer_system.name + keypath = \ + r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\iSCSI\Discovery" + try: + key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, keypath, 0, + _winreg.KEY_ALL_ACCESS) + temp = _winreg.QueryValueEx(key, 'DefaultInitiatorName') + initiator_name = str(temp[0]) + _winreg.CloseKey(key) + except Exception: + LOG.info(_("The ISCSI initiator name can't be found. " + "Choosing the default one")) + computer_system = cim_conn.Win32_ComputerSystem()[0] + initiator_name = "iqn.1991-05.com.microsoft:" + \ + hostname.lower() + return { + 'ip': CONF.my_ip, + 'initiator': initiator_name, + } + + def volume_in_mapping(self, mount_device, block_device_info): + block_device_list = [block_device.strip_dev(vol['mount_device']) + for vol in + driver.block_device_info_get_mapping( + block_device_info)] + swap = driver.block_device_info_get_swap(block_device_info) + if driver.swap_is_usable(swap): + block_device_list.append( + block_device.strip_dev(swap['device_name'])) + block_device_list += [block_device.strip_dev( + ephemeral['device_name']) + for ephemeral in + driver.block_device_info_get_ephemerals(block_device_info)] + + LOG.debug(_("block_device_list %s"), block_device_list) + return block_device.strip_dev(mount_device) in block_device_list diff --git a/nova/virt/hyperv/volumeops.py b/nova/virt/hyperv/volumeops.py index 821a79018..493ceeb6c 100644 --- a/nova/virt/hyperv/volumeops.py +++ b/nova/virt/hyperv/volumeops.py @@ -27,6 +27,7 @@ from nova.virt import driver from nova.virt.hyperv import baseops from nova.virt.hyperv import vmutils from nova.virt.hyperv import volumeutils +from nova.virt.hyperv import volumeutilsV2 LOG = logging.getLogger(__name__) @@ -37,6 +38,9 @@ hyper_volumeops_opts = [ cfg.IntOpt('hyperv_wait_between_attach_retry', default=5, help='The seconds to wait between an volume attachment attempt'), + cfg.BoolOpt('force_volumeutils_v1', + default=False, + help='Force volumeutils v1'), ] CONF = cfg.CONF @@ -62,7 +66,24 @@ class VolumeOps(baseops.BaseOps): CONF.hyperv_attaching_volume_retry_count self._wait_between_attach_retry = \ CONF.hyperv_wait_between_attach_retry - self._volutils = volumeutils.VolumeUtils() + self._volutils = self._get_volume_utils() + + def _get_volume_utils(self): + if(not CONF.force_volumeutils_v1) and \ + (self._get_hypervisor_version() >= 6.2): + return volumeutilsV2.VolumeUtilsV2( + self._conn_storage, self._conn_wmi) + else: + return volumeutils.VolumeUtils(self._conn_wmi) + + def _get_hypervisor_version(self): + """Get hypervisor version. + :returns: hypervisor version (ex. 12003) + """ + version = self._conn_cimv2.Win32_OperatingSystem()[0]\ + .Version + LOG.info(_('Windows version: %s ') % version) + return version def attach_boot_volume(self, block_device_info, vm_name): """Attach the boot volume to the IDE controller""" @@ -95,7 +116,7 @@ class VolumeOps(baseops.BaseOps): self._attach_volume_to_controller(ctrller, 0, mounted_disk, vm) except Exception as exn: LOG.exception(_('Attach boot from volume failed: %s'), exn) - self._volutils.logout_storage_target(self._conn_wmi, target_iqn) + self._volutils.logout_storage_target(target_iqn) raise vmutils.HyperVException( _('Unable to attach boot volume to instance %s') % vm_name) @@ -132,7 +153,7 @@ class VolumeOps(baseops.BaseOps): mounted_disk, vm) except Exception as exn: LOG.exception(_('Attach volume failed: %s'), exn) - self._volutils.logout_storage_target(self._conn_wmi, target_iqn) + self._volutils.logout_storage_target(target_iqn) raise vmutils.HyperVException( _('Unable to attach volume to instance %s') % instance_name) @@ -198,7 +219,7 @@ class VolumeOps(baseops.BaseOps): _('Failed to remove volume from VM %s') % instance_name) #Sending logout - self._volutils.logout_storage_target(self._conn_wmi, target_iqn) + self._volutils.logout_storage_target(target_iqn) def get_volume_connector(self, instance): if not self._initiator: diff --git a/nova/virt/hyperv/volumeutils.py b/nova/virt/hyperv/volumeutils.py index 91cc6b0a5..8ae437cf9 100644 --- a/nova/virt/hyperv/volumeutils.py +++ b/nova/virt/hyperv/volumeutils.py @@ -20,62 +20,37 @@ Helper methods for operations related to the management of volumes, and storage repositories """ -import subprocess -import sys import time -from nova import block_device +from eventlet.green import subprocess + from nova.openstack.common import cfg from nova.openstack.common import log as logging -from nova.virt import driver +from nova.virt.hyperv import basevolumeutils from nova.virt.hyperv import vmutils -# Check needed for unit testing on Unix -if sys.platform == 'win32': - import _winreg - LOG = logging.getLogger(__name__) CONF = cfg.CONF -CONF.import_opt('my_ip', 'nova.config') -class VolumeUtils(object): +class VolumeUtils(basevolumeutils.BaseVolumeUtils): + + def __init__(self, conn_wmi): + self._conn_wmi = conn_wmi + def execute(self, *args, **kwargs): + _PIPE = subprocess.PIPE # pylint: disable=E1101 proc = subprocess.Popen( [args], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, + stdin=_PIPE, + stdout=_PIPE, + stderr=_PIPE, ) stdout_value, stderr_value = proc.communicate() if stdout_value.find('The operation completed successfully') == -1: raise vmutils.HyperVException(_('An error has occurred when ' 'calling the iscsi initiator: %s') % stdout_value) - def get_iscsi_initiator(self, cim_conn): - """Get iscsi initiator name for this machine""" - - computer_system = cim_conn.Win32_ComputerSystem()[0] - hostname = computer_system.name - keypath = \ - r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\iSCSI\Discovery" - try: - key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, keypath, 0, - _winreg.KEY_ALL_ACCESS) - temp = _winreg.QueryValueEx(key, 'DefaultInitiatorName') - initiator_name = str(temp[0]) - _winreg.CloseKey(key) - except Exception: - LOG.info(_("The ISCSI initiator name can't be found. " - "Choosing the default one")) - computer_system = cim_conn.Win32_ComputerSystem()[0] - initiator_name = "iqn.1991-05.com.microsoft:" + \ - hostname.lower() - return { - 'ip': CONF.my_ip, - 'initiator': initiator_name, - } - def login_storage_target(self, target_lun, target_iqn, target_portal): """Add target portal, list targets and logins to the target""" separator = target_portal.find(':') @@ -89,13 +64,13 @@ class VolumeUtils(object): self.execute('iscsicli.exe ' + 'LisTargets') #Sending login self.execute('iscsicli.exe ' + 'qlogintarget ' + target_iqn) - #Waiting the disk to be mounted. Research this + #Waiting the disk to be mounted. Research this to avoid sleep time.sleep(CONF.hyperv_wait_between_attach_retry) - def logout_storage_target(self, _conn_wmi, target_iqn): + def logout_storage_target(self, target_iqn): """ Logs out storage target through its session id """ - sessions = _conn_wmi.query( + sessions = self._conn_wmi.query( "SELECT * FROM MSiSCSIInitiator_SessionClass \ WHERE TargetName='" + target_iqn + "'") for session in sessions: @@ -104,20 +79,3 @@ class VolumeUtils(object): def execute_log_out(self, session_id): """ Executes log out of the session described by its session ID """ self.execute('iscsicli.exe ' + 'logouttarget ' + session_id) - - def volume_in_mapping(self, mount_device, block_device_info): - block_device_list = [block_device.strip_dev(vol['mount_device']) - for vol in - driver.block_device_info_get_mapping( - block_device_info)] - swap = driver.block_device_info_get_swap(block_device_info) - if driver.swap_is_usable(swap): - block_device_list.append( - block_device.strip_dev(swap['device_name'])) - block_device_list += [block_device.strip_dev( - ephemeral['device_name']) - for ephemeral in - driver.block_device_info_get_ephemerals(block_device_info)] - - LOG.debug(_("block_device_list %s"), block_device_list) - return block_device.strip_dev(mount_device) in block_device_list diff --git a/nova/virt/hyperv/volumeutilsV2.py b/nova/virt/hyperv/volumeutilsV2.py new file mode 100644 index 000000000..8d7c91862 --- /dev/null +++ b/nova/virt/hyperv/volumeutilsV2.py @@ -0,0 +1,70 @@ +# 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. + +""" +Helper methods for operations related to the management of volumes, +and storage repositories for Windows 2012 +""" +import time + +from nova.openstack.common import cfg +from nova.openstack.common import log as logging +from nova.virt.hyperv import basevolumeutils + +LOG = logging.getLogger(__name__) +CONF = cfg.CONF + + +class VolumeUtilsV2(basevolumeutils.BaseVolumeUtils): + + def __init__(self, conn_storage, conn_wmi): + self._conn_storage = conn_storage + self._conn_wmi = conn_wmi + + def login_storage_target(self, target_lun, target_iqn, + target_portal): + """Add target portal, list targets and logins to the target""" + separator = target_portal.find(':') + target_address = target_portal[:separator] + target_port = target_portal[separator + 1:] + #Adding target portal to iscsi initiator. Sending targets + portal = self._conn_storage.__getattr__("MSFT_iSCSITargetPortal") + portal.New(TargetPortalAddress=target_address, + TargetPortalPortNumber=target_port) + #Connecting to the target + target = self._conn_storage.__getattr__("MSFT_iSCSITarget") + target.Connect(NodeAddress=target_iqn, + IsPersistent=True) + #Waiting the disk to be mounted. Research this + time.sleep(CONF.hyperv_wait_between_attach_retry) + + def logout_storage_target(self, target_iqn): + """ Logs out storage target through its session id """ + + target = self._conn_storage.MSFT_iSCSITarget( + NodeAddress=target_iqn)[0] + if target.IsConnected: + session = self._conn_storage.MSFT_iSCSISession( + TargetNodeAddress=target_iqn)[0] + if session.IsPersistent: + session.Unregister() + target.Disconnect() + + def execute_log_out(self, session_id): + session = self._conn_wmi.MSiSCSIInitiator_SessionClass( + SessionId=session_id)[0] + self.logout_storage_target(session.TargetName) |
