diff --git a/ironic_python_agent/tests/unit/test_utils.py b/ironic_python_agent/tests/unit/test_utils.py index 4ee0ebd39..bca2b42e5 100644 --- a/ironic_python_agent/tests/unit/test_utils.py +++ b/ironic_python_agent/tests/unit/test_utils.py @@ -34,6 +34,30 @@ from ironic_python_agent import hardware from ironic_python_agent.tests.unit import base as ironic_agent_base from ironic_python_agent import utils +PARTED_OUTPUT_UNFORMATTED = '''Model: whatever +Disk /dev/sda: 450GB +Sector size (logical/physical): 512B/512B +Partition Table: {} +Disk Flags: + +Number Start End Size File system Name Flags +14 1049kB 5243kB 4194kB bios_grub +15 5243kB 116MB 111MB fat32 boot, esp + 1 116MB 2361MB 2245MB ext4 +''' + + +PARTED_OUTPUT_NO_EFI = '''Model: whatever +Disk /dev/sda: 450GB +Sector size (logical/physical): 512B/512B +Partition Table: gpt +Disk Flags: + +Number Start End Size File system Name Flags +14 1049kB 5243kB 4194kB bios_grub + 1 116MB 2361MB 2245MB ext4 +''' + class ExecuteTestCase(ironic_agent_base.IronicAgentTest): # This test case does call utils.execute(), so don't block access to the @@ -592,3 +616,25 @@ class TestUtils(testtools.TestCase): self.assertIsNone( utils.extract_device('whatevernotmatchin12a') ) + + @mock.patch.object(utils, 'execute', autospec=True) + def test_get_efi_part_on_device(self, mocked_execute): + parted_ret = PARTED_OUTPUT_UNFORMATTED.format('gpt') + mocked_execute.side_effect = [ + (parted_ret, None) + ] + ret = utils.get_efi_part_on_device('/dev/sda') + mocked_execute.assert_has_calls( + [mock.call('parted', '-s', '/dev/sda', '--', 'print')] + ) + self.assertEqual('15', ret) + + @mock.patch.object(utils, 'execute', autospec=True) + def test_get_efi_part_on_device_not_found(self, mocked_execute): + mocked_execute.side_effect = [ + (PARTED_OUTPUT_NO_EFI, None) + ] + self.assertIsNone(utils.get_efi_part_on_device('/dev/sda')) + mocked_execute.assert_has_calls( + [mock.call('parted', '-s', '/dev/sda', '--', 'print')] + ) diff --git a/ironic_python_agent/utils.py b/ironic_python_agent/utils.py index 0257cfb1a..22a4e4b2e 100644 --- a/ironic_python_agent/utils.py +++ b/ironic_python_agent/utils.py @@ -63,6 +63,8 @@ COLLECT_LOGS_COMMANDS = { DEVICE_EXTRACTOR = re.compile(r'^(?:(.*\d)p|(.*\D))(?:\d+)$') +PARTED_ESP_PATTERN = re.compile(r'^\s*(\d+)\s.*\sfat\d*\s.*esp(,|\s|$).*$') + def execute(*cmd, **kwargs): """Convenience wrapper around ironic_lib's execute() method. @@ -453,3 +455,23 @@ def extract_device(part): if not m: return None return (m.group(1) or m.group(2)) + + +def get_efi_part_on_device(device): + """Looks for the efi partition on a given device + + :param device: lock device upon which to check for the efi partition + :return: the efi partition or None + """ + efi_part = None + out, _u = execute('parted', '-s', device, '--', 'print') + for line in out.splitlines(): + m = PARTED_ESP_PATTERN.match(line) + if m: + efi_part = m.group(1) + + LOG.debug("Found efi partition %s on device %s.", efi_part, device) + break + else: + LOG.debug("No efi partition found on device %s", device) + return efi_part