82
85
def check_for_setup_error(self):
83
86
"""Returns an error if prerequisites aren't met"""
84
out, err = self._execute("sudo vgs --noheadings -o name")
87
out, err = self._execute('sudo', 'vgs', '--noheadings', '-o', 'name')
85
88
volume_groups = out.split()
86
89
if not FLAGS.volume_group in volume_groups:
87
90
raise exception.Error(_("volume group %s doesn't exist")
88
91
% FLAGS.volume_group)
90
93
def create_volume(self, volume):
91
"""Creates a logical volume."""
94
"""Creates a logical volume. Can optionally return a Dictionary of
95
changes to the volume object to be persisted."""
92
96
if int(volume['size']) == 0:
95
99
sizestr = '%sG' % volume['size']
96
self._try_execute("sudo lvcreate -L %s -n %s %s" %
100
self._try_execute('sudo', 'lvcreate', '-L', sizestr, '-n',
101
104
def delete_volume(self, volume):
102
105
"""Deletes a logical volume."""
104
self._try_execute("sudo lvdisplay %s/%s" %
107
self._try_execute('sudo', 'lvdisplay',
105
109
(FLAGS.volume_group,
107
111
except Exception as e:
108
112
# If the volume isn't present, then don't attempt to delete
111
self._try_execute("sudo lvremove -f %s/%s" %
115
self._try_execute('sudo', 'lvremove', '-f', "%s/%s" %
112
116
(FLAGS.volume_group,
284
305
# ietadm show will exit with an error
285
306
# this export has already been removed
286
self._execute("sudo ietadm --op show --tid=%s " % iscsi_target)
307
self._execute('sudo', 'ietadm', '--op', 'show',
308
'--tid=%s' % iscsi_target)
287
309
except Exception as e:
288
310
LOG.info(_("Skipping remove_export. No iscsi_target " +
289
311
"is presently exported for volume: %d"), volume['id'])
292
self._execute("sudo ietadm --op delete --tid=%s "
293
"--lun=0" % iscsi_target)
294
self._execute("sudo ietadm --op delete --tid=%s" %
297
def _get_name_and_portal(self, volume_name, host):
298
"""Gets iscsi name and portal from volume name and host."""
299
(out, _err) = self._execute("sudo iscsiadm -m discovery -t "
300
"sendtargets -p %s" % host)
314
self._execute('sudo', 'ietadm', '--op', 'delete',
315
'--tid=%s' % iscsi_target,
317
self._execute('sudo', 'ietadm', '--op', 'delete',
318
'--tid=%s' % iscsi_target)
320
def _do_iscsi_discovery(self, volume):
321
#TODO(justinsb): Deprecate discovery and use stored info
322
#NOTE(justinsb): Discovery won't work with CHAP-secured targets (?)
323
LOG.warn(_("ISCSI provider_location not stored, using discovery"))
325
volume_name = volume['name']
327
(out, _err) = self._execute('sudo', 'iscsiadm', '-m', 'discovery',
328
'-t', 'sendtargets', '-p', volume['host'])
301
329
for target in out.splitlines():
302
330
if FLAGS.iscsi_ip_prefix in target and volume_name in target:
303
(location, _sep, iscsi_name) = target.partition(" ")
305
iscsi_portal = location.split(",")[0]
306
return (iscsi_name, iscsi_portal)
334
def _get_iscsi_properties(self, volume):
335
"""Gets iscsi configuration
337
We ideally get saved information in the volume entity, but fall back
338
to discovery if need be. Discovery may be completely removed in future
341
:target_discovered: boolean indicating whether discovery was used
343
:target_iqn: the IQN of the iSCSI target
345
:target_portal: the portal of the iSCSI target
347
:auth_method:, :auth_username:, :auth_password:
349
the authentication details. Right now, either auth_method is not
350
present meaning no authentication, or auth_method == `CHAP`
351
meaning use CHAP with the specified credentials.
356
location = volume['provider_location']
359
# provider_location is the same format as iSCSI discovery output
360
properties['target_discovered'] = False
362
location = self._do_iscsi_discovery(volume)
365
raise exception.Error(_("Could not find iSCSI export "
369
LOG.debug(_("ISCSI Discovery: Found %s") % (location))
370
properties['target_discovered'] = True
372
(iscsi_target, _sep, iscsi_name) = location.partition(" ")
374
iscsi_portal = iscsi_target.split(",")[0]
376
properties['target_iqn'] = iscsi_name
377
properties['target_portal'] = iscsi_portal
379
auth = volume['provider_auth']
382
(auth_method, auth_username, auth_secret) = auth.split()
384
properties['auth_method'] = auth_method
385
properties['auth_username'] = auth_username
386
properties['auth_password'] = auth_secret
390
def _run_iscsiadm(self, iscsi_properties, iscsi_command):
391
command = ("sudo iscsiadm -m node -T %s -p %s %s" %
392
(iscsi_properties['target_iqn'],
393
iscsi_properties['target_portal'],
395
(out, err) = self._execute(command)
396
LOG.debug("iscsiadm %s: stdout=%s stderr=%s" %
397
(iscsi_command, out, err))
400
def _iscsiadm_update(self, iscsi_properties, property_key, property_value):
401
iscsi_command = ("--op update -n %s -v %s" %
402
(property_key, property_value))
403
return self._run_iscsiadm(iscsi_properties, iscsi_command)
308
405
def discover_volume(self, volume):
309
406
"""Discover volume on a remote host."""
310
iscsi_name, iscsi_portal = self._get_name_and_portal(volume['name'],
312
self._execute("sudo iscsiadm -m node -T %s -p %s --login" %
313
(iscsi_name, iscsi_portal))
314
self._execute("sudo iscsiadm -m node -T %s -p %s --op update "
315
"-n node.startup -v automatic" %
316
(iscsi_name, iscsi_portal))
317
return "/dev/disk/by-path/ip-%s-iscsi-%s-lun-0" % (iscsi_portal,
407
iscsi_properties = self._get_iscsi_properties(volume)
409
if not iscsi_properties['target_discovered']:
410
self._run_iscsiadm(iscsi_properties, "--op new")
412
if iscsi_properties.get('auth_method'):
413
self._iscsiadm_update(iscsi_properties,
414
"node.session.auth.authmethod",
415
iscsi_properties['auth_method'])
416
self._iscsiadm_update(iscsi_properties,
417
"node.session.auth.username",
418
iscsi_properties['auth_username'])
419
self._iscsiadm_update(iscsi_properties,
420
"node.session.auth.password",
421
iscsi_properties['auth_password'])
423
self._run_iscsiadm(iscsi_properties, "--login")
425
self._iscsiadm_update(iscsi_properties, "node.startup", "automatic")
427
mount_device = ("/dev/disk/by-path/ip-%s-iscsi-%s-lun-0" %
428
(iscsi_properties['target_portal'],
429
iscsi_properties['target_iqn']))
431
# The /dev/disk/by-path/... node is not always present immediately
432
# TODO(justinsb): This retry-with-delay is a pattern, move to utils?
434
while not os.path.exists(mount_device):
435
if tries >= FLAGS.num_iscsi_scan_tries:
436
raise exception.Error(_("iSCSI device not found at %s") %
439
LOG.warn(_("ISCSI volume not yet found at: %(mount_device)s. "
440
"Will rescan & retry. Try number: %(tries)s") %
443
# The rescan isn't documented as being necessary(?), but it helps
444
self._run_iscsiadm(iscsi_properties, "--rescan")
447
if not os.path.exists(mount_device):
448
time.sleep(tries ** 2)
451
LOG.debug(_("Found iSCSI node %(mount_device)s "
452
"(after %(tries)s rescans)") %
320
457
def undiscover_volume(self, volume):
321
458
"""Undiscover volume on a remote host."""
322
iscsi_name, iscsi_portal = self._get_name_and_portal(volume['name'],
324
self._execute("sudo iscsiadm -m node -T %s -p %s --op update "
325
"-n node.startup -v manual" %
326
(iscsi_name, iscsi_portal))
327
self._execute("sudo iscsiadm -m node -T %s -p %s --logout " %
328
(iscsi_name, iscsi_portal))
329
self._execute("sudo iscsiadm -m node --op delete "
330
"--targetname %s" % iscsi_name)
459
iscsi_properties = self._get_iscsi_properties(volume)
460
self._iscsiadm_update(iscsi_properties, "node.startup", "manual")
461
self._run_iscsiadm(iscsi_properties, "--logout")
462
self._run_iscsiadm(iscsi_properties, "--op delete")
333
465
class FakeISCSIDriver(ISCSIDriver):