Add the iscsi device check and exception processing.
Before downloading the image and executing the command "qemu-img convert", check if the iSCSI device is still available via the command "sudo dd if=<device> of=/dev/null count=1". This command will raise an exception with the message "Input/output error", if the back-end storage is disconnected to the cinder-volume node, so we use it to test the availability of the storage device. If it is unavailable, there is no need to download the image & "qemu-img convert" and an exception DeviceUnavailable will be raised. Fixed Bug1169290. Change-Id: I133b4cc1bac493df073d42e240092cf2e6300454
This commit is contained in:
parent
b820205c73
commit
aa7fde57a3
@ -194,6 +194,10 @@ class ImageUnacceptable(Invalid):
|
|||||||
message = _("Image %(image_id)s is unacceptable: %(reason)s")
|
message = _("Image %(image_id)s is unacceptable: %(reason)s")
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceUnavailable(Invalid):
|
||||||
|
message = _("The device in the path %(path)s is unavailable: %(reason)s")
|
||||||
|
|
||||||
|
|
||||||
class InvalidUUID(Invalid):
|
class InvalidUUID(Invalid):
|
||||||
message = _("Expected a uuid but received %(uuid)s.")
|
message = _("Expected a uuid but received %(uuid)s.")
|
||||||
|
|
||||||
|
@ -310,6 +310,16 @@ class ISCSIDriver(VolumeDriver):
|
|||||||
(iscsi_command, out, err))
|
(iscsi_command, out, err))
|
||||||
return (out, err)
|
return (out, err)
|
||||||
|
|
||||||
|
def _run_iscsiadm_bare(self, iscsi_command, **kwargs):
|
||||||
|
check_exit_code = kwargs.pop('check_exit_code', 0)
|
||||||
|
(out, err) = utils.execute('iscsiadm',
|
||||||
|
*iscsi_command,
|
||||||
|
run_as_root=True,
|
||||||
|
check_exit_code=check_exit_code)
|
||||||
|
LOG.debug("iscsiadm %s: stdout=%s stderr=%s" %
|
||||||
|
(iscsi_command, out, err))
|
||||||
|
return (out, err)
|
||||||
|
|
||||||
def _iscsiadm_update(self, iscsi_properties, property_key, property_value,
|
def _iscsiadm_update(self, iscsi_properties, property_key, property_value,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
iscsi_command = ('--op', 'update', '-n', property_key,
|
iscsi_command = ('--op', 'update', '-n', property_key,
|
||||||
@ -347,6 +357,22 @@ class ISCSIDriver(VolumeDriver):
|
|||||||
def terminate_connection(self, volume, connector, **kwargs):
|
def terminate_connection(self, volume, connector, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def _check_valid_device(self, path):
|
||||||
|
cmd = ('dd', 'if=%(path)s' % {"path": path},
|
||||||
|
'of=/dev/null', 'count=1')
|
||||||
|
out, info = None, None
|
||||||
|
try:
|
||||||
|
out, info = utils.execute(*cmd, run_as_root=True)
|
||||||
|
except exception.ProcessExecutionError as e:
|
||||||
|
LOG.error(_("Failed to access the device on the path "
|
||||||
|
"%(path)s: %(error)s.") %
|
||||||
|
{"path": path, "error": e.stderr})
|
||||||
|
return False
|
||||||
|
# If the info is none, the path does not exist.
|
||||||
|
if info is None:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def _get_iscsi_initiator(self):
|
def _get_iscsi_initiator(self):
|
||||||
"""Get iscsi initiator name for this machine"""
|
"""Get iscsi initiator name for this machine"""
|
||||||
# NOTE openiscsi stores initiator name in a file that
|
# NOTE openiscsi stores initiator name in a file that
|
||||||
@ -418,40 +444,71 @@ class ISCSIDriver(VolumeDriver):
|
|||||||
"node.session.auth.password",
|
"node.session.auth.password",
|
||||||
iscsi_properties['auth_password'])
|
iscsi_properties['auth_password'])
|
||||||
|
|
||||||
# NOTE(vish): If we have another lun on the same target, we may
|
|
||||||
# have a duplicate login
|
|
||||||
self._run_iscsiadm(iscsi_properties, ("--login",),
|
|
||||||
check_exit_code=[0, 255])
|
|
||||||
|
|
||||||
self._iscsiadm_update(iscsi_properties, "node.startup", "automatic")
|
|
||||||
|
|
||||||
host_device = ("/dev/disk/by-path/ip-%s-iscsi-%s-lun-%s" %
|
host_device = ("/dev/disk/by-path/ip-%s-iscsi-%s-lun-%s" %
|
||||||
(iscsi_properties['target_portal'],
|
(iscsi_properties['target_portal'],
|
||||||
iscsi_properties['target_iqn'],
|
iscsi_properties['target_iqn'],
|
||||||
iscsi_properties.get('target_lun', 0)))
|
iscsi_properties.get('target_lun', 0)))
|
||||||
|
|
||||||
tries = 0
|
out = self._run_iscsiadm_bare(["-m", "session"],
|
||||||
while not os.path.exists(host_device):
|
run_as_root=True,
|
||||||
if tries >= self.configuration.num_iscsi_scan_tries:
|
check_exit_code=[0, 1, 21])[0] or ""
|
||||||
raise exception.CinderException(
|
|
||||||
_("iSCSI device not found at %s") % (host_device))
|
|
||||||
|
|
||||||
LOG.warn(_("ISCSI volume not yet found at: %(host_device)s. "
|
portals = [{'portal': p.split(" ")[2], 'iqn': p.split(" ")[3]}
|
||||||
"Will rescan & retry. Try number: %(tries)s") %
|
for p in out.splitlines() if p.startswith("tcp:")]
|
||||||
locals())
|
|
||||||
|
|
||||||
# The rescan isn't documented as being necessary(?), but it helps
|
stripped_portal = iscsi_properties['target_portal'].split(",")[0]
|
||||||
self._run_iscsiadm(iscsi_properties, ("--rescan",))
|
length_iqn = [s for s in portals
|
||||||
|
if stripped_portal ==
|
||||||
|
s['portal'].split(",")[0] and
|
||||||
|
s['iqn'] == iscsi_properties['target_iqn']]
|
||||||
|
if len(portals) == 0 or len(length_iqn) == 0:
|
||||||
|
try:
|
||||||
|
self._run_iscsiadm(iscsi_properties, ("--login",),
|
||||||
|
check_exit_code=[0, 255])
|
||||||
|
except exception.ProcessExecutionError as err:
|
||||||
|
if err.exit_code in [15]:
|
||||||
|
self._iscsiadm_update(iscsi_properties,
|
||||||
|
"node.startup",
|
||||||
|
"automatic")
|
||||||
|
return iscsi_properties, host_device
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
tries = tries + 1
|
self._iscsiadm_update(iscsi_properties,
|
||||||
if not os.path.exists(host_device):
|
"node.startup", "automatic")
|
||||||
time.sleep(tries ** 2)
|
|
||||||
|
|
||||||
if tries != 0:
|
tries = 0
|
||||||
LOG.debug(_("Found iSCSI node %(host_device)s "
|
while not os.path.exists(host_device):
|
||||||
"(after %(tries)s rescans)") %
|
if tries >= self.configuration.num_iscsi_scan_tries:
|
||||||
locals())
|
raise exception.CinderException(_("iSCSI device "
|
||||||
|
"not found "
|
||||||
|
"at %s") % (host_device))
|
||||||
|
|
||||||
|
LOG.warn(_("ISCSI volume not yet found at: %(host_device)s. "
|
||||||
|
"Will rescan & retry. Try number: %(tries)s.") %
|
||||||
|
{'host_device': host_device, 'tries': tries})
|
||||||
|
|
||||||
|
# The rescan isn't documented as being necessary(?),
|
||||||
|
# but it helps
|
||||||
|
self._run_iscsiadm(iscsi_properties, ("--rescan",))
|
||||||
|
|
||||||
|
tries = tries + 1
|
||||||
|
if not os.path.exists(host_device):
|
||||||
|
time.sleep(tries ** 2)
|
||||||
|
|
||||||
|
if tries != 0:
|
||||||
|
LOG.debug(_("Found iSCSI node %(host_device)s "
|
||||||
|
"(after %(tries)s rescans).") %
|
||||||
|
{'host_device': host_device,
|
||||||
|
'tries': tries})
|
||||||
|
|
||||||
|
if not self._check_valid_device(host_device):
|
||||||
|
raise exception.DeviceUnavailable(path=host_device,
|
||||||
|
reason=(_("Unable to access "
|
||||||
|
"the backend storage "
|
||||||
|
"via the path "
|
||||||
|
"%(path)s.") %
|
||||||
|
{'path': host_device}))
|
||||||
return iscsi_properties, host_device
|
return iscsi_properties, host_device
|
||||||
|
|
||||||
def get_volume_stats(self, refresh=False):
|
def get_volume_stats(self, refresh=False):
|
||||||
|
@ -615,7 +615,7 @@ class VolumeManager(manager.SchedulerDependentManager):
|
|||||||
"error: %(error)s") % {'volume_id': volume_id,
|
"error: %(error)s") % {'volume_id': volume_id,
|
||||||
'error': ex.stderr})
|
'error': ex.stderr})
|
||||||
raise exception.ImageCopyFailure(reason=ex.stderr)
|
raise exception.ImageCopyFailure(reason=ex.stderr)
|
||||||
except exception.ImageUnacceptable as ex:
|
except Exception as ex:
|
||||||
LOG.error(_("Failed to copy image to volume: %(volume_id)s, "
|
LOG.error(_("Failed to copy image to volume: %(volume_id)s, "
|
||||||
"error: %(error)s") % {'volume_id': volume_id,
|
"error: %(error)s") % {'volume_id': volume_id,
|
||||||
'error': ex})
|
'error': ex})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user