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:
@@ -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)
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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')
|
||||
|
||||
|
||||
@@ -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'])
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user