diff --git a/docker/services/nova-compute.yaml b/docker/services/nova-compute.yaml index 28e5bb1bfb..1490e6465c 100644 --- a/docker/services/nova-compute.yaml +++ b/docker/services/nova-compute.yaml @@ -85,6 +85,13 @@ parameters: default: false description: Whether config-download method is used or not. type: boolean + 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' resources: @@ -201,6 +208,13 @@ outputs: - '' - - 'TRIPLEO_DEPLOY_IDENTIFIER=' - {get_param: DeployIdentifier} + - list_join: + - '=' + - - 'NOVA_STATEDIR_OWNERSHIP_SKIP' + - list_join: + - ':' + - {get_param: NovaStatedirOwnershipSkip} + step_4: map_merge: - nova_wait_for_placement_service: diff --git a/docker_config_scripts/nova_statedir_ownership.py b/docker_config_scripts/nova_statedir_ownership.py index d9f54a4421..af6a0dad8e 100644 --- a/docker_config_scripts/nova_statedir_ownership.py +++ b/docker_config_scripts/nova_statedir_ownership.py @@ -107,13 +107,20 @@ class NovaStatedirOwnershipManager(object): docker nova uid/gid is not known in this context). """ def __init__(self, statedir, upgrade_marker='upgrade_marker', - nova_user='nova'): + nova_user='nova', exclude_paths=None): self.statedir = statedir self.nova_user = nova_user self.upgrade_marker_path = os.path.join(statedir, upgrade_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) != \ @@ -134,7 +141,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: @@ -181,5 +188,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/docker_config_scripts/tests/test_nova_statedir_ownership.py b/docker_config_scripts/tests/test_nova_statedir_ownership.py index 7e5a778c16..df86df9008 100644 --- a/docker_config_scripts/tests/test_nova_statedir_ownership.py +++ b/docker_config_scripts/tests/test_nova_statedir_ownership.py @@ -15,12 +15,14 @@ import contextlib import mock +import os from os import stat as orig_stat import six import stat from oslotest import base +from docker_config_scripts.nova_statedir_ownership import get_exclude_paths # noqa: E402 from docker_config_scripts.nova_statedir_ownership import \ NovaStatedirOwnershipManager # noqa: E402 from docker_config_scripts.nova_statedir_ownership import PathManager # noqa: E402 @@ -311,3 +313,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)