From c352a78fe0ceef2a77728c46bb08ac4f12217842 Mon Sep 17 00:00:00 2001 From: Oliver Walsh Date: Fri, 9 Oct 2020 17:28:16 +0100 Subject: [PATCH] Skip Trilio dirs when setting ownership in /var/lib/nova Trilio currently mounts an NFS export in /var/lib/nova to make it accessible from within the nova_compute and nova_libvirt containers. This can result in considerable delays when walking the directory tree to ensure the ownership is correct. This patch adds the ability to skip paths when recursively setting the ownership and selinux context in /var/lib/nova. The list of paths to skip can be set via te NovaStatedirOwnershipSkip heat parameter. This default to the Trilio dir. Change-Id: Ic6f053d56194613046ae0a4a908206ebb453fcf4 (cherry picked from commit c156534010cf27d6cca0407962d1dbc484717410) (cherry picked from commit fbc55f02f21a600b3a9ca5c20c20794ad2ca86a4) --- .../nova_statedir_ownership.py | 21 +++++++++++-- .../tests/test_nova_statedir_ownership.py | 30 +++++++++++++++++++ .../nova/nova-compute-container-puppet.yaml | 11 +++++++ 3 files changed, 59 insertions(+), 3 deletions(-) diff --git a/container_config_scripts/nova_statedir_ownership.py b/container_config_scripts/nova_statedir_ownership.py index aeb98b651c..52953a5f65 100644 --- a/container_config_scripts/nova_statedir_ownership.py +++ b/container_config_scripts/nova_statedir_ownership.py @@ -138,7 +138,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 @@ -146,6 +147,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) != \ @@ -173,7 +181,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: @@ -229,5 +237,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 c2d3b0406d..b2094b2673 100644 --- a/container_config_scripts/tests/test_nova_statedir_ownership.py +++ b/container_config_scripts/tests/test_nova_statedir_ownership.py @@ -15,6 +15,7 @@ import contextlib import mock +import os from os import stat as orig_stat import six import stat @@ -33,6 +34,7 @@ class FakeSelinux(object): pass 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 @@ -392,3 +394,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 87511be516..cf96f215bd 100644 --- a/deployment/nova/nova-compute-container-puppet.yaml +++ b/deployment/nova/nova-compute-container-puppet.yaml @@ -480,6 +480,13 @@ parameters: default: '' 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. @@ -887,6 +894,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: