diff --git a/container_config_scripts/nova_statedir_ownership.py b/container_config_scripts/nova_statedir_ownership.py index dccb47cb3c..b605980509 100644 --- a/container_config_scripts/nova_statedir_ownership.py +++ b/container_config_scripts/nova_statedir_ownership.py @@ -137,7 +137,8 @@ class NovaStatedirOwnershipManager(object): docker nova uid/gid is not known in this context). """ def __init__(self, statedir, upgrade_marker='upgrade_marker', - nova_user='nova', secontext_marker='../_nova_secontext'): + nova_user='nova', secontext_marker='../_nova_secontext', + exclude_paths=None): self.statedir = statedir self.nova_user = nova_user @@ -145,6 +146,13 @@ class NovaStatedirOwnershipManager(object): self.secontext_marker_path = os.path.normpath(os.path.join(statedir, secontext_marker)) self.upgrade = os.path.exists(self.upgrade_marker_path) + self.exclude_paths = [self.upgrade_marker_path] + if exclude_paths is not None: + for p in exclude_paths: + if not p.startswith(os.path.sep): + p = os.path.join(self.statedir, p) + self.exclude_paths.append(p) + self.target_uid, self.target_gid = self._get_nova_ids() self.previous_uid, self.previous_gid = self._get_previous_nova_ids() self.id_change = (self.target_uid, self.target_gid) != \ @@ -172,7 +180,7 @@ class NovaStatedirOwnershipManager(object): for f in os.listdir(top): pathname = os.path.join(top, f) - if pathname == self.upgrade_marker_path: + if pathname in self.exclude_paths: continue try: @@ -228,5 +236,12 @@ class NovaStatedirOwnershipManager(object): LOG.info('Nova statedir ownership complete') +def get_exclude_paths(): + exclude_paths = os.environ.get('NOVA_STATEDIR_OWNERSHIP_SKIP') + if exclude_paths is not None: + exclude_paths = exclude_paths.split(os.pathsep) + return exclude_paths + + if __name__ == '__main__': - NovaStatedirOwnershipManager('/var/lib/nova').run() + NovaStatedirOwnershipManager('/var/lib/nova', exclude_paths=get_exclude_paths()) diff --git a/container_config_scripts/tests/test_nova_statedir_ownership.py b/container_config_scripts/tests/test_nova_statedir_ownership.py index f8d6b6cc56..6389ee8579 100644 --- a/container_config_scripts/tests/test_nova_statedir_ownership.py +++ b/container_config_scripts/tests/test_nova_statedir_ownership.py @@ -16,6 +16,7 @@ from unittest import mock import contextlib +import os from os import stat as orig_stat import six import stat @@ -36,6 +37,7 @@ class FakeSelinux(object): sys.modules["selinux"] = FakeSelinux +from container_config_scripts.nova_statedir_ownership import get_exclude_paths # noqa: E402 from container_config_scripts.nova_statedir_ownership import \ NovaStatedirOwnershipManager # noqa: E402 from container_config_scripts.nova_statedir_ownership import PathManager # noqa: E402 @@ -395,3 +397,31 @@ class NovaStatedirOwnershipManagerTestCase(base.BaseTestCase): for fn, expected in six.iteritems(expected_changes): assert_ids(testtree, fn, expected[0], expected[1]) fake_unlink.assert_called_with('/var/lib/nova/upgrade_marker') + + def test_exclude_path(self): + testtree = generate_testtree1(current_uid, current_gid) + + with fake_testtree(testtree) as ( + fake_chown, _, fake_listdir, fake_stat, _, _, _): + manager = NovaStatedirOwnershipManager( + '/var/lib/nova', + exclude_paths=['instances/foo/bar', '/var/lib/nova/instances/foo/removeddir'] + ) + manager.run() + self.assertIn('/var/lib/nova/instances/foo/bar', manager.exclude_paths) + self.assertIn('/var/lib/nova/instances/foo/removeddir', manager.exclude_paths) + self.assertNotIn(mock.call('/var/lib/nova/instances/foo/bar'), fake_stat.call_args_list) + self.assertNotIn(mock.call('/var/lib/nova/instances/foo/bar'), fake_chown.call_args_list) + self.assertNotIn(mock.call('/var/lib/nova/instances/foo/removeddir'), fake_stat.call_args_list) + self.assertNotIn(mock.call('/var/lib/nova/instances/foo/removeddir'), fake_chown.call_args_list) + self.assertNotIn(mock.call('/var/lib/nova/instances/foo/removeddir'), fake_listdir.call_args_list) + + @mock.patch.dict(os.environ, {'NOVA_STATEDIR_OWNERSHIP_SKIP': 'foo:bar:foo/bar/baz'}) + def test_get_exclude_paths(self): + expected = [ + 'foo', + 'bar', + 'foo/bar/baz' + ] + exclude_paths = get_exclude_paths() + self.assertEqual(exclude_paths, expected) diff --git a/deployment/nova/nova-compute-container-puppet.yaml b/deployment/nova/nova-compute-container-puppet.yaml index 2c610ce518..f3c1a38594 100644 --- a/deployment/nova/nova-compute-container-puppet.yaml +++ b/deployment/nova/nova-compute-container-puppet.yaml @@ -500,6 +500,13 @@ parameters: default: false tags: - role_specific + NovaStatedirOwnershipSkip: + type: comma_delimited_list + description: > + List of paths relative to nova_statedir to ignore when recursively setting the + ownership and selinux context. + default: + - 'triliovault-mounts' # DEPRECATED: the following options are deprecated and are currently maintained # for backwards compatibility. They will be removed in future release. @@ -917,6 +924,10 @@ outputs: expression: str($.data.debug) data: debug: {get_attr: [NovaBase, role_data, config_settings, 'nova::logging::debug']} + NOVA_STATEDIR_OWNERSHIP_SKIP: + list_join: + - ':' + - {get_param: NovaStatedirOwnershipSkip} step_5: map_merge: - nova_compute: