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 <stephenfin@redhat.com>
This commit is contained in:
Stephen Finucane
2020-07-24 12:08:29 +01:00
parent 44376d2e21
commit 789bff29fc
3 changed files with 51 additions and 3 deletions

View File

@@ -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

View File

@@ -17,6 +17,7 @@
import errno
import os
import shutil
from oslo_utils import fileutils
@@ -48,11 +49,22 @@ 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)
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
def makedirs(path):
@@ -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):

View File

@@ -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')