~powersj/curtin/add-bionic

« back to all changes in this revision

Viewing changes to curtin/block/iscsi.py

iscsi: use curtin storage config to disconnect iscsi targets

When curtin disconnects from iscsi targets after an install, it does so
after unmounting the targets, however it passed the target dir into the
iscsi disconnect code which then silently failed to find any
session configurations.

We've been lucky as the existing shutdown iscsi service has been
logging out of sessions defined on the host; however on recent
Artful systems, the open-iscsi service is not restarted after
populating /etc/iscsi/nodes directory and since it is not running
in the ephemeral environment the service will not automatically
stop the iscsi devices, causing a hang on shutdown.

To resolve this parse the curtin storage config for iscsi disks,
construct IscsiDisk objects and then call their disconnect() method.
In addition to this logic fix, this branch contains:

- adds a missing Artful LVM Iscsi vmtest class
- Updated logging messages in iscsi paths, including
  a message when iscsi_disconnect doesn't find the
  /etc/iscsi/nodes directory.
- Updated IscsiDisk disconnect with similar logging

Show diffs side-by-side

added added

removed removed

Lines of Context:
238
238
    return _ISCSI_DISKS
239
239
 
240
240
 
 
241
def get_iscsi_disks_from_config(cfg):
 
242
    """Parse a curtin storage config and return a list
 
243
       of iscsi disk objects for each configuration present
 
244
    """
 
245
    if not cfg:
 
246
        cfg = {}
 
247
 
 
248
    sconfig = cfg.get('storage', {}).get('config', {})
 
249
    if not sconfig:
 
250
        LOG.warning('Configuration dictionary did not contain'
 
251
                    ' a storage configuration')
 
252
        return []
 
253
 
 
254
    # Construct IscsiDisk objects for each iscsi volume present
 
255
    iscsi_disks = [IscsiDisk(disk['path']) for disk in sconfig
 
256
                   if disk['type'] == 'disk' and
 
257
                   disk.get('path', "").startswith('iscsi:')]
 
258
    LOG.debug('Found %s iscsi disks in storage config', len(iscsi_disks))
 
259
    return iscsi_disks
 
260
 
 
261
 
241
262
def disconnect_target_disks(target_root_path=None):
242
263
    target_nodes_path = util.target_path(target_root_path, '/etc/iscsi/nodes')
243
264
    fails = []
244
265
    if os.path.isdir(target_nodes_path):
245
266
        for target in os.listdir(target_nodes_path):
 
267
            if target not in iscsiadm_sessions():
 
268
                LOG.debug('iscsi target %s not active, skipping', target)
 
269
                continue
246
270
            # conn is "host,port,lun"
247
271
            for conn in os.listdir(
248
272
                            os.path.sep.join([target_nodes_path, target])):
254
278
                    fails.append(target)
255
279
                    LOG.warn("Unable to logout of iSCSI target %s: %s",
256
280
                             target, e)
257
 
 
 
281
    else:
 
282
        LOG.warning('Skipping disconnect: failed to find iscsi nodes path: %s',
 
283
                    target_nodes_path)
258
284
    if fails:
259
285
        raise RuntimeError(
260
286
            "Unable to logout of iSCSI targets: %s" % ', '.join(fails))
414
440
 
415
441
    def disconnect(self):
416
442
        if self.target not in iscsiadm_sessions():
 
443
            LOG.warning('Iscsi target %s not in active iscsi sessions',
 
444
                        self.target)
417
445
            return
418
446
 
419
 
        util.subp(['sync'])
420
 
        iscsiadm_logout(self.target, self.portal)
 
447
        try:
 
448
            util.subp(['sync'])
 
449
            iscsiadm_logout(self.target, self.portal)
 
450
        except util.ProcessExecutionError as e:
 
451
            LOG.warn("Unable to logout of iSCSI target %s from portal %s: %s",
 
452
                     self.target, self.portal, e)
421
453
 
422
454
# vi: ts=4 expandtab syntax=python