2c27834add
... by replacing it by the built-in unittest.mock . Change-Id: Ie853237d5c59730353d5ea4a7e61888e066614be
199 lines
8.1 KiB
Python
199 lines
8.1 KiB
Python
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import functools
|
|
import logging
|
|
import os
|
|
from unittest import mock
|
|
|
|
import diskimage_builder.block_device.tests.test_config as tc
|
|
|
|
from diskimage_builder.block_device.config import config_tree_to_graph
|
|
from diskimage_builder.block_device.config import create_graph
|
|
from diskimage_builder.block_device.level2.mkfs import FilesystemNode
|
|
from diskimage_builder.block_device.level3.mount import cmp_mount_order
|
|
from diskimage_builder.block_device.level3.mount import MountPointNode
|
|
from diskimage_builder.block_device.tests.test_base import TestBase
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class TestMountComparator(TestBase):
|
|
|
|
def test_mount_comparator(self):
|
|
# This tests cmp_mount_order to ensure it sorts in the
|
|
# expected order. The comparator takes a tuple of
|
|
# (mount_point, node_name) but we can ignore the name
|
|
partitions = [
|
|
('/var/log', 'fake_log'),
|
|
('/boot', 'fake_boot'),
|
|
('/', 'fake_name'),
|
|
('/var', 'fake_name')]
|
|
partitions.sort(key=functools.cmp_to_key(cmp_mount_order))
|
|
|
|
res = list(x[0] for x in partitions)
|
|
|
|
# "/" must be first
|
|
self.assertEqual(res[0], '/')
|
|
|
|
# /var before /var/log
|
|
var_pos = res.index('/var')
|
|
var_log_pos = res.index('/var/log')
|
|
self.assertGreater(var_log_pos, var_pos)
|
|
|
|
|
|
class TestMountOrder(tc.TestGraphGeneration):
|
|
|
|
def _exec_sudo_log(*args, **kwargs):
|
|
# Used as a side-effect from exec_sudo mocking so we can see
|
|
# the call in-place in logs
|
|
logger.debug("exec_sudo: %s", " ".join(args[0]))
|
|
|
|
@mock.patch('diskimage_builder.block_device.level3.mount.exec_sudo',
|
|
side_effect=_exec_sudo_log)
|
|
@mock.patch('diskimage_builder.block_device.level2.mkfs.exec_sudo',
|
|
side_effect=_exec_sudo_log)
|
|
def test_mfks_and_mount_order(self, mock_exec_sudo_mkfs,
|
|
mock_exec_sudo_mount):
|
|
# XXX: better mocking for the os.path.exists calls to avoid
|
|
# failing if this exists.
|
|
self.assertFalse(os.path.exists('/fake/'))
|
|
|
|
# This is probably in order after graph creation, so ensure it
|
|
# remains stable. We test the mount and umount call sequences
|
|
config = self.load_config_file('multiple_partitions_graph.yaml')
|
|
state = {}
|
|
graph, call_order = create_graph(config, self.fake_default_config,
|
|
state)
|
|
|
|
# Mocked block device state
|
|
state['blockdev'] = {}
|
|
state['blockdev']['root'] = {'device': '/dev/loopXp1/root'}
|
|
state['blockdev']['var'] = {'device': '/dev/loopXp2/var'}
|
|
state['blockdev']['var_log'] = {'device': '/dev/loopXp3/var_log'}
|
|
state['blockdev']['swap'] = {'device': '/dev/loopXp4/swap'}
|
|
|
|
for node in call_order:
|
|
if isinstance(node, (FilesystemNode, MountPointNode)):
|
|
node.create()
|
|
for node in reversed(call_order):
|
|
if isinstance(node, (FilesystemNode, MountPointNode)):
|
|
node.umount()
|
|
|
|
# ensure that partitions were mounted in order root->var->var/log
|
|
self.assertListEqual(state['mount_order'],
|
|
['/', '/var', '/var/log', 'none'])
|
|
|
|
# fs creation sequence (note we don't care about order of this
|
|
# as they're all independent)
|
|
cmd_sequence = [
|
|
mock.call(['mkfs', '-t', 'xfs', '-L', 'mkfs_root',
|
|
'-m', 'uuid=root-uuid-1234',
|
|
'-q', '/dev/loopXp1/root']),
|
|
mock.call(['mkfs', '-t', 'xfs', '-L', 'mkfs_var',
|
|
'-m', 'uuid=var-uuid-1234',
|
|
'-q', '/dev/loopXp2/var']),
|
|
mock.call(['mkfs', '-t', 'vfat', '-n', 'VARLOG',
|
|
'/dev/loopXp3/var_log']),
|
|
mock.call(['mkswap', '-L', 'mkfs_swap', '-U', 'swap-uuid-1234',
|
|
'/dev/loopXp4/swap'])
|
|
]
|
|
self.assertEqual(mock_exec_sudo_mkfs.call_count, len(cmd_sequence))
|
|
mock_exec_sudo_mkfs.assert_has_calls(cmd_sequence, any_order=True)
|
|
|
|
# Check mount sequence
|
|
cmd_sequence = [
|
|
# mount sequence
|
|
mock.call(['mkdir', '-p', '/fake/']),
|
|
mock.call(['mount', '/dev/loopXp1/root', '/fake/']),
|
|
mock.call(['mkdir', '-p', '/fake/var']),
|
|
mock.call(['mount', '/dev/loopXp2/var', '/fake/var']),
|
|
mock.call(['mkdir', '-p', '/fake/var/log']),
|
|
mock.call(['mount', '/dev/loopXp3/var_log', '/fake/var/log']),
|
|
# umount sequence
|
|
mock.call(['sync']),
|
|
# note /fake/var/log is a vfs partition to make sure
|
|
# we don't try to fstrim it
|
|
mock.call(['umount', '/fake/var/log']),
|
|
mock.call(['sync']),
|
|
mock.call(['fstrim', '--verbose', '/fake/var']),
|
|
mock.call(['umount', '/fake/var']),
|
|
mock.call(['sync']),
|
|
mock.call(['fstrim', '--verbose', '/fake/']),
|
|
mock.call(['umount', '/fake/'])
|
|
]
|
|
self.assertListEqual(mock_exec_sudo_mount.call_args_list, cmd_sequence)
|
|
|
|
@mock.patch('diskimage_builder.block_device.level3.mount.exec_sudo',
|
|
side_effect=_exec_sudo_log)
|
|
def test_mount_order_unsorted(self, mock_exec_sudo):
|
|
# As above, but this is out of order and gets sorted
|
|
# so that root is mounted first (and skips the mkfs testing).
|
|
config = self.load_config_file('lvm_tree_partition_ordering.yaml')
|
|
parsed_graph = config_tree_to_graph(config)
|
|
state = {}
|
|
|
|
graph, call_order = create_graph(parsed_graph,
|
|
self.fake_default_config,
|
|
state)
|
|
state['filesys'] = {
|
|
'mkfs_root': {
|
|
'device': '/dev/loopXp1',
|
|
'fstype': 'xfs'
|
|
},
|
|
'mkfs_var': {
|
|
'device': '/dev/loopXp2',
|
|
'fstype': 'xfs',
|
|
},
|
|
'mkfs_boot': {
|
|
'device': '/dev/loopXp3',
|
|
'fstype': 'vfat',
|
|
},
|
|
'mkfs_swap': {
|
|
'device': '/dev/loopXp4',
|
|
'fstype': 'swap',
|
|
},
|
|
}
|
|
|
|
for node in call_order:
|
|
if isinstance(node, MountPointNode):
|
|
node.create()
|
|
for node in reversed(call_order):
|
|
if isinstance(node, MountPointNode):
|
|
node.umount()
|
|
|
|
# ensure that partitions are mounted in order / -> /boot -> /var
|
|
# swap not mounted in runtime, but should be in 'mount_order' list
|
|
self.assertListEqual(state['mount_order'],
|
|
['/', '/boot', '/var', 'none'])
|
|
|
|
cmd_sequence = [
|
|
# mount sequence
|
|
mock.call(['mkdir', '-p', '/fake/']),
|
|
mock.call(['mount', '/dev/loopXp1', '/fake/']),
|
|
mock.call(['mkdir', '-p', '/fake/boot']),
|
|
mock.call(['mount', '/dev/loopXp3', '/fake/boot']),
|
|
mock.call(['mkdir', '-p', '/fake/var']),
|
|
mock.call(['mount', '/dev/loopXp2', '/fake/var']),
|
|
# umount sequence
|
|
mock.call(['sync']),
|
|
mock.call(['fstrim', '--verbose', '/fake/var']),
|
|
mock.call(['umount', '/fake/var']),
|
|
mock.call(['sync']),
|
|
# no trim on vfat /fake/boot
|
|
mock.call(['umount', '/fake/boot']),
|
|
mock.call(['sync']),
|
|
mock.call(['fstrim', '--verbose', '/fake/']),
|
|
mock.call(['umount', '/fake/'])
|
|
]
|
|
self.assertListEqual(mock_exec_sudo.call_args_list, cmd_sequence)
|