summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ironic_python_agent/efi_utils.py10
-rw-r--r--ironic_python_agent/partition_utils.py45
-rw-r--r--ironic_python_agent/tests/unit/test_partition_utils.py31
-rw-r--r--releasenotes/notes/configdrive-partuuid-3259cfb7428c1483.yaml17
-rw-r--r--releasenotes/notes/detect-endianness-f53a6c4571aba3fe.yaml6
-rw-r--r--tox.ini8
-rw-r--r--zuul.d/ironic-python-agent-jobs.yaml64
-rw-r--r--zuul.d/project.yaml22
8 files changed, 153 insertions, 50 deletions
diff --git a/ironic_python_agent/efi_utils.py b/ironic_python_agent/efi_utils.py
index 54d1e62f..456698f5 100644
--- a/ironic_python_agent/efi_utils.py
+++ b/ironic_python_agent/efi_utils.py
@@ -297,8 +297,14 @@ def _run_efibootmgr(valid_efi_bootloaders, device, efi_partition,
'File: %s', v_bl)
# These files are always UTF-16 encoded, sometimes have a header.
# Positive bonus is python silently drops the FEFF header.
- with open(mount_point + '/' + v_bl, 'r', encoding='utf-16') as csv:
- contents = str(csv.read())
+ try:
+ with open(mount_point + '/' + v_bl, 'r',
+ encoding='utf-16') as csv:
+ contents = str(csv.read())
+ except UnicodeError:
+ with open(mount_point + '/' + v_bl, 'r',
+ encoding='utf-16-le') as csv:
+ contents = str(csv.read())
csv_contents = contents.split(',', maxsplit=3)
csv_filename = v_bl.split('/')[-1]
v_efi_bl_path = v_bl.replace(csv_filename, str(csv_contents[0]))
diff --git a/ironic_python_agent/partition_utils.py b/ironic_python_agent/partition_utils.py
index 42b90445..d82d0072 100644
--- a/ironic_python_agent/partition_utils.py
+++ b/ironic_python_agent/partition_utils.py
@@ -35,6 +35,7 @@ from oslo_config import cfg
from oslo_log import log
from oslo_utils import excutils
from oslo_utils import units
+from oslo_utils import uuidutils
import requests
from ironic_python_agent import errors
@@ -377,14 +378,18 @@ def create_config_drive_partition(node_uuid, device, configdrive):
"%(part)s",
{'node': node_uuid, 'part': config_drive_part})
else:
- cur_parts = set(part['number']
- for part in disk_utils.list_partitions(device))
-
+ part_uuid = None
if disk_utils.get_partition_table_type(device) == 'gpt':
+ part_uuid = uuidutils.generate_uuid()
create_option = '0:-%dMB:0' % MAX_CONFIG_DRIVE_SIZE_MB
- utils.execute('sgdisk', '-n', create_option, device,
+ uuid_option = '0:%s' % part_uuid
+ utils.execute('sgdisk', '-n', create_option,
+ '-u', uuid_option, device,
run_as_root=True)
else:
+ cur_parts = set(part['number']
+ for part in disk_utils.list_partitions(device))
+
# Check if the disk has 4 partitions. The MBR based disk
# cannot have more than 4 partitions.
# TODO(stendulker): One can use logical partitions to create
@@ -426,17 +431,29 @@ def create_config_drive_partition(node_uuid, device, configdrive):
# Trigger device rescan
disk_utils.trigger_device_rescan(device)
- upd_parts = set(part['number']
- for part in disk_utils.list_partitions(device))
- new_part = set(upd_parts) - set(cur_parts)
- if len(new_part) != 1:
- raise exception.InstanceDeployFailure(
- 'Disk partitioning failed on device %(device)s. '
- 'Unable to retrieve config drive partition information.'
- % {'device': device})
+ if part_uuid is None:
+ new_parts = {part['number']: part
+ for part in disk_utils.list_partitions(device)}
+ new_part = set(new_parts) - set(cur_parts)
+ if len(new_part) != 1:
+ raise exception.InstanceDeployFailure(
+ 'Disk partitioning failed on device %(device)s. '
+ 'Unable to retrieve config drive partition '
+ 'information.' % {'device': device})
- config_drive_part = disk_utils.partition_index_to_path(
- device, new_part.pop())
+ config_drive_part = disk_utils.partition_index_to_path(
+ device, new_part.pop())
+ else:
+ try:
+ config_drive_part = get_partition(device, part_uuid)
+ except errors.DeviceNotFound:
+ msg = ('Failed to create config drive on disk %(disk)s '
+ 'for node %(node)s. Partition with UUID %(uuid)s '
+ 'has not been found after creation.') % {
+ 'disk': device, 'node': node_uuid,
+ 'uuid': part_uuid}
+ LOG.error(msg)
+ raise exception.InstanceDeployFailure(msg)
disk_utils.udev_settle()
diff --git a/ironic_python_agent/tests/unit/test_partition_utils.py b/ironic_python_agent/tests/unit/test_partition_utils.py
index 88122ed5..1d232490 100644
--- a/ironic_python_agent/tests/unit/test_partition_utils.py
+++ b/ironic_python_agent/tests/unit/test_partition_utils.py
@@ -656,6 +656,7 @@ class CreateConfigDriveTestCases(base.IronicAgentTest):
mock_dd.assert_called_with(configdrive_file, configdrive_part)
mock_unlink.assert_called_with(configdrive_file)
+ @mock.patch('oslo_utils.uuidutils.generate_uuid', lambda: 'fake-uuid')
@mock.patch.object(utils, 'execute', autospec=True)
@mock.patch.object(utils, 'unlink_without_raise',
autospec=True)
@@ -665,7 +666,7 @@ class CreateConfigDriveTestCases(base.IronicAgentTest):
autospec=True)
@mock.patch.object(disk_utils, 'get_partition_table_type',
autospec=True)
- @mock.patch.object(disk_utils, 'list_partitions',
+ @mock.patch.object(partition_utils, 'get_partition',
autospec=True)
@mock.patch.object(partition_utils, 'get_labelled_partition',
autospec=True)
@@ -673,42 +674,25 @@ class CreateConfigDriveTestCases(base.IronicAgentTest):
autospec=True)
def test_create_partition_gpt(self, mock_get_configdrive,
mock_get_labelled_partition,
- mock_list_partitions, mock_table_type,
+ mock_get_partition_by_uuid,
+ mock_table_type,
mock_fix_gpt_partition,
mock_dd, mock_unlink, mock_execute):
config_url = 'http://1.2.3.4/cd'
configdrive_file = '/tmp/xyz'
configdrive_mb = 10
- initial_partitions = [{'end': 49152, 'number': 1, 'start': 1,
- 'flags': 'boot', 'filesystem': 'ext4',
- 'size': 49151},
- {'end': 51099, 'number': 3, 'start': 49153,
- 'flags': '', 'filesystem': '', 'size': 2046},
- {'end': 51099, 'number': 5, 'start': 49153,
- 'flags': '', 'filesystem': '', 'size': 2046}]
- updated_partitions = [{'end': 49152, 'number': 1, 'start': 1,
- 'flags': 'boot', 'filesystem': 'ext4',
- 'size': 49151},
- {'end': 51099, 'number': 3, 'start': 49153,
- 'flags': '', 'filesystem': '', 'size': 2046},
- {'end': 51099, 'number': 4, 'start': 49153,
- 'flags': '', 'filesystem': '', 'size': 2046},
- {'end': 51099, 'number': 5, 'start': 49153,
- 'flags': '', 'filesystem': '', 'size': 2046}]
-
mock_get_configdrive.return_value = (configdrive_mb, configdrive_file)
mock_get_labelled_partition.return_value = None
mock_table_type.return_value = 'gpt'
- mock_list_partitions.side_effect = [initial_partitions,
- updated_partitions]
expected_part = '/dev/fake4'
+ mock_get_partition_by_uuid.return_value = expected_part
partition_utils.create_config_drive_partition(self.node_uuid, self.dev,
config_url)
mock_execute.assert_has_calls([
- mock.call('sgdisk', '-n', '0:-64MB:0', self.dev,
- run_as_root=True),
+ mock.call('sgdisk', '-n', '0:-64MB:0', '-u', '0:fake-uuid',
+ self.dev, run_as_root=True),
mock.call('sync'),
mock.call('udevadm', 'settle'),
mock.call('partprobe', self.dev, attempts=10, run_as_root=True),
@@ -719,7 +703,6 @@ class CreateConfigDriveTestCases(base.IronicAgentTest):
delay_on_retry=True)
])
- self.assertEqual(2, mock_list_partitions.call_count)
mock_table_type.assert_called_with(self.dev)
mock_fix_gpt_partition.assert_called_with(self.dev, self.node_uuid)
mock_dd.assert_called_with(configdrive_file, expected_part)
diff --git a/releasenotes/notes/configdrive-partuuid-3259cfb7428c1483.yaml b/releasenotes/notes/configdrive-partuuid-3259cfb7428c1483.yaml
new file mode 100644
index 00000000..95344273
--- /dev/null
+++ b/releasenotes/notes/configdrive-partuuid-3259cfb7428c1483.yaml
@@ -0,0 +1,17 @@
+---
+issues:
+ - |
+ Creating a configdrive partition on a devicemapper device (e.g. a multipath
+ storage device) with MBR partitioning may fail with the following error::
+
+ Command execution failed: Failed to create config drive on disk /dev/dm-0
+ for node 168af30d-0fad-4d67-af99-b28b3238e977. Error: Unexpected error
+ while running command.
+
+ Use GPT partitioning instead.
+fixes:
+ - |
+ Fixes creating a configdrive partition on a devicemapper device (e.g.
+ a multipath storage device) with GPT partitioning. The newly created
+ partition is now detected by a pre-generated UUID rather than by comparing
+ partition numbers.
diff --git a/releasenotes/notes/detect-endianness-f53a6c4571aba3fe.yaml b/releasenotes/notes/detect-endianness-f53a6c4571aba3fe.yaml
new file mode 100644
index 00000000..82d90886
--- /dev/null
+++ b/releasenotes/notes/detect-endianness-f53a6c4571aba3fe.yaml
@@ -0,0 +1,6 @@
+---
+fixes:
+ - |
+ In case the CSV file used for the bootloader hint does not have BOM
+ we fail reading its content as utf-16 codec is too generic.
+ Fail over to utf-16-le as Little Endian is mostly used.
diff --git a/tox.ini b/tox.ini
index ccbe9fa5..4c47f960 100644
--- a/tox.ini
+++ b/tox.ini
@@ -15,7 +15,7 @@ setenv =
LANGUAGE=en_US
LC_ALL=en_US.utf-8
deps =
- -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
+ -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/yoga}
-r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands = stestr run {posargs}
@@ -71,7 +71,7 @@ setenv = PYTHONHASHSEED=0
sitepackages = False
# NOTE(dtantsur): documentation building process requires importing IPA
deps =
- -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
+ -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/yoga}
-r{toxinidir}/requirements.txt
-r{toxinidir}/doc/requirements.txt
commands =
@@ -89,7 +89,7 @@ commands =
[testenv:releasenotes]
usedevelop = False
deps =
- -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
+ -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/yoga}
-r{toxinidir}/doc/requirements.txt
commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
@@ -128,7 +128,7 @@ commands =
[testenv:bandit]
usedevelop = False
deps =
- -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
+ -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/yoga}
-r{toxinidir}/test-requirements.txt
commands = bandit -r ironic_python_agent -x tests -n5 -ll -c tools/bandit.yml
diff --git a/zuul.d/ironic-python-agent-jobs.yaml b/zuul.d/ironic-python-agent-jobs.yaml
index a5cb2977..06b2d894 100644
--- a/zuul.d/ironic-python-agent-jobs.yaml
+++ b/zuul.d/ironic-python-agent-jobs.yaml
@@ -14,6 +14,12 @@
- ^tools/.*$
- ^tox.ini$
required-projects:
+ - name: openstack/devstack
+ override-checkout: stable/yoga
+ - name: openstack/requirements
+ override-checkout: stable/yoga
+ - name: openstack/ironic
+ override-checkout: bugfix/20.0
- openstack/ironic-lib
vars:
# The default is 1GB, we need a little more to prevent OOMs killing the jobs
@@ -128,6 +134,16 @@
name: ironic-standalone-ipa-src
parent: ironic-standalone
description: Test ironic standalone with IPA from source
+ required-projects:
+ - name: openstack/devstack
+ override-checkout: stable/yoga
+ - name: openstack/requirements
+ override-checkout: stable/yoga
+ - name: openstack/ironic
+ override-checkout: bugfix/20.0
+ - name: openstack/ironic-python-agent-builder
+ override-checkout: stable/yoga
+ - openstack/ironic-lib
vars:
devstack_localrc:
IRONIC_BUILD_DEPLOY_RAMDISK: True
@@ -140,8 +156,17 @@
name: metalsmith-integration-ipa-src-uefi
parent: metalsmith-integration-glance-localboot-centos8-uefi
required-projects:
+ - name: openstack/devstack
+ override-checkout: stable/yoga
+ - name: openstack/requirements
+ override-checkout: stable/yoga
+ - name: openstack/ironic
+ override-checkout: bugfix/20.0
+ - name: openstack/metalsmith
+ override-checkout: stable/yoga
- openstack/ironic-python-agent
- - openstack/ironic-python-agent-builder
+ - name: openstack/ironic-python-agent-builder
+ override-checkout: stable/yoga
- openstack/ironic-lib
vars:
devstack_localrc:
@@ -157,3 +182,40 @@
devstack_localrc:
IRONIC_RAMDISK_TYPE: tinyipa
IRONIC_VM_SPECS_RAM: 1024
+
+- project-template:
+ name: openstack-python3-yoga-jobs-ironic-bugfix
+ description: |
+ Runs unit tests for an OpenStack Python project under the CPython
+ version 3 releases designated for testing in the Yoga release.
+ check:
+ jobs:
+ - openstack-tox-pep8:
+ required-projects:
+ - name: openstack/requirements
+ override-checkout: stable/yoga
+ - openstack-tox-py36:
+ required-projects:
+ - name: openstack/requirements
+ override-checkout: stable/yoga
+ - openstack-tox-py39:
+ required-projects:
+ - name: openstack/requirements
+ override-checkout: stable/yoga
+ gate:
+ jobs:
+ - openstack-tox-pep8:
+ required-projects:
+ - name: openstack/requirements
+ override-checkout: stable/yoga
+ - openstack-tox-py36:
+ required-projects:
+ - name: openstack/requirements
+ override-checkout: stable/yoga
+ - openstack-tox-py39:
+ required-projects:
+ - name: openstack/requirements
+ override-checkout: stable/yoga
+ post:
+ jobs:
+ - publish-openstack-python-branch-tarball
diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml
index fa87feaf..c04ee4d5 100644
--- a/zuul.d/project.yaml
+++ b/zuul.d/project.yaml
@@ -1,14 +1,19 @@
- project:
templates:
- check-requirements
- - openstack-cover-jobs
- - openstack-lower-constraints-master-branch-jobs
- - openstack-python3-yoga-jobs
+ - openstack-python3-yoga-jobs-ironic-bugfix
- publish-openstack-docs-pti
- release-notes-jobs-python3
check:
jobs:
- - openstack-tox-functional
+ - openstack-tox-functional:
+ required-projects:
+ - name: openstack/requirements
+ override-checkout: stable/yoga
+ - openstack-tox-cover:
+ required-projects:
+ - name: openstack/requirements
+ override-checkout: stable/yoga
- ipa-tox-examples
# NOTE(iurygregory) Only run this two jobs since we are testing
# wholedisk + partition on tempest
@@ -30,7 +35,14 @@
gate:
queue: ironic
jobs:
- - openstack-tox-functional
+ - openstack-tox-functional:
+ required-projects:
+ - name: openstack/requirements
+ override-checkout: stable/yoga
+ - openstack-tox-cover:
+ required-projects:
+ - name: openstack/requirements
+ override-checkout: stable/yoga
- ipa-tempest-bios-ipmi-direct-src
- ipa-tempest-uefi-redfish-vmedia-src
- metalsmith-integration-ipa-src-uefi