Wiping out md and lvm devices before partitioning

This change is intended to wipe out md and lvm
metadata which could remain on a disk. We do it
in 3 steps
1) we wipe out all md and lvm devices
2) we put 1M of zeros at the beginning of every new partition
3) we again wipe out all md and lvm devices

For details see comments in fuel_agent/fuel_agent/manager.py

Implements: blueprint image-based-provisioning
Change-Id: I55f18bd22f5609da18e33f1b238176c3bb95d327
This commit is contained in:
Vladimir Kozhukalov
2014-10-13 12:15:30 +04:00
parent 4d6d91ac66
commit afd0bf6988
6 changed files with 96 additions and 27 deletions

View File

@@ -13,6 +13,7 @@
# limitations under the License.
import os
import time
from oslo.config import cfg
@@ -68,15 +69,53 @@ class Manager(object):
self.image_scheme = self.driver.image_scheme(self.partition_scheme)
def do_partitioning(self):
# If disks are not wiped out at all, it is likely they contain lvm
# and md metadata which will prevent re-creating a partition table
# with 'device is busy' error.
mu.mdclean_all()
lu.lvremove_all()
lu.vgremove_all()
lu.pvremove_all()
for parted in self.partition_scheme.parteds:
pu.make_label(parted.name, parted.label)
for prt in parted.partitions:
pu.make_partition(prt.device, prt.begin, prt.end, prt.type)
# We wipe out the beginning of every new partition
# right after creating it. It allows us to avoid possible
# interactive dialog if some data (metadata or file system)
# present on this new partition.
timestamp = time.time()
while 1:
if time.time() > timestamp + 30:
raise errors.PartitionNotFoundError(
'Error while wiping data on partition %s.'
'Partition not found' % prt.name)
try:
utils.execute('test', '-e', prt.name,
check_exit_code=[0])
except errors.ProcessExecutionError:
time.sleep(1)
continue
else:
utils.execute('dd', 'if=/dev/zero', 'bs=1M', 'count=1',
'of=%s' % prt.name, check_exit_code=[0])
break
for flag in prt.flags:
pu.set_partition_flag(prt.device, prt.count, flag)
if prt.guid:
pu.set_gpt_type(prt.device, prt.count, prt.guid)
# If one creates partitions with the same boundaries as last time,
# there might be md and lvm metadata on those partitions. To prevent
# failing of creating md and lvm devices we need to make sure
# unused metadata are wiped out.
mu.mdclean_all()
lu.lvremove_all()
lu.vgremove_all()
lu.pvremove_all()
# creating meta disks
for md in self.partition_scheme.mds:
mu.mdcreate(md.name, md.level, *md.devices)

View File

@@ -289,16 +289,17 @@ class TestLvmUtils(test_base.BaseTestCase):
@mock.patch.object(lu, 'lvdisplay')
@mock.patch.object(utils, 'execute')
def test_lvremove_ok(self, mock_exec, mock_lvdisplay):
mock_lvdisplay.return_value = [{'name': 'lvname'}, {'name': 'some'}]
lu.lvremove('lvname')
mock_exec.assert_called_once_with('lvremove', '-f', 'lvname',
mock_lvdisplay.return_value = [{'path': '/dev/vg/lv'},
{'path': '/dev/vg2/lv2'}]
lu.lvremove('/dev/vg/lv')
mock_exec.assert_called_once_with('lvremove', '-f', '/dev/vg/lv',
check_exit_code=[0])
@mock.patch.object(lu, 'lvdisplay')
@mock.patch.object(utils, 'execute')
def test_lvremove_not_found(self, mock_exec, mock_lvdisplay):
mock_lvdisplay.return_value = [{'name': 'some'}]
self.assertRaises(errors.LVNotFoundError, lu.lvremove, 'lvname')
mock_lvdisplay.return_value = [{'path': '/dev/vg/lv'}]
self.assertRaises(errors.LVNotFoundError, lu.lvremove, '/dev/vg/lv2')
@mock.patch.object(lu, 'vgdisplay')
@mock.patch.object(lu, 'lvdisplay')

View File

@@ -47,6 +47,11 @@ class TestManager(test_base.BaseTestCase):
self.assertFalse(self.mgr.configdrive_scheme is None)
self.assertFalse(self.mgr.image_scheme is None)
@mock.patch.object(utils, 'execute')
@mock.patch.object(mu, 'mdclean_all')
@mock.patch.object(lu, 'lvremove_all')
@mock.patch.object(lu, 'vgremove_all')
@mock.patch.object(lu, 'pvremove_all')
@mock.patch.object(fu, 'make_fs')
@mock.patch.object(lu, 'lvcreate')
@mock.patch.object(lu, 'vgcreate')
@@ -59,7 +64,8 @@ class TestManager(test_base.BaseTestCase):
@mock.patch.object(hu, 'list_block_devices')
def test_do_partitioning(self, mock_hu_lbd, mock_pu_ml, mock_pu_mp,
mock_pu_spf, mock_pu_sgt, mock_mu_m, mock_lu_p,
mock_lu_v, mock_lu_l, mock_fu_mf):
mock_lu_v, mock_lu_l, mock_fu_mf, mock_pvr,
mock_vgr, mock_lvr, mock_mdr, mock_exec):
mock_hu_lbd.return_value = test_nailgun.LIST_BLOCK_DEVICES_SAMPLE
self.mgr.do_parsing()
self.mgr.do_partitioning()

View File

@@ -118,7 +118,7 @@ localhost.localdomain)
mu.mdcreate('/dev/md0', 'mirror', '/dev/fake1', '/dev/fake2')
mock_exec.assert_called_once_with(
'mdadm', '--create', '--force', '/dev/md0', '-e1.2',
'mdadm', '--create', '--force', '/dev/md0', '-e0.90',
'--level=mirror',
'--raid-devices=2', '/dev/fake1', '/dev/fake2',
check_exit_code=[0])
@@ -181,11 +181,11 @@ localhost.localdomain)
self.assertEqual(mock_mdclean.call_args_list, expected_calls)
@mock.patch.object(utils, 'execute')
@mock.patch.object(mu, 'mddisplay')
def test_mdremove_ok(self, mock_mddisplay, mock_exec):
@mock.patch.object(mu, 'get_mdnames')
def test_mdremove_ok(self, mock_get_mdn, mock_exec):
# should check if md exists
# should run mdadm command to remove md device
mock_mddisplay.return_value = [{'name': '/dev/md0'}]
mock_get_mdn.return_value = ['/dev/md0']
expected_calls = [
mock.call('mdadm', '--stop', '/dev/md0', check_exit_code=[0]),
mock.call('mdadm', '--remove', '/dev/md0', check_exit_code=[0, 1])
@@ -193,11 +193,11 @@ localhost.localdomain)
mu.mdremove('/dev/md0')
self.assertEqual(mock_exec.call_args_list, expected_calls)
@mock.patch.object(mu, 'mddisplay')
def test_mdremove_notfound(self, mock_mddisplay):
@mock.patch.object(mu, 'get_mdnames')
def test_mdremove_notfound(self, mock_get_mdn):
# should check if md exists
# should raise error if it does not
mock_mddisplay.return_value = [{'name': '/dev/md0'}]
mock_get_mdn.return_value = ['/dev/md0']
self.assertRaises(
errors.MDNotFoundError, mu.mdremove, '/dev/md1')

View File

@@ -215,9 +215,24 @@ def lvcreate(vgname, lvname, size):
vgname, check_exit_code=[0])
def lvremove(lvname):
def lvremove(lvpath):
# check if lv exists
if not filter(lambda x: x['name'] == lvname, lvdisplay()):
if not filter(lambda x: x['path'] == lvpath, lvdisplay()):
raise errors.LVNotFoundError(
'Error while removing lv: lv %s not found' % lvname)
utils.execute('lvremove', '-f', lvname, check_exit_code=[0])
'Error while removing lv: lv %s not found' % lvpath)
utils.execute('lvremove', '-f', lvpath, check_exit_code=[0])
def lvremove_all():
for lv in lvdisplay():
lvremove(lv['path'])
def vgremove_all():
for vg in vgdisplay():
vgremove(vg['name'])
def pvremove_all():
for pv in pvdisplay():
pvremove(pv['name'])

View File

@@ -17,7 +17,6 @@ from fuel_agent.openstack.common import log as logging
from fuel_agent.utils import hardware_utils as hu
from fuel_agent.utils import utils
LOG = logging.getLogger(__name__)
@@ -57,11 +56,15 @@ def mddisplay(names=None):
mdnames = names or get_mdnames()
mds = []
for mdname in mdnames:
output = utils.execute('mdadm', '--detail', mdname,
check_exit_code=[0])[0]
md = {'name': mdname}
md.update(mddetail_parse(output))
mds.append(md)
try:
output = utils.execute('mdadm', '--detail', mdname,
check_exit_code=[0])[0]
md.update(mddetail_parse(output))
except errors.ProcessExecutionError:
continue
finally:
mds.append(md)
LOG.debug('Found md devices: {0}'.format(mds))
return mds
@@ -98,20 +101,17 @@ def mdcreate(mdname, level, device, *args):
# cleaning md metadata from devices
map(mdclean, devices)
utils.execute('mdadm', '--create', '--force', mdname, '-e1.2',
utils.execute('mdadm', '--create', '--force', mdname, '-e0.90',
'--level=%s' % level,
'--raid-devices=%s' % len(devices), *devices,
check_exit_code=[0])
def mdremove(mdname):
mds = mddisplay()
# check if md exists
if not filter(lambda x: x['name'] == mdname, mds):
if mdname not in get_mdnames():
raise errors.MDNotFoundError(
'Error while removing md: md %s not found' % mdname)
utils.execute('mdadm', '--stop', mdname, check_exit_code=[0])
utils.execute('mdadm', '--remove', mdname, check_exit_code=[0, 1])
@@ -120,3 +120,11 @@ def mdclean(device):
# we don't care if device actually exists or not
utils.execute('mdadm', '--zero-superblock', '--force', device,
check_exit_code=[0])
def mdclean_all():
LOG.debug('Trying to wipe out all md devices')
for md in mddisplay():
mdremove(md['name'])
for dev in md.get('devices', []):
mdclean(dev)