diff --git a/fuel_agent/manager.py b/fuel_agent/manager.py index f5160cb..737ed10 100644 --- a/fuel_agent/manager.py +++ b/fuel_agent/manager.py @@ -127,37 +127,11 @@ class Manager(object): lu.vgremove_all() lu.pvremove_all() - # Here is udev's rules blacklisting to be done: - # by adding symlinks to /dev/null in /etc/udev/rules.d for already - # existent rules in /lib/. - # 'parted' generates too many udev events in short period of time - # so we should increase processing speed for those events, - # otherwise partitioning is doomed. - empty_rule_path = os.path.join(CONF.udev_rules_dir, - os.path.basename(CONF.udev_empty_rule)) - with open(empty_rule_path, 'w') as f: - f.write('#\n') LOG.debug("Enabling udev's rules blacklisting") - for rule in os.listdir(CONF.udev_rules_lib_dir): - dst = os.path.join(CONF.udev_rules_dir, rule) - if os.path.isdir(dst): - continue - if dst.endswith('.rules'): - # for successful blacklisting already existent file with name - # from /etc which overlaps with /lib should be renamed prior - # symlink creation. - try: - if os.path.exists(dst): - os.rename(dst, dst[:-len('.rules')] + - CONF.udev_rename_substr) - utils.execute('udevadm', 'settle', '--quiet') - except OSError: - LOG.debug("Skipping udev rule %s blacklising" % dst) - else: - os.symlink(empty_rule_path, dst) - utils.execute('udevadm', 'settle', '--quiet') - utils.execute('udevadm', 'control', '--reload-rules', - check_exit_code=[0]) + utils.blacklist_udev_rules(udev_rules_dir=CONF.udev_rules_dir, + udev_rules_lib_dir=CONF.udev_rules_lib_dir, + udev_rename_substr=CONF.udev_rename_substr, + udev_empty_rule=CONF.udev_empty_rule) for parted in self.driver.partition_scheme.parteds: for prt in parted.partitions: @@ -191,36 +165,10 @@ class Manager(object): raise errors.PartitionNotFoundError( 'Partition %s not found after creation' % prt.name) - # disable udev's rules blacklisting LOG.debug("Disabling udev's rules blacklisting") - for rule in os.listdir(CONF.udev_rules_dir): - src = os.path.join(CONF.udev_rules_dir, rule) - if os.path.isdir(src): - continue - if src.endswith('.rules'): - if os.path.islink(src): - try: - os.remove(src) - utils.execute('udevadm', 'settle', '--quiet') - except OSError: - LOG.debug( - "Skipping udev rule %s de-blacklisting" % src) - elif src.endswith(CONF.udev_rename_substr): - try: - if os.path.exists(src): - os.rename(src, src[:-len(CONF.udev_rename_substr)] + - '.rules') - utils.execute('udevadm', 'settle', '--quiet') - except OSError: - LOG.debug("Skipping udev rule %s de-blacklisting" % src) - utils.execute('udevadm', 'control', '--reload-rules', - check_exit_code=[0]) - # NOTE(agordeev): re-create all the links which were skipped by udev - # while blacklisted - # NOTE(agordeev): do subsystem match, otherwise it will stuck - utils.execute('udevadm', 'trigger', '--subsystem-match=block', - check_exit_code=[0]) - utils.execute('udevadm', 'settle', '--quiet', check_exit_code=[0]) + utils.unblacklist_udev_rules( + udev_rules_dir=CONF.udev_rules_dir, + udev_rename_substr=CONF.udev_rename_substr) # If one creates partitions with the same boundaries as last time, # there might be md and lvm metadata on those partitions. To prevent diff --git a/fuel_agent/tests/test_manager.py b/fuel_agent/tests/test_manager.py index b946d9e..de9a742 100644 --- a/fuel_agent/tests/test_manager.py +++ b/fuel_agent/tests/test_manager.py @@ -268,12 +268,10 @@ class TestManager(unittest2.TestCase): mock.call('swap', '', '', '/dev/mapper/os-swap')] self.assertEqual(mock_fu_mf_expected_calls, mock_fu_mf.call_args_list) - @mock.patch('six.moves.builtins.open') - @mock.patch.object(os, 'symlink') - @mock.patch.object(os, 'remove') - @mock.patch.object(os, 'path') - @mock.patch.object(os, 'listdir') - @mock.patch.object(utils, 'execute') + @mock.patch.object(manager.os.path, 'exists') + @mock.patch.object(manager.utils, 'blacklist_udev_rules') + @mock.patch.object(manager.utils, 'unblacklist_udev_rules') + @mock.patch.object(manager.utils, 'execute') @mock.patch.object(mu, 'mdclean_all') @mock.patch.object(lu, 'lvremove_all') @mock.patch.object(lu, 'vgremove_all') @@ -292,11 +290,9 @@ class TestManager(unittest2.TestCase): mock_pu_spf, mock_pu_sgt, mock_mu_m, mock_lu_p, mock_lu_v, mock_lu_l, mock_fu_mf, mock_pvr, mock_vgr, mock_lvr, mock_mdr, mock_exec, - mock_os_ld, mock_os_p, mock_os_r, mock_os_s, - mock_open): - mock_os_ld.return_value = ['not_a_rule', 'fake.rules'] - mock_os_p.exists.return_value = True + mock_unbl, mock_bl, mock_os_path): mock_hu_lbd.return_value = test_nailgun.LIST_BLOCK_DEVICES_SAMPLE + mock_os_path.return_value = True self.mgr.driver.partition_scheme.mds = [ objects.MD('fake_md1', 'mirror', devices=['/dev/sda1', '/dev/sdb1']), @@ -310,12 +306,10 @@ class TestManager(unittest2.TestCase): ['/dev/sdb3', '/dev/sdc1'])], mock_mu_m.call_args_list) - @mock.patch('six.moves.builtins.open') - @mock.patch.object(os, 'symlink') - @mock.patch.object(os, 'remove') - @mock.patch.object(os, 'path') - @mock.patch.object(os, 'listdir') - @mock.patch.object(utils, 'execute') + @mock.patch.object(manager.os.path, 'exists') + @mock.patch.object(manager.utils, 'blacklist_udev_rules') + @mock.patch.object(manager.utils, 'unblacklist_udev_rules') + @mock.patch.object(manager.utils, 'execute') @mock.patch.object(mu, 'mdclean_all') @mock.patch.object(lu, 'lvremove_all') @mock.patch.object(lu, 'vgremove_all') @@ -334,12 +328,16 @@ class TestManager(unittest2.TestCase): mock_pu_spf, mock_pu_sgt, mock_mu_m, mock_lu_p, mock_lu_v, mock_lu_l, mock_fu_mf, mock_pvr, mock_vgr, mock_lvr, mock_mdr, mock_exec, - mock_os_ld, mock_os_p, mock_os_r, mock_os_s, - mock_open): - mock_os_ld.return_value = ['not_a_rule', 'fake.rules'] - mock_os_p.exists.return_value = True + mock_unbl, mock_bl, mock_os_path): + mock_os_path.return_value = True mock_hu_lbd.return_value = test_nailgun.LIST_BLOCK_DEVICES_SAMPLE self.mgr.do_partitioning() + mock_unbl.assert_called_once_with(udev_rules_dir='/etc/udev/rules.d', + udev_rename_substr='.renamedrule') + mock_bl.assert_called_once_with(udev_rules_dir='/etc/udev/rules.d', + udev_rules_lib_dir='/lib/udev/rules.d', + udev_empty_rule='empty_rule', + udev_rename_substr='.renamedrule') mock_pu_ml_expected_calls = [mock.call('/dev/sda', 'gpt'), mock.call('/dev/sdb', 'gpt'), mock.call('/dev/sdc', 'gpt')] diff --git a/fuel_agent/tests/test_md_utils.py b/fuel_agent/tests/test_md_utils.py index f9874a0..0685e97 100644 --- a/fuel_agent/tests/test_md_utils.py +++ b/fuel_agent/tests/test_md_utils.py @@ -247,19 +247,20 @@ localhost.localdomain) mock_mddisplay.return_value = [{'name': '/dev/md11'}] self.assertRaises(errors.MDRemovingError, mu.mdclean_all) + @mock.patch.object(utils, 'udevadm_settle') @mock.patch.object(utils, 'execute') @mock.patch.object(mu, 'get_mdnames') - def test_mdremove_ok(self, mock_get_mdn, mock_exec): + def test_mdremove_ok(self, mock_get_mdn, mock_exec, mock_udev): # should check if md exists # should run mdadm command to remove md device mock_get_mdn.return_value = ['/dev/md0'] expected_calls = [ - mock.call('udevadm', 'settle', '--quiet', check_exit_code=[0]), mock.call('mdadm', '--stop', '/dev/md0', check_exit_code=[0]), mock.call('mdadm', '--remove', '/dev/md0', check_exit_code=[0, 1]) ] mu.mdremove('/dev/md0') self.assertEqual(mock_exec.call_args_list, expected_calls) + mock_udev.assert_called_once_with() @mock.patch.object(mu, 'get_mdnames') def test_mdremove_notfound(self, mock_get_mdn): diff --git a/fuel_agent/tests/test_partition_utils.py b/fuel_agent/tests/test_partition_utils.py index 3f41b59..14e1890 100644 --- a/fuel_agent/tests/test_partition_utils.py +++ b/fuel_agent/tests/test_partition_utils.py @@ -31,9 +31,10 @@ class TestPartitionUtils(unittest2.TestCase): pu.wipe('/dev/fake') mock_label.assert_called_once_with('/dev/fake') + @mock.patch.object(utils, 'udevadm_settle') @mock.patch.object(pu, 'reread_partitions') @mock.patch.object(utils, 'execute') - def test_make_label(self, mock_exec, mock_rerd): + def test_make_label(self, mock_exec, mock_rerd, mock_udev): # should run parted OS command # in order to create label on a device mock_exec.return_value = ('out', '') @@ -41,21 +42,22 @@ class TestPartitionUtils(unittest2.TestCase): # gpt by default pu.make_label('/dev/fake') mock_exec_expected_calls = [ - mock.call('udevadm', 'settle', '--quiet', check_exit_code=[0]), mock.call('parted', '-s', '/dev/fake', 'mklabel', 'gpt', check_exit_code=[0, 1])] self.assertEqual(mock_exec_expected_calls, mock_exec.call_args_list) mock_rerd.assert_called_once_with('/dev/fake', out='out') + mock_udev.assert_called_once_with() mock_exec.reset_mock() mock_rerd.reset_mock() + mock_udev.reset_mock() # label is set explicitly pu.make_label('/dev/fake', label='msdos') mock_exec_expected_calls = [ - mock.call('udevadm', 'settle', '--quiet', check_exit_code=[0]), mock.call('parted', '-s', '/dev/fake', 'mklabel', 'msdos', check_exit_code=[0, 1])] self.assertEqual(mock_exec_expected_calls, mock_exec.call_args_list) + mock_udev.assert_called_once_with() mock_rerd.assert_called_once_with('/dev/fake', out='out') def test_make_label_wrong_label(self): @@ -64,9 +66,10 @@ class TestPartitionUtils(unittest2.TestCase): self.assertRaises(errors.WrongPartitionLabelError, pu.make_label, '/dev/fake', 'wrong') + @mock.patch.object(utils, 'udevadm_settle') @mock.patch.object(pu, 'reread_partitions') @mock.patch.object(utils, 'execute') - def test_set_partition_flag(self, mock_exec, mock_rerd): + def test_set_partition_flag(self, mock_exec, mock_rerd, mock_udev): # should run parted OS command # in order to set flag on a partition mock_exec.return_value = ('out', '') @@ -74,20 +77,21 @@ class TestPartitionUtils(unittest2.TestCase): # default state is 'on' pu.set_partition_flag('/dev/fake', 1, 'boot') mock_exec_expected_calls = [ - mock.call('udevadm', 'settle', '--quiet', check_exit_code=[0]), mock.call('parted', '-s', '/dev/fake', 'set', '1', 'boot', 'on', 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') mock_exec.reset_mock() mock_rerd.reset_mock() + mock_udev.reset_mock() # if state argument is given use it pu.set_partition_flag('/dev/fake', 1, 'boot', state='off') mock_exec_expected_calls = [ - mock.call('udevadm', 'settle', '--quiet', check_exit_code=[0]), mock.call('parted', '-s', '/dev/fake', 'set', '1', 'boot', 'off', 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') @@ -107,10 +111,11 @@ class TestPartitionUtils(unittest2.TestCase): pu.set_partition_flag, '/dev/fake', 1, 'boot', state='wrong') + @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(self, mock_exec, mock_info, mock_rerd): + def test_make_partition(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', '') @@ -122,10 +127,10 @@ class TestPartitionUtils(unittest2.TestCase): } pu.make_partition('/dev/fake', 100, 200, 'primary') mock_exec_expected_calls = [ - mock.call('udevadm', 'settle', '--quiet', check_exit_code=[0]), mock.call('parted', '-a', 'optimal', '-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') @@ -165,10 +170,12 @@ class TestPartitionUtils(unittest2.TestCase): self.assertEqual(mock_info.call_args_list, [mock.call('/dev/fake')] * 3) + @mock.patch.object(utils, 'udevadm_settle') @mock.patch.object(pu, 'reread_partitions') @mock.patch.object(pu, 'info') @mock.patch.object(utils, 'execute') - def test_remove_partition(self, mock_exec, mock_info, mock_rerd): + def test_remove_partition(self, mock_exec, mock_info, mock_rerd, + mock_udev): # should run parted OS command # in order to remove partition mock_exec.return_value = ('out', '') @@ -192,9 +199,9 @@ class TestPartitionUtils(unittest2.TestCase): } pu.remove_partition('/dev/fake', 1) mock_exec_expected_calls = [ - mock.call('udevadm', 'settle', '--quiet', check_exit_code=[0]), mock.call('parted', '-s', '/dev/fake', 'rm', '1', 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') @@ -224,17 +231,19 @@ class TestPartitionUtils(unittest2.TestCase): self.assertRaises(errors.PartitionNotFoundError, pu.remove_partition, '/dev/fake', 3) + @mock.patch.object(utils, 'udevadm_settle') @mock.patch.object(utils, 'execute') - def test_set_gpt_type(self, mock_exec): + def test_set_gpt_type(self, mock_exec, mock_udev): pu.set_gpt_type('dev', 'num', 'type') mock_exec_expected_calls = [ - mock.call('udevadm', 'settle', '--quiet', check_exit_code=[0]), mock.call('sgdisk', '--typecode=%s:%s' % ('num', 'type'), 'dev', check_exit_code=[0])] self.assertEqual(mock_exec_expected_calls, mock_exec.call_args_list) + mock_udev.assert_called_once_with() + @mock.patch.object(utils, 'udevadm_settle') @mock.patch.object(utils, 'execute') - def test_info(self, mock_exec): + def test_info(self, mock_exec, mock_udev): mock_exec.return_value = [ 'BYT;\n' '/dev/fake:476940MiB:scsi:512:4096:msdos:ATA 1BD14;\n' @@ -261,27 +270,29 @@ class TestPartitionUtils(unittest2.TestCase): actual = pu.info('/dev/fake') self.assertEqual(expected, actual) mock_exec_expected_calls = [ - mock.call('udevadm', 'settle', '--quiet', check_exit_code=[0]), mock.call('parted', '-s', '/dev/fake', '-m', 'unit', 'MiB', 'print', 'free', check_exit_code=[0])] self.assertEqual(mock_exec_expected_calls, mock_exec.call_args_list) + mock_udev.assert_called_once_with() @mock.patch.object(utils, 'execute') def test_reread_partitions_ok(self, mock_exec): pu.reread_partitions('/dev/fake', out='') self.assertEqual(mock_exec.call_args_list, []) + @mock.patch.object(utils, 'udevadm_settle') @mock.patch.object(time, 'sleep') @mock.patch.object(utils, 'execute') - def test_reread_partitions_device_busy(self, mock_exec, mock_sleep): + def test_reread_partitions_device_busy(self, mock_exec, mock_sleep, + mock_udev): mock_exec.return_value = ('', '') pu.reread_partitions('/dev/fake', out='_Device or resource busy_') mock_exec_expected = [ mock.call('partprobe', '/dev/fake', check_exit_code=[0, 1]), - mock.call('udevadm', 'settle', '--quiet', check_exit_code=[0]), ] self.assertEqual(mock_exec.call_args_list, mock_exec_expected) mock_sleep.assert_called_once_with(2) + mock_udev.assert_called_once_with() @mock.patch.object(utils, 'execute') def test_reread_partitions_timeout(self, mock_exec): diff --git a/fuel_agent/tests/test_utils.py b/fuel_agent/tests/test_utils.py index 8e085b0..6ce51ce 100644 --- a/fuel_agent/tests/test_utils.py +++ b/fuel_agent/tests/test_utils.py @@ -212,3 +212,178 @@ class ExecuteTestCase(unittest2.TestCase): # by default files are sorted in backward direction self.assertEqual(filename, 'file1') mock_oslistdir.assert_called_once_with('/some/path') + + @mock.patch.object(utils, 'execute') + def test_udevadm_settle(self, mock_exec): + utils.udevadm_settle() + mock_exec.assert_called_once_with('udevadm', 'settle', '--quiet', + check_exit_code=[0]) + + +@mock.patch.object(utils, 'open', create=True, new_callable=mock.mock_open) +@mock.patch.object(utils, 'os', autospec=True) +@mock.patch.object(utils, 'execute') +@mock.patch.object(utils, 'udevadm_settle') +class TestUdevRulesBlacklisting(unittest2.TestCase): + @staticmethod + def _fake_join(path1, path2): + return '{0}/{1}'.format(path1, path2) + + def test_blacklist_udev_rules_rule_exists(self, mock_udev, mock_execute, + mock_os, mock_open): + mock_os.path.join.side_effect = self._fake_join + mock_os.path.basename.return_value = 'fake_basename' + mock_os.listdir.return_value = ['fake.rules', 'fake_err.rules'] + mock_os.path.isdir.return_value = False + mock_os.path.exists.return_value = True + mock_os.rename.side_effect = [None, OSError] + utils.blacklist_udev_rules('/etc/udev/rules.d', '/lib/udev/rules.d', + '.renamedrule', 'empty_rule') + self.assertEqual([mock.call('/etc/udev/rules.d/fake.rules'), + mock.call('/etc/udev/rules.d/fake_err.rules')], + mock_os.path.exists.call_args_list) + self.assertEqual([mock.call('/etc/udev/rules.d/fake.rules', + '/etc/udev/rules.d/fake.renamedrule'), + mock.call('/etc/udev/rules.d/fake_err.rules', + '/etc/udev/rules.d/fake_err.renamedrule')], + mock_os.rename.call_args_list) + self.assertEqual([mock.call('/etc/udev/rules.d', 'fake_basename'), + mock.call('/etc/udev/rules.d', 'fake.rules'), + mock.call('/etc/udev/rules.d', 'fake_err.rules')], + mock_os.path.join.call_args_list) + mock_os.symlink.assert_called_once_with( + '/etc/udev/rules.d/fake_basename', + '/etc/udev/rules.d/fake.rules') + self.assertEqual(2 * [mock.call()], mock_udev.call_args_list) + + def test_blacklist_udev_rules_rule_doesnot_exist(self, mock_udev, + mock_execute, mock_os, + mock_open): + mock_os.path.join.side_effect = self._fake_join + mock_os.path.basename.return_value = 'fake_basename' + mock_os.listdir.return_value = ['fake.rules'] + mock_os.path.isdir.return_value = False + mock_os.path.exists.return_value = False + utils.blacklist_udev_rules('/etc/udev/rules.d', '/lib/udev/rules.d', + '.renamedrule', 'empty_rule') + self.assertFalse(mock_os.rename.called) + mock_os.path.isdir.assert_called_once_with( + '/etc/udev/rules.d/fake.rules') + mock_os.path.exists.assert_called_once_with( + '/etc/udev/rules.d/fake.rules') + self.assertEqual([mock.call('/etc/udev/rules.d', 'fake_basename'), + mock.call('/etc/udev/rules.d', 'fake.rules')], + mock_os.path.join.call_args_list) + mock_os.symlink.assert_called_once_with( + '/etc/udev/rules.d/fake_basename', + '/etc/udev/rules.d/fake.rules') + mock_udev.assert_called_once_with() + + def test_blacklist_udev_rules_not_a_rule(self, mock_udev, mock_execute, + mock_os, mock_open): + mock_os.path.join.side_effect = self._fake_join + mock_os.path.basename.return_value = 'fake_basename' + mock_os.listdir.return_value = ['not_a_rule', 'dir'] + mock_os.path.isdir.side_effect = [False, True] + utils.blacklist_udev_rules('/etc/udev/rules.d', '/lib/udev/rules.d', + '.renamedrule', 'empty_rule') + self.assertFalse(mock_udev.called) + self.assertFalse(mock_os.symlink.called) + self.assertFalse(mock_os.rename.called) + self.assertFalse(mock_os.path.exists.called) + mock_os.listdir.assert_called_once_with('/lib/udev/rules.d') + self.assertEqual([mock.call('/etc/udev/rules.d/not_a_rule'), + mock.call('/etc/udev/rules.d/dir')], + mock_os.path.isdir.call_args_list) + self.assertEqual([mock.call('/etc/udev/rules.d', 'fake_basename'), + mock.call('/etc/udev/rules.d', 'not_a_rule'), + mock.call('/etc/udev/rules.d', 'dir')], + mock_os.path.join.call_args_list) + + def test_blacklist_udev_rules_create_empty_rule(self, mock_udev, + mock_execute, mock_os, + mock_open): + mock_os.path.join.side_effect = self._fake_join + mock_os.path.basename.return_value = 'fake_basename' + utils.blacklist_udev_rules('/etc/udev/rules.d', '/lib/udev/rules.d', + '.renamedrule', 'empty_rule') + mock_open.assert_called_once_with('/etc/udev/rules.d/fake_basename', + 'w') + file_handler = mock_open.return_value.__enter__.return_value + file_handler.write.assert_called_once_with('#\n') + mock_os.path.basename.assert_called_once_with('empty_rule') + + def test_blacklist_udev_rules_execute(self, mock_udev, mock_execute, + mock_os, mock_open): + utils.blacklist_udev_rules('/etc/udev/rules.d', '/lib/udev/rules.d', + '.renamedrule', 'empty_rule') + mock_execute.assert_called_once_with( + 'udevadm', 'control', '--reload-rules', check_exit_code=[0]) + + def test_unblacklist_udev_rules_remove(self, mock_udev, mock_execute, + mock_os, mock_open): + mock_os.path.join.side_effect = self._fake_join + mock_os.listdir.return_value = ['fake.rules', 'fake_err.rules'] + mock_os.remove.side_effect = [None, OSError] + mock_os.path.isdir.side_effect = 2 * [False] + mock_os.path.islink.return_value = True + utils.unblacklist_udev_rules('/etc/udev/rules.d', '.renamedrule') + self.assertFalse(mock_os.path.exists.called) + self.assertFalse(mock_os.rename.called) + mock_os.listdir.assert_called_once_with('/etc/udev/rules.d') + expected_rules_calls = [mock.call('/etc/udev/rules.d/fake.rules'), + mock.call('/etc/udev/rules.d/fake_err.rules')] + self.assertEqual(expected_rules_calls, + mock_os.path.islink.call_args_list) + self.assertEqual(expected_rules_calls, + mock_os.remove.call_args_list) + self.assertEqual(2 * [mock.call()], mock_udev.call_args_list) + + def test_unblacklist_udev_rules_executes(self, mock_udev, mock_execute, + mock_os, mock_open): + utils.unblacklist_udev_rules('/etc/udev/rules.d', '.renamedrule') + self.assertEqual([mock.call('udevadm', 'control', '--reload-rules', + check_exit_code=[0]), + mock.call('udevadm', 'trigger', + '--subsystem-match=block', + check_exit_code=[0])], + mock_execute.call_args_list) + + def test_unblacklist_udev_rules_rename(self, mock_udev, mock_execute, + mock_os, mock_open): + mock_os.path.join.side_effect = self._fake_join + mock_os.listdir.return_value = ['fake.renamedrule', + 'fake_err.renamedrule'] + mock_os.rename.side_effect = [None, OSError] + mock_os.path.isdir.side_effect = 2 * [False] + utils.unblacklist_udev_rules('/etc/udev/rules.d', '.renamedrule') + self.assertFalse(mock_os.path.islink.called) + self.assertFalse(mock_os.remove.called) + mock_os.listdir.assert_called_once_with('/etc/udev/rules.d') + self.assertEqual([mock.call('/etc/udev/rules.d/fake.renamedrule'), + mock.call('/etc/udev/rules.d/fake_err.renamedrule')], + mock_os.path.exists.call_args_list) + self.assertEqual([mock.call('/etc/udev/rules.d/fake.renamedrule', + '/etc/udev/rules.d/fake.rules'), + mock.call('/etc/udev/rules.d/fake_err.renamedrule', + '/etc/udev/rules.d/fake_err.rules')], + mock_os.rename.call_args_list) + self.assertEqual(2 * [mock.call()], mock_udev.call_args_list) + + def test_unblacklist_udev_rules_not_a_rule(self, mock_udev, mock_execute, + mock_os, mock_open): + mock_os.path.join.side_effect = self._fake_join + mock_os.listdir.return_value = ['not_a_rule', 'dir'] + mock_os.path.isdir.side_effect = [False, True] + utils.unblacklist_udev_rules('/etc/udev/rules.d', '.renamedrule') + mock_os.listdir.assert_called_once_with('/etc/udev/rules.d') + self.assertEqual([mock.call('/etc/udev/rules.d', 'not_a_rule'), + mock.call('/etc/udev/rules.d', 'dir')], + mock_os.path.join.call_args_list) + self.assertEqual([mock.call('/etc/udev/rules.d/not_a_rule'), + mock.call('/etc/udev/rules.d/dir')], + mock_os.path.isdir.call_args_list) + self.assertFalse(mock_os.path.exists.called) + self.assertFalse(mock_os.remove.called) + self.assertFalse(mock_os.rename.called) + mock_udev.assert_called_once_with() diff --git a/fuel_agent/utils/md.py b/fuel_agent/utils/md.py index f9b1076..24133c8 100644 --- a/fuel_agent/utils/md.py +++ b/fuel_agent/utils/md.py @@ -142,7 +142,7 @@ def mdremove(mdname): # too busy with events when we start to modifiy md devices hard. # Thus `udevadm settle` is helping to avoid the later failure and # to prevent strange behaviour of md device. - utils.execute('udevadm', 'settle', '--quiet', check_exit_code=[0]) + utils.udevadm_settle() utils.execute('mdadm', '--stop', mdname, check_exit_code=[0]) utils.execute('mdadm', '--remove', mdname, check_exit_code=[0, 1]) diff --git a/fuel_agent/utils/partition.py b/fuel_agent/utils/partition.py index a9ef6f5..acdd0b3 100644 --- a/fuel_agent/utils/partition.py +++ b/fuel_agent/utils/partition.py @@ -49,7 +49,7 @@ def parse_partition_info(output): def info(dev): - utils.execute('udevadm', 'settle', '--quiet', check_exit_code=[0]) + utils.udevadm_settle() output = utils.execute('parted', '-s', dev, '-m', 'unit', 'MiB', 'print', 'free', @@ -80,7 +80,7 @@ def make_label(dev, label='gpt'): if label not in ('gpt', 'msdos'): raise errors.WrongPartitionLabelError( 'Wrong partition label type: %s' % label) - utils.execute('udevadm', 'settle', '--quiet', check_exit_code=[0]) + utils.udevadm_settle() out, err = utils.execute('parted', '-s', dev, 'mklabel', label, check_exit_code=[0, 1]) LOG.debug('Parted output: \n%s' % out) @@ -109,7 +109,7 @@ def set_partition_flag(dev, num, flag, state='on'): if state not in ('on', 'off'): raise errors.WrongPartitionSchemeError( 'Wrong partition flag state: %s' % state) - utils.execute('udevadm', 'settle', '--quiet', check_exit_code=[0]) + utils.udevadm_settle() out, err = utils.execute('parted', '-s', dev, 'set', str(num), flag, state, check_exit_code=[0, 1]) LOG.debug('Parted output: \n%s' % out) @@ -130,7 +130,7 @@ def set_gpt_type(dev, num, type_guid): # TODO(kozhukalov): check whether type_guid is valid LOG.debug('Setting partition GUID: dev=%s num=%s guid=%s' % (dev, num, type_guid)) - utils.execute('udevadm', 'settle', '--quiet', check_exit_code=[0]) + utils.udevadm_settle() utils.execute('sgdisk', '--typecode=%s:%s' % (num, type_guid), dev, check_exit_code=[0]) @@ -154,7 +154,7 @@ def make_partition(dev, begin, end, ptype): 'Invalid boundaries: begin and end ' 'are not inside available free space') - utils.execute('udevadm', 'settle', '--quiet', check_exit_code=[0]) + utils.udevadm_settle() out, err = utils.execute( 'parted', '-a', 'optimal', '-s', dev, 'unit', 'MiB', 'mkpart', ptype, str(begin), str(end), check_exit_code=[0, 1]) @@ -167,7 +167,7 @@ def remove_partition(dev, num): if not any(x['fstype'] != 'free' and x['num'] == num for x in info(dev)['parts']): raise errors.PartitionNotFoundError('Partition %s not found' % num) - utils.execute('udevadm', 'settle', '--quiet', check_exit_code=[0]) + utils.udevadm_settle() out, err = utils.execute('parted', '-s', dev, 'rm', str(num), check_exit_code=[0, 1]) reread_partitions(dev, out=out) @@ -191,4 +191,4 @@ def reread_partitions(dev, out='Device or resource busy', timeout=60): time.sleep(2) out, err = utils.execute('partprobe', dev, check_exit_code=[0, 1]) LOG.debug('Partprobe output: \n%s' % out) - utils.execute('udevadm', 'settle', '--quiet', check_exit_code=[0]) + utils.udevadm_settle() diff --git a/fuel_agent/utils/utils.py b/fuel_agent/utils/utils.py index af04646..691c128 100644 --- a/fuel_agent/utils/utils.py +++ b/fuel_agent/utils/utils.py @@ -265,3 +265,72 @@ def guess_filename(path, regexp, sort=True, reverse=True): if re.search(regexp, filename): return filename return None + + +def blacklist_udev_rules(udev_rules_dir, udev_rules_lib_dir, + udev_rename_substr, udev_empty_rule): + """Blacklist udev rules + + Here is udev's rules blacklisting to be done: + by adding symlinks to /dev/null in /etc/udev/rules.d for already + existent rules in /lib/. + 'parted' generates too many udev events in short period of time + so we should increase processing speed for those events, + otherwise partitioning is doomed. + """ + empty_rule_path = os.path.join(udev_rules_dir, + os.path.basename(udev_empty_rule)) + with open(empty_rule_path, 'w') as f: + f.write('#\n') + for rule in os.listdir(udev_rules_lib_dir): + dst = os.path.join(udev_rules_dir, rule) + if os.path.isdir(dst): + continue + if dst.endswith('.rules'): + # for successful blacklisting already existent file with name + # from /etc which overlaps with /lib should be renamed prior + # symlink creation. + try: + if os.path.exists(dst): + os.rename(dst, dst[:-len('.rules')] + udev_rename_substr) + udevadm_settle() + except OSError: + LOG.debug("Skipping udev rule %s blacklising" % dst) + else: + os.symlink(empty_rule_path, dst) + udevadm_settle() + execute('udevadm', 'control', '--reload-rules', check_exit_code=[0]) + + +def unblacklist_udev_rules(udev_rules_dir, udev_rename_substr): + """disable udev's rules blacklisting""" + for rule in os.listdir(udev_rules_dir): + src = os.path.join(udev_rules_dir, rule) + if os.path.isdir(src): + continue + if src.endswith('.rules'): + if os.path.islink(src): + try: + os.remove(src) + udevadm_settle() + except OSError: + LOG.debug( + "Skipping udev rule %s de-blacklisting" % src) + elif src.endswith(udev_rename_substr): + try: + if os.path.exists(src): + os.rename(src, src[:-len(udev_rename_substr)] + '.rules') + udevadm_settle() + except OSError: + LOG.debug("Skipping udev rule %s de-blacklisting" % src) + execute('udevadm', 'control', '--reload-rules', check_exit_code=[0]) + # NOTE(agordeev): re-create all the links which were skipped by udev + # while blacklisted + # NOTE(agordeev): do subsystem match, otherwise it will stuck + execute('udevadm', 'trigger', '--subsystem-match=block', + check_exit_code=[0]) + udevadm_settle() + + +def udevadm_settle(): + execute('udevadm', 'settle', '--quiet', check_exit_code=[0])