charm-swift-storage/lib/misc_utils.py
Chris Glass 30c3fb9353 Resolve links before using path as block device
If the charm code is passed symlinks to block devices (as is often the
case with newer MAAS substrate versions), resolve links before
attempting to use the block device for storage.

Charmhelpers were updated as well.

Testing done:

- Unit tests pass
- Tests pass
- Multiple Openstack Autopilot deployments pass

Change-Id: If966239502d0752c86e46f3f0aee96f43828aa08
Closes-Bug: 1577408
Signed-off-by: Chris Glass <chris.glass@canonical.com>
2016-05-06 15:25:34 +00:00

122 lines
3.1 KiB
Python

import os
from charmhelpers.contrib.storage.linux.utils import (
is_block_device,
zap_disk,
)
from charmhelpers.contrib.storage.linux.loopback import (
ensure_loopback_device,
)
from charmhelpers.contrib.storage.linux.lvm import (
deactivate_lvm_volume_group,
is_lvm_physical_volume,
remove_lvm_physical_volume,
)
from charmhelpers.core.host import (
mounts,
umount,
restart_on_change,
)
from charmhelpers.core.hookenv import (
log,
INFO,
ERROR,
)
from charmhelpers.core.unitdata import (
HookData,
kv,
)
DEFAULT_LOOPBACK_SIZE = '5G'
def ensure_block_device(block_device):
'''
Confirm block_device, create as loopback if necessary.
:param block_device: str: Full path of block device to ensure.
:returns: str: Full path of ensured block device.
'''
if block_device.startswith("/"):
# Resolve non-relative link(s) if device is a symlink to a real device.
# This fixes/prevents bug #1577408.
real_device = os.path.realpath(block_device)
if real_device != block_device:
log('Block device "{}" is a symlink to "{}". Using "{}".'.format(
block_device, real_device, real_device), level=INFO)
block_device = real_device
_none = ['None', 'none', None]
if (block_device in _none):
log('prepare_storage(): Missing required input: '
'block_device=%s.' % block_device, level=ERROR)
raise
if block_device.startswith('/dev/'):
bdev = block_device
elif block_device.startswith('/'):
_bd = block_device.split('|')
if len(_bd) == 2:
bdev, size = _bd
else:
bdev = block_device
size = DEFAULT_LOOPBACK_SIZE
bdev = ensure_loopback_device(bdev, size)
else:
bdev = '/dev/%s' % block_device
if not is_block_device(bdev):
log('Failed to locate valid block device at %s' % bdev, level=ERROR)
# ignore missing block devices
return
return bdev
def clean_storage(block_device):
'''
Ensures a block device is clean. That is:
- unmounted
- any lvm volume groups are deactivated
- any lvm physical device signatures removed
- partition table wiped
:param block_device: str: Full path to block device to clean.
'''
for mp, d in mounts():
if d == block_device:
log('clean_storage(): Found %s mounted @ %s, unmounting.' %
(d, mp), level=INFO)
umount(mp, persist=True)
if is_lvm_physical_volume(block_device):
deactivate_lvm_volume_group(block_device)
remove_lvm_physical_volume(block_device)
else:
zap_disk(block_device)
def is_paused():
"""Is the unit paused?"""
with HookData()():
if kv().get('unit-paused'):
return True
else:
return False
def pause_aware_restart_on_change(restart_map):
"""Avoids restarting services if config changes when unit is paused."""
def wrapper(f):
if is_paused():
return f
else:
return restart_on_change(restart_map)(f)
return wrapper