From 789bff29fc579d39fcb37b22eb3276662d4971d1 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 24 Jul 2020 12:08:29 +0100 Subject: [PATCH] privsep: Add support for recursive chown, move_tree operations We'll need these shortly to handle shuffling of vTPM files during cold migrations and resizes. Part of blueprint add-emulated-virtual-tpm Change-Id: I7c8dedc9e74117838db5d418dda1dcbe7143ad08 Signed-off-by: Stephen Finucane --- mypy-files.txt | 1 + nova/privsep/path.py | 21 ++++++++++++++++-- nova/tests/unit/privsep/test_path.py | 32 +++++++++++++++++++++++++++- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/mypy-files.txt b/mypy-files.txt index 11aa91ec41c2..017be88f07cd 100644 --- a/mypy-files.txt +++ b/mypy-files.txt @@ -1,5 +1,6 @@ nova/compute/manager.py nova/crypto.py +nova/privsep/path.py nova/virt/driver.py nova/virt/hardware.py nova/virt/libvirt/__init__.py diff --git a/nova/privsep/path.py b/nova/privsep/path.py index d67247a98e97..869a05eb2d10 100644 --- a/nova/privsep/path.py +++ b/nova/privsep/path.py @@ -17,6 +17,7 @@ import errno import os +import shutil from oslo_utils import fileutils @@ -48,10 +49,21 @@ def readlink(path): @nova.privsep.sys_admin_pctxt.entrypoint -def chown(path, uid=-1, gid=-1): +def chown( + path: str, uid: int = -1, gid: int = -1, recursive: bool = False, +) -> None: if not os.path.exists(path): raise exception.FileNotFound(file_path=path) - return os.chown(path, uid, gid) + + if not recursive or os.path.isfile(path): + return os.chown(path, uid, gid) + + for root, dirs, files in os.walk(path): + os.chown(root, uid, gid) + for item in dirs: + os.chown(os.path.join(root, item), uid, gid) + for item in files: + os.chown(os.path.join(root, item), uid, gid) @nova.privsep.sys_admin_pctxt.entrypoint @@ -66,6 +78,11 @@ def chmod(path, mode): os.chmod(path, mode) +@nova.privsep.sys_admin_pctxt.entrypoint +def move_tree(source_path: str, dest_path: str) -> None: + shutil.move(source_path, dest_path) + + @nova.privsep.sys_admin_pctxt.entrypoint def utime(path): if not os.path.exists(path): diff --git a/nova/tests/unit/privsep/test_path.py b/nova/tests/unit/privsep/test_path.py index aec5ea17e676..faf2933de5e4 100644 --- a/nova/tests/unit/privsep/test_path.py +++ b/nova/tests/unit/privsep/test_path.py @@ -83,7 +83,7 @@ class FileTestCase(test.NoDBTestCase): def test_chown(self, mock_chown, mock_exists): nova.privsep.path.chown('/fake/path', uid=42, gid=43) mock_exists.assert_called_with('/fake/path') - mock_chown.assert_called_with('/fake/path', 42, 43) + mock_chown.assert_called_once_with('/fake/path', 42, 43) @mock.patch('os.path.exists', return_value=False) def test_chown_file_not_found(self, mock_exists): @@ -91,6 +91,36 @@ class FileTestCase(test.NoDBTestCase): nova.privsep.path.chown, '/fake/path') + @mock.patch('os.walk', return_value=[('.', ['foo'], ['bar.py'])]) + @mock.patch('os.path.isfile', return_value=False) + @mock.patch('os.path.exists', return_value=True) + @mock.patch('os.chown') + def test_chown_recursive( + self, mock_chown, mock_exists, mock_isfile, mock_walk, + ): + nova.privsep.path.chown('/fake/path', uid=42, gid=43, recursive=True) + mock_exists.assert_called_with('/fake/path') + mock_isfile.assert_called_once_with('/fake/path') + mock_walk.assert_called_once_with('/fake/path') + mock_chown.assert_has_calls([ + mock.call('.', 42, 43), + mock.call('./foo', 42, 43), + mock.call('./bar.py', 42, 43), + ]) + + @mock.patch('os.walk') + @mock.patch('os.path.isfile', return_value=True) + @mock.patch('os.path.exists', return_value=True) + @mock.patch('os.chown') + def test_chown_recursive_is_file( + self, mock_chown, mock_exists, mock_isfile, mock_walk, + ): + nova.privsep.path.chown('/fake/path', uid=42, gid=43, recursive=True) + mock_exists.assert_called_with('/fake/path') + mock_isfile.assert_called_once_with('/fake/path') + mock_chown.assert_called_once_with('/fake/path', 42, 43) + mock_walk.assert_not_called() + @mock.patch('oslo_utils.fileutils.ensure_tree') def test_makedirs(self, mock_ensure_tree): nova.privsep.path.makedirs('/fake/path')