Handle situation when a configdrive is already mounted

Glean mounts the configdrive and does not unmount it afterwards.
If a mount point already exists, just use it.

Change-Id: Ia62279afbb9fd9770864942dc40629b69ae8f4ae
This commit is contained in:
Dmitry Tantsur 2020-12-16 18:17:24 +01:00
parent 9ae99506c0
commit d69f12e0fd
2 changed files with 92 additions and 24 deletions

View File

@ -1028,21 +1028,35 @@ class TestCopyConfigFromVmedia(testtools.TestCase):
mock_copy.assert_not_called()
def test_no_files(self, mock_execute, mock_copy, mock_find_device):
mock_execute.side_effect = [
processutils.ProcessExecutionError,
('', ''),
('', ''),
]
mock_find_device.return_value = '/dev/something'
utils.copy_config_from_vmedia()
mock_execute.assert_has_calls([
mock.call('findmnt', '-n', '-oTARGET', '/dev/something'),
mock.call('mount', '/dev/something', mock.ANY),
mock.call('umount', mock.ANY),
])
mock_copy.assert_not_called()
def test_mounted_no_files(self, mock_execute, mock_copy, mock_find_device):
mock_execute.return_value = '/some/path', ''
mock_find_device.return_value = '/dev/something'
utils.copy_config_from_vmedia()
mock_execute.assert_called_once_with(
'findmnt', '-n', '-oTARGET', '/dev/something')
mock_copy.assert_not_called()
@mock.patch.object(os, 'makedirs', autospec=True)
def test_copy(self, mock_makedirs, mock_execute, mock_copy,
mock_find_device):
mock_find_device.return_value = '/dev/something'
path = None
def _fake_exec(command, arg1, arg2=None):
def _fake_exec(command, arg1, arg2=None, *args):
nonlocal path
if command == 'mount':
path = arg2
@ -1059,6 +1073,8 @@ class TestCopyConfigFromVmedia(testtools.TestCase):
with open(os.path.join(path, 'etc', 'ironic-python-agent.d',
'ironic.conf'), 'wt') as fp:
fp.write('I am a config')
elif command == 'findmnt':
raise processutils.ProcessExecutionError("")
else:
self.assertEqual('umount', command)
@ -1080,3 +1096,39 @@ class TestCopyConfigFromVmedia(testtools.TestCase):
mock.call(mock.ANY, '/etc/ironic-python-agent.d/ironic.conf'),
], any_order=True)
self.assertFalse(os.path.exists(path))
@mock.patch.object(os, 'makedirs', autospec=True)
def test_copy_mounted(self, mock_makedirs, mock_execute, mock_copy,
mock_find_device):
mock_find_device.return_value = '/dev/something'
path = tempfile.mkdtemp()
self.addCleanup(lambda: shutil.rmtree(path))
# NOTE(dtantsur): makedirs is mocked
os.mkdir(os.path.join(path, 'etc'))
os.mkdir(os.path.join(path, 'etc', 'ironic-python-agent'))
os.mkdir(os.path.join(path, 'etc', 'ironic-python-agent.d'))
with open(os.path.join(path, 'not copied'), 'wt') as fp:
fp.write('not copied')
with open(os.path.join(path, 'etc', 'ironic-python-agent',
'ironic.crt'), 'wt') as fp:
fp.write('I am a cert')
with open(os.path.join(path, 'etc', 'ironic-python-agent.d',
'ironic.conf'), 'wt') as fp:
fp.write('I am a config')
mock_execute.return_value = path, ''
mock_find_device.return_value = '/dev/something'
utils.copy_config_from_vmedia()
mock_makedirs.assert_has_calls([
mock.call('/etc/ironic-python-agent', exist_ok=True),
mock.call('/etc/ironic-python-agent.d', exist_ok=True),
], any_order=True)
mock_execute.assert_called_once_with(
'findmnt', '-n', '-oTARGET', '/dev/something')
mock_copy.assert_has_calls([
mock.call(mock.ANY, '/etc/ironic-python-agent/ironic.crt'),
mock.call(mock.ANY, '/etc/ironic-python-agent.d/ironic.conf'),
], any_order=True)

View File

@ -212,6 +212,39 @@ def _early_log(msg, *args):
print('ironic-python-agent:', msg % args, file=sys.stderr)
def _copy_config_from(path):
for ext in ('', '.d'):
src = os.path.join(path, 'etc', 'ironic-python-agent%s' % ext)
if not os.path.isdir(src):
_early_log('%s not found', src)
continue
dest = '/etc/ironic-python-agent%s' % ext
_early_log('Copying configuration from %s to %s', src, dest)
try:
os.makedirs(dest, exist_ok=True)
# TODO(dtantsur): use shutil.copytree(.., dirs_exist_ok=True)
# when the minimum supported Python is 3.8.
for name in os.listdir(src):
src_file = os.path.join(src, name)
dst_file = os.path.join(dest, name)
shutil.copy(src_file, dst_file)
except Exception as exc:
msg = ("Unable to copy vmedia configuration %s to %s: %s"
% (src, dest, exc))
raise errors.VirtualMediaBootError(msg)
def _find_mount_point(device):
try:
path, _e = execute('findmnt', '-n', '-oTARGET', device)
except processutils.ProcessExecutionError:
return
else:
return path.strip()
def copy_config_from_vmedia():
"""Copies any configuration from a virtual media device.
@ -223,29 +256,12 @@ def copy_config_from_vmedia():
_early_log('No virtual media device detected')
return
with _mounted(vmedia_device_file) as vmedia_mount_point:
for ext in ('', '.d'):
src = os.path.join(vmedia_mount_point, 'etc',
'ironic-python-agent%s' % ext)
if not os.path.isdir(src):
_early_log('%s not found', src)
continue
dest = '/etc/ironic-python-agent%s' % ext
_early_log('Copying configuration from %s to %s', src, dest)
try:
os.makedirs(dest, exist_ok=True)
# TODO(dtantsur): use shutil.copytree(.., dirs_exist_ok=True)
# when the minimum supported Python is 3.8.
for name in os.listdir(src):
src_file = os.path.join(src, name)
dst_file = os.path.join(dest, name)
shutil.copy(src_file, dst_file)
except Exception as exc:
msg = ("Unable to copy vmedia configuration %s to %s: %s"
% (src, dest, exc))
raise errors.VirtualMediaBootError(msg)
mounted = _find_mount_point(vmedia_device_file)
if mounted:
_copy_config_from(mounted)
else:
with _mounted(vmedia_device_file) as vmedia_mount_point:
_copy_config_from(vmedia_mount_point)
def _get_cached_params():