[coreycb,r=james-page] Add option to remove missing physical volumes from volume group.

This commit is contained in:
James Page 2014-10-14 16:59:34 +01:00
commit ea938dbdb5
5 changed files with 63 additions and 16 deletions

View File

@ -27,12 +27,16 @@ options:
description: | description: |
The block devices on which to create LVM volume group. The block devices on which to create LVM volume group.
May also be set to None for deployments that will not need local May be set to None for deployments that will not need local
storage (eg, Ceph/RBD-backed volumes). storage (eg, Ceph/RBD-backed volumes).
This can also be a space delimited list of block devices to attempt This can also be a space delimited list of block devices to attempt
to use in the cinder LVM volume group - each block device detected to use in the cinder LVM volume group - each block device detected
will be added to the available physical volumes in the volume group. will be added to the available physical volumes in the volume group.
May be set to the path and size of a local file
(/path/to/file.img|$sizeG), which will be created and used as a
loopback device (for testing only). $sizeG defaults to 5G
ceph-osd-replication-count: ceph-osd-replication-count:
default: 3 default: 3
type: int type: int
@ -51,8 +55,14 @@ options:
default: "false" default: "false"
type: string type: string
description: | description: |
If true, charm will attempt to overwrite block devices containin If true, charm will attempt to overwrite block devices containing
previous filesystems or LVM, assuming it is not in use. previous filesystems or LVM, assuming it is not in use.
remove-missing:
default: False
type: boolean
description: |
If True, charm will attempt to remove missing physical volumes from
volume group, if logical volumes are not allocated on them.
database-user: database-user:
default: cinder default: cinder
type: string type: string

View File

@ -105,7 +105,8 @@ def config_changed():
block_devices = conf['block-device'].split() block_devices = conf['block-device'].split()
configure_lvm_storage(block_devices, configure_lvm_storage(block_devices,
conf['volume-group'], conf['volume-group'],
conf['overwrite'] in ['true', 'True', True]) conf['overwrite'] in ['true', 'True', True],
conf['remove-missing'])
if openstack_upgrade_available('cinder-common'): if openstack_upgrade_available('cinder-common'):
do_openstack_upgrade(configs=CONFIGS) do_openstack_upgrade(configs=CONFIGS)

View File

@ -267,9 +267,19 @@ def services():
return list(set(_services)) return list(set(_services))
def reduce_lvm_volume_group_missing(volume_group):
'''
Remove all missing physical volumes from the volume group, if there
are no logical volumes allocated on them.
:param volume_group: str: Name of volume group to reduce.
'''
subprocess.check_call(['vgreduce', '--removemissing', volume_group])
def extend_lvm_volume_group(volume_group, block_device): def extend_lvm_volume_group(volume_group, block_device):
''' '''
Extend and LVM volume group onto a given block device. Extend an LVM volume group onto a given block device.
Assumes block device has already been initialized as an LVM PV. Assumes block device has already been initialized as an LVM PV.
@ -279,13 +289,16 @@ def extend_lvm_volume_group(volume_group, block_device):
subprocess.check_call(['vgextend', volume_group, block_device]) subprocess.check_call(['vgextend', volume_group, block_device])
def configure_lvm_storage(block_devices, volume_group, overwrite=False): def configure_lvm_storage(block_devices, volume_group, overwrite=False,
remove_missing=False):
''' Configure LVM storage on the list of block devices provided ''' Configure LVM storage on the list of block devices provided
:param block_devices: list: List of whitelisted block devices to detect :param block_devices: list: List of whitelisted block devices to detect
and use if found and use if found
:param overwrite: bool: Scrub any existing block data if block device is :param overwrite: bool: Scrub any existing block data if block device is
not already in-use not already in-use
:param remove_missing: bool: Remove missing physical volumes from volume
group if logical volume not allocated on them
''' '''
devices = [] devices = []
for block_device in block_devices: for block_device in block_devices:
@ -320,6 +333,10 @@ def configure_lvm_storage(block_devices, volume_group, overwrite=False):
create_lvm_volume_group(volume_group, new_devices[0]) create_lvm_volume_group(volume_group, new_devices[0])
new_devices.remove(new_devices[0]) new_devices.remove(new_devices[0])
# Remove missing physical volumes from volume group
if remove_missing:
reduce_lvm_volume_group_missing(volume_group)
if len(new_devices) > 0: if len(new_devices) > 0:
# Extend the volume group as required # Extend the volume group as required
for new_device in new_devices: for new_device in new_devices:

View File

@ -115,7 +115,7 @@ class TestChangedHooks(CharmTestCase):
self.assertTrue(conf_https.called) self.assertTrue(conf_https.called)
self.configure_lvm_storage.assert_called_with(['sdb'], self.configure_lvm_storage.assert_called_with(['sdb'],
'cinder-volumes', 'cinder-volumes',
False) False, False)
@patch.object(hooks, 'configure_https') @patch.object(hooks, 'configure_https')
def test_config_changed_block_devices(self, conf_https): def test_config_changed_block_devices(self, conf_https):
@ -124,13 +124,14 @@ class TestChangedHooks(CharmTestCase):
self.test_config.set('block-device', 'sdb /dev/sdc sde') self.test_config.set('block-device', 'sdb /dev/sdc sde')
self.test_config.set('volume-group', 'cinder-new') self.test_config.set('volume-group', 'cinder-new')
self.test_config.set('overwrite', 'True') self.test_config.set('overwrite', 'True')
self.test_config.set('remove-missing', True)
hooks.hooks.execute(['hooks/config-changed']) hooks.hooks.execute(['hooks/config-changed'])
self.assertTrue(self.CONFIGS.write_all.called) self.assertTrue(self.CONFIGS.write_all.called)
self.assertTrue(conf_https.called) self.assertTrue(conf_https.called)
self.configure_lvm_storage.assert_called_with( self.configure_lvm_storage.assert_called_with(
['sdb', '/dev/sdc', 'sde'], ['sdb', '/dev/sdc', 'sde'],
'cinder-new', 'cinder-new',
True) True, True)
@patch.object(hooks, 'configure_https') @patch.object(hooks, 'configure_https')
def test_config_changed_upgrade_available(self, conf_https): def test_config_changed_upgrade_available(self, conf_https):

View File

@ -211,11 +211,13 @@ class TestCinderUtils(CharmTestCase):
('/mnt/loop0', cinder_utils.DEFAULT_LOOPBACK_SIZE)) ('/mnt/loop0', cinder_utils.DEFAULT_LOOPBACK_SIZE))
@patch.object(cinder_utils, 'clean_storage') @patch.object(cinder_utils, 'clean_storage')
@patch.object(cinder_utils, 'reduce_lvm_volume_group_missing')
@patch.object(cinder_utils, 'extend_lvm_volume_group') @patch.object(cinder_utils, 'extend_lvm_volume_group')
def test_configure_lvm_storage(self, extend_lvm, clean_storage): def test_configure_lvm_storage(self, extend_lvm, reduce_lvm,
clean_storage):
devices = ['/dev/vdb', '/dev/vdc'] devices = ['/dev/vdb', '/dev/vdc']
self.is_lvm_physical_volume.return_value = False self.is_lvm_physical_volume.return_value = False
cinder_utils.configure_lvm_storage(devices, 'test', True) cinder_utils.configure_lvm_storage(devices, 'test', True, True)
clean_storage.assert_has_calls( clean_storage.assert_has_calls(
[call('/dev/vdb'), [call('/dev/vdb'),
call('/dev/vdc')] call('/dev/vdc')]
@ -225,24 +227,29 @@ class TestCinderUtils(CharmTestCase):
call('/dev/vdc')] call('/dev/vdc')]
) )
self.create_lvm_volume_group.assert_called_with('test', '/dev/vdb') self.create_lvm_volume_group.assert_called_with('test', '/dev/vdb')
reduce_lvm.assert_called_with('test')
extend_lvm.assert_called_with('test', '/dev/vdc') extend_lvm.assert_called_with('test', '/dev/vdc')
@patch.object(cinder_utils, 'clean_storage') @patch.object(cinder_utils, 'clean_storage')
@patch.object(cinder_utils, 'reduce_lvm_volume_group_missing')
@patch.object(cinder_utils, 'extend_lvm_volume_group') @patch.object(cinder_utils, 'extend_lvm_volume_group')
def test_configure_lvm_storage_loopback(self, extend_lvm, clean_storage): def test_configure_lvm_storage_loopback(self, extend_lvm, reduce_lvm,
clean_storage):
devices = ['/mnt/loop0|10'] devices = ['/mnt/loop0|10']
self.ensure_loopback_device.return_value = '/dev/loop0' self.ensure_loopback_device.return_value = '/dev/loop0'
self.is_lvm_physical_volume.return_value = False self.is_lvm_physical_volume.return_value = False
cinder_utils.configure_lvm_storage(devices, 'test', True) cinder_utils.configure_lvm_storage(devices, 'test', True, True)
clean_storage.assert_called_with('/dev/loop0') clean_storage.assert_called_with('/dev/loop0')
self.ensure_loopback_device.assert_called_with('/mnt/loop0', '10') self.ensure_loopback_device.assert_called_with('/mnt/loop0', '10')
self.create_lvm_physical_volume.assert_called_with('/dev/loop0') self.create_lvm_physical_volume.assert_called_with('/dev/loop0')
self.create_lvm_volume_group.assert_called_with('test', '/dev/loop0') self.create_lvm_volume_group.assert_called_with('test', '/dev/loop0')
reduce_lvm.assert_called_with('test')
self.assertFalse(extend_lvm.called) self.assertFalse(extend_lvm.called)
@patch.object(cinder_utils, 'clean_storage') @patch.object(cinder_utils, 'clean_storage')
@patch.object(cinder_utils, 'reduce_lvm_volume_group_missing')
@patch.object(cinder_utils, 'extend_lvm_volume_group') @patch.object(cinder_utils, 'extend_lvm_volume_group')
def test_configure_lvm_storage_existing_vg(self, extend_lvm, def test_configure_lvm_storage_existing_vg(self, extend_lvm, reduce_lvm,
clean_storage): clean_storage):
def pv_lookup(device): def pv_lookup(device):
devices = { devices = {
@ -260,19 +267,21 @@ class TestCinderUtils(CharmTestCase):
devices = ['/dev/vdb', '/dev/vdc'] devices = ['/dev/vdb', '/dev/vdc']
self.is_lvm_physical_volume.side_effect = pv_lookup self.is_lvm_physical_volume.side_effect = pv_lookup
self.list_lvm_volume_group.side_effect = vg_lookup self.list_lvm_volume_group.side_effect = vg_lookup
cinder_utils.configure_lvm_storage(devices, 'test', True) cinder_utils.configure_lvm_storage(devices, 'test', True, True)
clean_storage.assert_has_calls( clean_storage.assert_has_calls(
[call('/dev/vdc')] [call('/dev/vdc')]
) )
self.create_lvm_physical_volume.assert_has_calls( self.create_lvm_physical_volume.assert_has_calls(
[call('/dev/vdc')] [call('/dev/vdc')]
) )
reduce_lvm.assert_called_with('test')
extend_lvm.assert_called_with('test', '/dev/vdc') extend_lvm.assert_called_with('test', '/dev/vdc')
self.assertFalse(self.create_lvm_volume_group.called) self.assertFalse(self.create_lvm_volume_group.called)
@patch.object(cinder_utils, 'clean_storage') @patch.object(cinder_utils, 'clean_storage')
@patch.object(cinder_utils, 'reduce_lvm_volume_group_missing')
@patch.object(cinder_utils, 'extend_lvm_volume_group') @patch.object(cinder_utils, 'extend_lvm_volume_group')
def test_configure_lvm_storage_different_vg(self, extend_lvm, def test_configure_lvm_storage_different_vg(self, extend_lvm, reduce_lvm,
clean_storage): clean_storage):
def pv_lookup(device): def pv_lookup(device):
devices = { devices = {
@ -290,15 +299,18 @@ class TestCinderUtils(CharmTestCase):
devices = ['/dev/vdb', '/dev/vdc'] devices = ['/dev/vdb', '/dev/vdc']
self.is_lvm_physical_volume.side_effect = pv_lookup self.is_lvm_physical_volume.side_effect = pv_lookup
self.list_lvm_volume_group.side_effect = vg_lookup self.list_lvm_volume_group.side_effect = vg_lookup
cinder_utils.configure_lvm_storage(devices, 'test', True) cinder_utils.configure_lvm_storage(devices, 'test', True, True)
clean_storage.assert_called_with('/dev/vdc') clean_storage.assert_called_with('/dev/vdc')
self.create_lvm_physical_volume.assert_called_with('/dev/vdc') self.create_lvm_physical_volume.assert_called_with('/dev/vdc')
reduce_lvm.assert_called_with('test')
extend_lvm.assert_called_with('test', '/dev/vdc') extend_lvm.assert_called_with('test', '/dev/vdc')
self.assertFalse(self.create_lvm_volume_group.called) self.assertFalse(self.create_lvm_volume_group.called)
@patch.object(cinder_utils, 'clean_storage') @patch.object(cinder_utils, 'clean_storage')
@patch.object(cinder_utils, 'reduce_lvm_volume_group_missing')
@patch.object(cinder_utils, 'extend_lvm_volume_group') @patch.object(cinder_utils, 'extend_lvm_volume_group')
def test_configure_lvm_storage_different_vg_ignore(self, extend_lvm, def test_configure_lvm_storage_different_vg_ignore(self, extend_lvm,
reduce_lvm,
clean_storage): clean_storage):
def pv_lookup(device): def pv_lookup(device):
devices = { devices = {
@ -316,12 +328,18 @@ class TestCinderUtils(CharmTestCase):
devices = ['/dev/vdb', '/dev/vdc'] devices = ['/dev/vdb', '/dev/vdc']
self.is_lvm_physical_volume.side_effect = pv_lookup self.is_lvm_physical_volume.side_effect = pv_lookup
self.list_lvm_volume_group.side_effect = vg_lookup self.list_lvm_volume_group.side_effect = vg_lookup
cinder_utils.configure_lvm_storage(devices, 'test', False) cinder_utils.configure_lvm_storage(devices, 'test', False, False)
self.assertFalse(clean_storage.called) self.assertFalse(clean_storage.called)
self.assertFalse(self.create_lvm_physical_volume.called) self.assertFalse(self.create_lvm_physical_volume.called)
self.assertFalse(reduce_lvm.called)
self.assertFalse(extend_lvm.called) self.assertFalse(extend_lvm.called)
self.assertFalse(self.create_lvm_volume_group.called) self.assertFalse(self.create_lvm_volume_group.called)
@patch('subprocess.check_call')
def test_reduce_lvm_volume_group_missing(self, _call):
cinder_utils.reduce_lvm_volume_group_missing('test')
_call.assert_called_with(['vgreduce', '--removemissing', 'test'])
@patch('subprocess.check_call') @patch('subprocess.check_call')
def test_extend_lvm_volume_group(self, _call): def test_extend_lvm_volume_group(self, _call):
cinder_utils.extend_lvm_volume_group('test', '/dev/sdb') cinder_utils.extend_lvm_volume_group('test', '/dev/sdb')