summaryrefslogtreecommitdiffstats
path: root/nova/virt
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2012-12-05 19:59:50 +0000
committerGerrit Code Review <review@openstack.org>2012-12-05 19:59:50 +0000
commit729263a0bb03e09e664f7f006d78dc8a4a4c83cb (patch)
tree91cfba6ac73f4dfccaad909c05e6f23d2ba548ef /nova/virt
parent2804b01a6bb404d3e120289680a3cfa4e4c3e380 (diff)
parent92792423bf5a62397d961e2cc8bc09e5b6710188 (diff)
downloadnova-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.py8
-rw-r--r--nova/virt/hyperv/basevolumeutils.py80
-rw-r--r--nova/virt/hyperv/volumeops.py29
-rw-r--r--nova/virt/hyperv/volumeutils.py72
-rw-r--r--nova/virt/hyperv/volumeutilsV2.py70
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)