diff options
-rw-r--r-- | ironic/drivers/modules/image_utils.py | 10 | ||||
-rw-r--r-- | ironic/drivers/modules/pxe_base.py | 6 | ||||
-rw-r--r-- | ironic/tests/unit/drivers/modules/test_image_utils.py | 37 | ||||
-rw-r--r-- | ironic/tests/unit/drivers/modules/test_pxe.py | 12 | ||||
-rw-r--r-- | releasenotes/config.yaml | 5 | ||||
-rw-r--r-- | releasenotes/notes/fix-context-image-hardlink-16f452974abc7327.yaml | 7 | ||||
-rw-r--r-- | releasenotes/notes/prevent-pxe-retry-when-token-exists-a4f38f7da56c1397.yaml | 7 | ||||
-rw-r--r-- | reno.yaml | 4 | ||||
-rw-r--r-- | tox.ini | 12 | ||||
-rw-r--r-- | zuul.d/ironic-jobs.yaml | 2 | ||||
-rw-r--r-- | zuul.d/project.yaml | 1 |
11 files changed, 88 insertions, 15 deletions
diff --git a/ironic/drivers/modules/image_utils.py b/ironic/drivers/modules/image_utils.py index bb0dfa166..838189ca8 100644 --- a/ironic/drivers/modules/image_utils.py +++ b/ironic/drivers/modules/image_utils.py @@ -208,6 +208,16 @@ class ImageHandler(object): try: os.link(image_file, published_file) os.chmod(image_file, self._file_permission) + try: + utils.execute( + '/usr/sbin/restorecon', '-i', '-R', 'v', public_dir) + except FileNotFoundError as exc: + LOG.debug( + "Could not restore SELinux context on " + "%(public_dir)s, restorecon command not found.\n" + "Error: %(error)s", + {'public_dir': public_dir, + 'error': exc}) except OSError as exc: LOG.debug( diff --git a/ironic/drivers/modules/pxe_base.py b/ironic/drivers/modules/pxe_base.py index 317b65b85..78d7b5987 100644 --- a/ironic/drivers/modules/pxe_base.py +++ b/ironic/drivers/modules/pxe_base.py @@ -490,6 +490,12 @@ class PXEBaseMixin(object): def _should_retry_boot(node): # NOTE(dtantsur): this assumes IPA, do we need to make it generic? for field in ('agent_last_heartbeat', 'last_power_state_change'): + if node.driver_internal_info.get('agent_secret_token', False): + LOG.debug('Not retrying PXE boot for node %(node)s; an agent ' + 'token has been identified, meaning the agent ' + 'has started.', + {'node': node.uuid}) + return False if manager_utils.value_within_timeout( node.driver_internal_info.get(field), CONF.pxe.boot_retry_timeout): diff --git a/ironic/tests/unit/drivers/modules/test_image_utils.py b/ironic/tests/unit/drivers/modules/test_image_utils.py index 6d79629d9..4e382fbbf 100644 --- a/ironic/tests/unit/drivers/modules/test_image_utils.py +++ b/ironic/tests/unit/drivers/modules/test_image_utils.py @@ -105,47 +105,70 @@ class RedfishImageHandlerTestCase(db_base.DbTestCase): mock_swift_api.delete_object.assert_called_once_with( 'ironic_redfish_container', object_name) + @mock.patch.object(utils, 'execute', autospec=True) @mock.patch.object(os, 'chmod', autospec=True) @mock.patch.object(image_utils, 'shutil', autospec=True) @mock.patch.object(os, 'link', autospec=True) @mock.patch.object(os, 'mkdir', autospec=True) def test_publish_image_local_link( - self, mock_mkdir, mock_link, mock_shutil, mock_chmod): + self, mock_mkdir, mock_link, mock_shutil, mock_chmod, + mock_execute): self.config(use_swift=False, group='redfish') self.config(http_url='http://localhost', group='deploy') img_handler_obj = image_utils.ImageHandler(self.node.driver) - url = img_handler_obj.publish_image('file.iso', 'boot.iso') - self.assertEqual( 'http://localhost/redfish/boot.iso', url) + mock_mkdir.assert_called_once_with('/httpboot/redfish', 0o755) + mock_link.assert_called_once_with( + 'file.iso', '/httpboot/redfish/boot.iso') + mock_chmod.assert_called_once_with('file.iso', 0o644) + mock_execute.assert_called_once_with( + '/usr/sbin/restorecon', '-i', '-R', 'v', '/httpboot/redfish') + @mock.patch.object(utils, 'execute', autospec=True) + @mock.patch.object(os, 'chmod', autospec=True) + @mock.patch.object(image_utils, 'shutil', autospec=True) + @mock.patch.object(os, 'link', autospec=True) + @mock.patch.object(os, 'mkdir', autospec=True) + def test_publish_image_local_link_no_restorecon( + self, mock_mkdir, mock_link, mock_shutil, mock_chmod, + mock_execute): + self.config(use_swift=False, group='redfish') + self.config(http_url='http://localhost', group='deploy') + img_handler_obj = image_utils.ImageHandler(self.node.driver) + url = img_handler_obj.publish_image('file.iso', 'boot.iso') + self.assertEqual( + 'http://localhost/redfish/boot.iso', url) mock_mkdir.assert_called_once_with('/httpboot/redfish', 0o755) mock_link.assert_called_once_with( 'file.iso', '/httpboot/redfish/boot.iso') mock_chmod.assert_called_once_with('file.iso', 0o644) + mock_execute.return_value = FileNotFoundError + mock_shutil.assert_not_called() + @mock.patch.object(utils, 'execute', autospec=True) @mock.patch.object(os, 'chmod', autospec=True) @mock.patch.object(image_utils, 'shutil', autospec=True) @mock.patch.object(os, 'link', autospec=True) @mock.patch.object(os, 'mkdir', autospec=True) def test_publish_image_external_ip( - self, mock_mkdir, mock_link, mock_shutil, mock_chmod): + self, mock_mkdir, mock_link, mock_shutil, mock_chmod, + mock_execute): self.config(use_swift=False, group='redfish') self.config(http_url='http://localhost', external_http_url='http://non-local.host', group='deploy') img_handler_obj = image_utils.ImageHandler(self.node.driver) - url = img_handler_obj.publish_image('file.iso', 'boot.iso') - self.assertEqual( 'http://non-local.host/redfish/boot.iso', url) - mock_mkdir.assert_called_once_with('/httpboot/redfish', 0o755) mock_link.assert_called_once_with( 'file.iso', '/httpboot/redfish/boot.iso') mock_chmod.assert_called_once_with('file.iso', 0o644) + mock_execute.assert_called_once_with( + '/usr/sbin/restorecon', '-i', '-R', 'v', '/httpboot/redfish') @mock.patch.object(os, 'chmod', autospec=True) @mock.patch.object(image_utils, 'shutil', autospec=True) diff --git a/ironic/tests/unit/drivers/modules/test_pxe.py b/ironic/tests/unit/drivers/modules/test_pxe.py index d999a8f7a..be48f890e 100644 --- a/ironic/tests/unit/drivers/modules/test_pxe.py +++ b/ironic/tests/unit/drivers/modules/test_pxe.py @@ -1277,6 +1277,18 @@ class PXEBootRetryTestCase(db_base.DbTestCase): mock_boot_dev.assert_called_once_with(task, 'pxe', persistent=False) + def test_check_boot_status_not_retry_with_token(self, mock_power, + mock_boot_dev): + with task_manager.acquire(self.context, self.node.uuid, + shared=True) as task: + task.node.driver_internal_info = { + 'agent_secret_token': 'xyz' + } + task.driver.boot._check_boot_status(task) + self.assertTrue(task.shared) + mock_power.assert_not_called() + mock_boot_dev.assert_not_called() + class iPXEBootRetryTestCase(PXEBootRetryTestCase): diff --git a/releasenotes/config.yaml b/releasenotes/config.yaml new file mode 100644 index 000000000..26538010e --- /dev/null +++ b/releasenotes/config.yaml @@ -0,0 +1,5 @@ +--- +# Ignore the kilo-eol tag because that branch does not work with reno +# and contains no release notes. +# Ignore bugfix tags because their releasenotes are covered under stable +closed_branch_tag_re: 'r"(?!^(kilo-|bugfix-)).+-eol$"' diff --git a/releasenotes/notes/fix-context-image-hardlink-16f452974abc7327.yaml b/releasenotes/notes/fix-context-image-hardlink-16f452974abc7327.yaml new file mode 100644 index 000000000..90d38d5cc --- /dev/null +++ b/releasenotes/notes/fix-context-image-hardlink-16f452974abc7327.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + Fixes an issue where if selinux is enabled and enforcing, and + the published image is a hardlink, the source selinux context + is preserved, causing access denied when retrieving the image + using hardlink URL. diff --git a/releasenotes/notes/prevent-pxe-retry-when-token-exists-a4f38f7da56c1397.yaml b/releasenotes/notes/prevent-pxe-retry-when-token-exists-a4f38f7da56c1397.yaml new file mode 100644 index 000000000..5db6db6ec --- /dev/null +++ b/releasenotes/notes/prevent-pxe-retry-when-token-exists-a4f38f7da56c1397.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + Fixes a race condition in PXE initialization where logic to retry + what we suspect as potentially failed PXE boot operations was not + consulting if an ``agent token`` had been established, which is the + very first step in agent initialization. diff --git a/reno.yaml b/reno.yaml deleted file mode 100644 index dd0aac790..000000000 --- a/reno.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -# Ignore the kilo-eol tag because that branch does not work with reno -# and contains no release notes. -closed_branch_tag_re: "(.+)(?<!kilo)-eol" @@ -1,14 +1,15 @@ [tox] minversion = 3.18.0 -skipsdist = True envlist = py3,pep8 ignore_basepython_conflict=true +requires = + tox<4 [testenv] usedevelop = True basepython = python3 setenv = VIRTUAL_ENV={envdir} - PYTHONDONTWRITEBYTECODE = 1 + PYTHONDONTWRITEBYTECODE=1 LANGUAGE=en_US LC_ALL=en_US.UTF-8 PYTHONWARNINGS=default::DeprecationWarning @@ -18,7 +19,12 @@ deps = -r{toxinidir}/test-requirements.txt commands = stestr run --slowest {posargs} -passenv = http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY +passenv = http_proxy + HTTP_PROXY + https_proxy + HTTPS_PROXY + no_proxy + NO_PROXY [testenv:unit-with-driver-libs] deps = {[testenv]deps} diff --git a/zuul.d/ironic-jobs.yaml b/zuul.d/ironic-jobs.yaml index 44a59b78a..c02d4488f 100644 --- a/zuul.d/ironic-jobs.yaml +++ b/zuul.d/ironic-jobs.yaml @@ -24,6 +24,8 @@ override-checkout: stable/zed - name: openstack/requirements override-checkout: stable/zed + - name: openstack/cinder + override-checkout: stable/zed irrelevant-files: - ^.*\.rst$ - ^api-ref/.*$ diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml index 6bf317a45..9bff3446a 100644 --- a/zuul.d/project.yaml +++ b/zuul.d/project.yaml @@ -6,7 +6,6 @@ - openstack-python3-zed-jobs-ironic-bugfix202-arm64 - periodic-stable-jobs - publish-openstack-docs-pti - - release-notes-jobs-python3 check: jobs: - ironic-tox-unit-with-driver-libs |