diff --git a/fuel_agent/manager.py b/fuel_agent/manager.py index 6f99eebf..3a2cad1a 100644 --- a/fuel_agent/manager.py +++ b/fuel_agent/manager.py @@ -163,6 +163,12 @@ opts = [ 'volumes. Devices in directories outside this hierarchy will be ' 'ignored.' ), + cfg.StrOpt( + 'partition_alignment', + default='optimal', + help='Set alignment for newly created partitions, valid alignment ' + 'types are: none, cylinder, minimal, optimal' + ), cfg.StrOpt( 'lvm_conf_path', default='/etc/lvm/lvm.conf', @@ -217,7 +223,8 @@ class Manager(object): for parted in parteds: pu.make_label(parted.name, parted.label) for prt in parted.partitions: - pu.make_partition(prt.device, prt.begin, prt.end, prt.type) + pu.make_partition(prt.device, prt.begin, prt.end, prt.type, + alignment=CONF.partition_alignment) utils.udevadm_trigger_blocks() for flag in prt.flags: pu.set_partition_flag(prt.device, prt.count, flag) diff --git a/fuel_agent/objects/partition/parted.py b/fuel_agent/objects/partition/parted.py index 3cff0bb6..eb06c41d 100644 --- a/fuel_agent/objects/partition/parted.py +++ b/fuel_agent/objects/partition/parted.py @@ -89,8 +89,10 @@ class Parted(base.Serializable): if not self.partitions: return 1 if self.partitions[-1] == self.extended: - return self.partitions[-1].begin - return self.partitions[-1].end + # NOTE(agordeev): this 1M room could be enough for minimal + # partition alignment mode for the most of cases. + return self.partitions[-1].begin + 1 + return self.partitions[-1].end + 1 def next_name(self): if self.next_type() == 'extended': diff --git a/fuel_agent/tests/test_manager.py b/fuel_agent/tests/test_manager.py index 94267017..80390927 100644 --- a/fuel_agent/tests/test_manager.py +++ b/fuel_agent/tests/test_manager.py @@ -447,19 +447,21 @@ class TestManager(unittest2.TestCase): self.assertEqual(mock_pu_ml_expected_calls, mock_pu_ml.call_args_list) mock_pu_mp_expected_calls = [ - mock.call('/dev/sda', 1, 25, 'primary'), - mock.call('/dev/sda', 25, 225, 'primary'), - mock.call('/dev/sda', 225, 425, 'primary'), - mock.call('/dev/sda', 425, 625, 'primary'), - mock.call('/dev/sda', 625, 20063, 'primary'), - mock.call('/dev/sda', 20063, 65660, 'primary'), - mock.call('/dev/sda', 65660, 65680, 'primary'), - mock.call('/dev/sdb', 1, 25, 'primary'), - mock.call('/dev/sdb', 25, 225, 'primary'), - mock.call('/dev/sdb', 225, 65196, 'primary'), - mock.call('/dev/sdc', 1, 25, 'primary'), - mock.call('/dev/sdc', 25, 225, 'primary'), - mock.call('/dev/sdc', 225, 65196, 'primary')] + mock.call('/dev/sda', 1, 25, 'primary', alignment='optimal'), + mock.call('/dev/sda', 26, 226, 'primary', alignment='optimal'), + mock.call('/dev/sda', 227, 427, 'primary', alignment='optimal'), + mock.call('/dev/sda', 428, 628, 'primary', alignment='optimal'), + mock.call('/dev/sda', 629, 20067, 'primary', alignment='optimal'), + mock.call('/dev/sda', 20068, 65665, 'primary', + alignment='optimal'), + mock.call('/dev/sda', 65666, 65686, 'primary', + alignment='optimal'), + mock.call('/dev/sdb', 1, 25, 'primary', alignment='optimal'), + mock.call('/dev/sdb', 26, 226, 'primary', alignment='optimal'), + mock.call('/dev/sdb', 227, 65198, 'primary', alignment='optimal'), + mock.call('/dev/sdc', 1, 25, 'primary', alignment='optimal'), + mock.call('/dev/sdc', 26, 226, 'primary', alignment='optimal'), + mock.call('/dev/sdc', 227, 65198, 'primary', alignment='optimal')] self.assertEqual(mock_pu_mp_expected_calls, mock_pu_mp.call_args_list) mock_pu_spf_expected_calls = [mock.call('/dev/sda', 1, 'bios_grub'), @@ -1230,6 +1232,7 @@ class TestManagerMultipathPartition(unittest2.TestCase): test_nailgun.MPATH_DISK_KS_SPACES self.mgr = manager.Manager(data) + @mock.patch.object(mu, 'mdclean_all') @mock.patch.object(manager.utils, 'refresh_multipath') @mock.patch.object(hu, 'is_multipath_device') @mock.patch.object(manager.os.path, 'exists') @@ -1240,7 +1243,7 @@ class TestManagerMultipathPartition(unittest2.TestCase): @mock.patch.object(hu, 'list_block_devices') def test_do_partitioning_mp(self, mock_hu_lbd, mock_fu_mf, mock_exec, mock_unbl, mock_bl, mock_os_path, mock_mp, - mock_refresh_multipath): + mock_refresh_multipath, mock_mdclean_all): mock_os_path.return_value = True mock_hu_lbd.return_value = test_nailgun.LIST_BLOCK_DEVICES_MPATH self.mgr._make_partitions = mock.MagicMock() @@ -1300,14 +1303,19 @@ class TestManagerMultipathPartition(unittest2.TestCase): mock.call('/dev/sdc', 'gpt')]) self.assertEqual(mock_make_partition.mock_calls, [ - mock.call('/dev/mapper/12312', 1, 25, 'primary'), - mock.call('/dev/mapper/12312', 25, 225, 'primary'), - mock.call('/dev/mapper/12312', 225, 425, 'primary'), - mock.call('/dev/mapper/12312', 425, 625, 'primary'), - mock.call('/dev/mapper/12312', 625, 645, 'primary'), - mock.call('/dev/sdc', 1, 25, 'primary'), - mock.call('/dev/sdc', 25, 225, 'primary'), - mock.call('/dev/sdc', 225, 425, 'primary')]) + mock.call('/dev/mapper/12312', 1, 25, 'primary', + alignment='optimal'), + mock.call('/dev/mapper/12312', 26, 226, 'primary', + alignment='optimal'), + mock.call('/dev/mapper/12312', 227, 427, 'primary', + alignment='optimal'), + mock.call('/dev/mapper/12312', 428, 628, 'primary', + alignment='optimal'), + mock.call('/dev/mapper/12312', 629, 649, 'primary', + alignment='optimal'), + mock.call('/dev/sdc', 1, 25, 'primary', alignment='optimal'), + mock.call('/dev/sdc', 26, 226, 'primary', alignment='optimal'), + mock.call('/dev/sdc', 227, 427, 'primary', alignment='optimal')]) self.assertEqual(mock_set_partition_flag.mock_calls, [ mock.call('/dev/mapper/12312', 1, 'bios_grub'), diff --git a/fuel_agent/tests/test_partition.py b/fuel_agent/tests/test_partition.py index 95339bfa..5c1f63b6 100644 --- a/fuel_agent/tests/test_partition.py +++ b/fuel_agent/tests/test_partition.py @@ -286,15 +286,15 @@ class TestParted(unittest2.TestCase): def test_next_begin_last_extended_partition(self): self.prtd.partitions.append( - objects.Partition('name', 'count', 'device', 'begin', 'end', + objects.Partition('name', 'count', 'device', 1, 100, 'extended')) - self.assertEqual('begin', self.prtd.next_begin()) + self.assertEqual(2, self.prtd.next_begin()) def test_next_begin_no_last_extended_partition(self): self.prtd.partitions.append( - objects.Partition('name', 'count', 'device', 'begin', 'end', + objects.Partition('name', 'count', 'device', 1, 100, 'primary')) - self.assertEqual('end', self.prtd.next_begin()) + self.assertEqual(101, self.prtd.next_begin()) def test_next_count_no_logical(self): self.assertEqual(1, self.prtd.next_count('primary')) diff --git a/fuel_agent/tests/test_partition_utils.py b/fuel_agent/tests/test_partition_utils.py index 14e18901..29fca5f9 100644 --- a/fuel_agent/tests/test_partition_utils.py +++ b/fuel_agent/tests/test_partition_utils.py @@ -134,6 +134,35 @@ class TestPartitionUtils(unittest2.TestCase): self.assertEqual(mock_exec_expected_calls, mock_exec.call_args_list) mock_rerd.assert_called_once_with('/dev/fake', out='out') + @mock.patch.object(utils, 'udevadm_settle') + @mock.patch.object(pu, 'reread_partitions') + @mock.patch.object(pu, 'info') + @mock.patch.object(utils, 'execute') + def test_make_partition_minimal(self, mock_exec, mock_info, mock_rerd, + mock_udev): + # should run parted OS command + # in order to create new partition + mock_exec.return_value = ('out', '') + + mock_info.return_value = { + 'parts': [ + {'begin': 0, 'end': 1000, 'fstype': 'free'}, + ] + } + pu.make_partition('/dev/fake', 100, 200, 'primary', + alignment='minimal') + mock_exec_expected_calls = [ + mock.call('parted', '-a', 'minimal', '-s', '/dev/fake', 'unit', + 'MiB', 'mkpart', 'primary', '100', '200', + check_exit_code=[0, 1])] + mock_udev.assert_called_once_with() + self.assertEqual(mock_exec_expected_calls, mock_exec.call_args_list) + mock_rerd.assert_called_once_with('/dev/fake', out='out') + + def test_make_partition_wrong_alignment(self): + self.assertRaises(errors.WrongPartitionSchemeError, pu.make_partition, + '/dev/fake', 1, 10, 'primary', 'invalid') + @mock.patch.object(utils, 'execute') def test_make_partition_wrong_ptype(self, mock_exec): # should check if partition type is one of diff --git a/fuel_agent/utils/partition.py b/fuel_agent/utils/partition.py index acdd0b3d..f7126b13 100644 --- a/fuel_agent/utils/partition.py +++ b/fuel_agent/utils/partition.py @@ -19,6 +19,7 @@ from fuel_agent.openstack.common import log as logging from fuel_agent.utils import utils LOG = logging.getLogger(__name__) +PARTITION_ALIGMENT = ('none', 'cylinder', 'minimal', 'optimal') def parse_partition_info(output): @@ -135,12 +136,27 @@ def set_gpt_type(dev, num, type_guid): dev, check_exit_code=[0]) -def make_partition(dev, begin, end, ptype): +def make_partition(dev, begin, end, ptype, alignment='optimal'): + """Creates a partition on the device. + + :param dev: A device file, e.g. /dev/sda. + :param begin: Beginning of the partition. + :param end: Ending of the partition. + :param ptype: Partition type: primary or logical. + :param alignment: Set alignment mode for newly created partitions, + valid alignment types are: none, cylinder, minimal, optimal. For more + information about this you can find in GNU parted manual. + + :returns: None + """ LOG.debug('Trying to create a partition: dev=%s begin=%s end=%s' % (dev, begin, end)) if ptype not in ('primary', 'logical'): raise errors.WrongPartitionSchemeError( 'Wrong partition type: %s' % ptype) + if alignment not in PARTITION_ALIGMENT: + raise errors.WrongPartitionSchemeError( + 'Wrong partition alignment requested: %s' % alignment) # check begin >= end if begin >= end: @@ -156,7 +172,7 @@ def make_partition(dev, begin, end, ptype): utils.udevadm_settle() out, err = utils.execute( - 'parted', '-a', 'optimal', '-s', dev, 'unit', 'MiB', + 'parted', '-a', alignment, '-s', dev, 'unit', 'MiB', 'mkpart', ptype, str(begin), str(end), check_exit_code=[0, 1]) LOG.debug('Parted output: \n%s' % out) reread_partitions(dev, out=out)