Пример #1
0
    def test_mount_order(self, mock_exec_sudo):

        config = self.load_config_file('multiple_partitions_graph.yaml')

        graph, call_order = create_graph(config, self.fake_default_config)

        result = {}
        result['filesys'] = {}
        result['filesys']['mkfs_root'] = {}
        result['filesys']['mkfs_root']['device'] = 'fake'
        result['filesys']['mkfs_var'] = {}
        result['filesys']['mkfs_var']['device'] = 'fake'
        result['filesys']['mkfs_var_log'] = {}
        result['filesys']['mkfs_var_log']['device'] = 'fake'

        rollback = []

        for node in call_order:
            if isinstance(node, MountPointNode):
                # XXX: do we even need to create?  We could test the
                # sudo arguments from the mock in the below asserts
                # too
                node.create(result, rollback)

        # ensure that partitions are mounted in order root->var->var/log
        self.assertListEqual(result['mount_order'], ['/', '/var', '/var/log'])
Пример #2
0
    def cmd_create(self):
        """Creates the block device"""

        logger.info("create() called")
        logger.debug("Using config [%s]", self.config)

        # Create a new, empty state
        state = BlockDeviceState()
        try:
            dg, call_order = create_graph(self.config, self.params, state)
            for node in call_order:
                node.create()
        except Exception:
            logger.exception("Create failed; rollback initiated")
            reverse_order = reversed(call_order)
            for node in reverse_order:
                node.rollback()
            # save the state for debugging
            state.save_state(self.state_json_file_name)
            logger.error("Rollback complete, exiting")
            raise

        # dump state and nodes, in order
        # XXX: we only dump the call_order (i.e. nodes) not the whole
        #      graph here, because later calls do not need the graph
        #      at this stage.  might they?
        state.save_state(self.state_json_file_name)
        pickle.dump(call_order, open(self.node_pickle_file_name, 'wb'))

        logger.info("create() finished")
        return 0
Пример #3
0
    def cmd_create(self):
        """Creates the block device"""

        logger.info("create() called")
        logger.debug("Using config [%s]", self.config)

        # Create a new, empty state
        state = BlockDeviceState()
        try:
            dg, call_order = create_graph(self.config, self.params, state)
            for node in call_order:
                node.create()
        except Exception:
            logger.exception("Create failed; rollback initiated")
            reverse_order = reversed(call_order)
            for node in reverse_order:
                node.rollback()
            # save the state for debugging
            state.save_state(self.state_json_file_name)
            logger.error("Rollback complete, exiting")
            raise

        # dump state and nodes, in order
        # XXX: we only dump the call_order (i.e. nodes) not the whole
        #      graph here, because later calls do not need the graph
        #      at this stage.  might they?
        state.save_state(self.state_json_file_name)
        pickle.dump(call_order, open(self.node_pickle_file_name, 'wb'))

        logger.info("create() finished")
        return 0
Пример #4
0
    def test_mount_order(self, mock_exec_sudo):

        config = self.load_config_file('multiple_partitions_graph.yaml')

        state = {}

        graph, call_order = create_graph(config, self.fake_default_config,
                                         state)

        # build up some fake state so that we don't have to mock out
        # all the parent calls that would really make these values, as
        # we just want to test MountPointNode
        state['filesys'] = {}
        state['filesys']['mkfs_root'] = {}
        state['filesys']['mkfs_root']['device'] = 'fake'
        state['filesys']['mkfs_var'] = {}
        state['filesys']['mkfs_var']['device'] = 'fake'
        state['filesys']['mkfs_var_log'] = {}
        state['filesys']['mkfs_var_log']['device'] = 'fake'

        for node in call_order:
            if isinstance(node, MountPointNode):
                # XXX: do we even need to create?  We could test the
                # sudo arguments from the mock in the below asserts
                # too
                node.create()

        # ensure that partitions are mounted in order root->var->var/log
        self.assertListEqual(state['mount_order'], ['/', '/var', '/var/log'])
Пример #5
0
    def test_mount_order(self, mock_exec_sudo):
        # 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)

        # build up some fake state so that we don't have to mock out
        # all the parent calls that would really make these values, as
        # we just want to test MountPointNode
        state['filesys'] = {
            'mkfs_root': {
                'device': 'fake_root_device'
            },
            'mkfs_var': {
                'device': 'fake_var_device'
            },
            'mkfs_var_log': {
                'device': 'fake_var_log_device'
            }
        }

        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 root->var->var/log
        self.assertListEqual(state['mount_order'], ['/', '/var', '/var/log'])

        cmd_sequence = [
            # mount sequence
            mock.call(['mkdir', '-p', '/fake/']),
            mock.call(['mount', 'fake_root_device', '/fake/']),
            mock.call(['mkdir', '-p', '/fake/var']),
            mock.call(['mount', 'fake_var_device', '/fake/var']),
            mock.call(['mkdir', '-p', '/fake/var/log']),
            mock.call(['mount', 'fake_var_log_device', '/fake/var/log']),
            # umount sequence
            mock.call(['sync']),
            mock.call(['fstrim', '--verbose', '/fake/var/log']),
            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.call_args_list, cmd_sequence)
    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',
            },
        }

        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
        self.assertListEqual(state['mount_order'], ['/', '/boot', '/var'])

        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)
Пример #7
0
    def test_gpt_efi(self, mock_exec_sudo):
        # Test the command-sequence for a GPT/EFI partition setup
        tree = self.load_config_file('gpt_efi.yaml')
        config = config_tree_to_graph(tree)

        state = BlockDeviceState()

        graph, call_order = create_graph(config, self.fake_default_config,
                                         state)

        # Create a fake temp backing file (we check the size of it,
        # etc).
        # TODO(ianw): exec_sudo is generically mocked out, thus the
        # actual creation is mocked out ... but we could do this
        # without root and use parted to create the partitions on this
        # for slightly better testing.  An exercise for another day...
        self.tmp_dir = fixtures.TempDir()
        self.useFixture(self.tmp_dir)
        self.image_path = os.path.join(self.tmp_dir.path, "image.raw")
        # should be sparse...
        image_create(self.image_path, 1024 * 1024 * 1024)
        logger.debug("Temp image in %s", self.image_path)

        # Fake state for the loopback device
        state['blockdev'] = {}
        state['blockdev']['image0'] = {}
        state['blockdev']['image0']['image'] = self.image_path
        state['blockdev']['image0']['device'] = "/dev/loopX"

        for node in call_order:
            if isinstance(node, PartitionNode):
                node.create()

        # check the parted call looks right
        parted_cmd = [
            'sgdisk', self.image_path, '-n', '1:0:+8M', '-t', '1:EF00', '-c',
            '1:ESP', '-n', '2:0:+8M', '-t', '2:EF02', '-c', '2:BSP', '-n',
            '3:0:+1006M', '-t', '3:8300', '-c', '3:Root Part'
        ]
        cmd_sequence = [
            mock.call(parted_cmd),
            mock.call(['sync']),
            mock.call(['kpartx', '-avs', '/dev/loopX'])
        ]
        self.assertEqual(mock_exec_sudo.call_count, len(cmd_sequence))
        mock_exec_sudo.assert_has_calls(cmd_sequence)

        # Check two new partitions appear in state correctly
        self.assertDictEqual(state['blockdev']['ESP'],
                             {'device': '/dev/mapper/loopXp1'})
        self.assertDictEqual(state['blockdev']['BSP'],
                             {'device': '/dev/mapper/loopXp2'})
        self.assertDictEqual(state['blockdev']['Root Part'],
                             {'device': '/dev/mapper/loopXp3'})
Пример #8
0
    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',
            },
        }

        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
        self.assertListEqual(state['mount_order'], ['/', '/boot', '/var'])

        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)
Пример #9
0
    def test_gpt_efi(self, mock_exec_sudo):
        # Test the command-sequence for a GPT/EFI partition setup
        tree = self.load_config_file('gpt_efi.yaml')
        config = config_tree_to_graph(tree)

        state = BlockDeviceState()

        graph, call_order = create_graph(config, self.fake_default_config,
                                         state)

        # Create a fake temp backing file (we check the size of it,
        # etc).
        # TODO(ianw): exec_sudo is generically mocked out, thus the
        # actual creation is mocked out ... but we could do this
        # without root and use parted to create the partitions on this
        # for slightly better testing.  An exercise for another day...
        self.tmp_dir = fixtures.TempDir()
        self.useFixture(self.tmp_dir)
        self.image_path = os.path.join(self.tmp_dir.path, "image.raw")
        # should be sparse...
        image_create(self.image_path, 1024 * 1024 * 1024)
        logger.debug("Temp image in %s", self.image_path)

        # Fake state for the loopback device
        state['blockdev'] = {}
        state['blockdev']['image0'] = {}
        state['blockdev']['image0']['image'] = self.image_path
        state['blockdev']['image0']['device'] = "/dev/loopX"

        for node in call_order:
            if isinstance(node, PartitionNode):
                node.create()

        # check the parted call looks right
        parted_cmd = ['sgdisk', self.image_path,
                      '-n', '1:0:+8M', '-t', '1:EF00', '-c', '1:ESP',
                      '-n', '2:0:+8M', '-t', '2:EF02', '-c', '2:BSP',
                      '-n', '3:0:+1006M', '-t', '3:8300', '-c', '3:Root Part']
        cmd_sequence = [
            mock.call(parted_cmd),
            mock.call(['sync']),
            mock.call(['kpartx', '-avs', '/dev/loopX'])
        ]
        self.assertEqual(mock_exec_sudo.call_count, len(cmd_sequence))
        mock_exec_sudo.assert_has_calls(cmd_sequence)

        # Check two new partitions appear in state correctly
        self.assertDictEqual(state['blockdev']['ESP'],
                             {'device': '/dev/mapper/loopXp1'})
        self.assertDictEqual(state['blockdev']['BSP'],
                             {'device': '/dev/mapper/loopXp2'})
        self.assertDictEqual(state['blockdev']['Root Part'],
                             {'device': '/dev/mapper/loopXp3'})
Пример #10
0
    def cmd_delete(self):
        """Cleanup all remaining relicts - in case of an error"""

        # Deleting must be done in reverse order
        dg, call_order = create_graph(self.config, self.params)
        reverse_order = reversed(call_order)

        for node in reverse_order:
            node.delete(self.state)

        logger.info("Removing temporary dir [%s]", self.state_dir)
        shutil.rmtree(self.state_dir)

        return 0
Пример #11
0
    def test_deep_graph_generator(self):
        config = self.load_config_file('deep_graph.yaml')

        graph, call_order = create_graph(config, self.fake_default_config, {})

        call_order_list = [n.name for n in call_order]

        # manually created from deep_graph.yaml
        # Note unlike below, the sort here is stable because the graph
        # doesn't have multiple paths with only one partition
        call_order_names = ['image0', 'root', 'mkfs_root',
                            'mount_mkfs_root',
                            'fstab_mount_mkfs_root']

        self.assertListEqual(call_order_list, call_order_names)
Пример #12
0
    def test_deep_graph_generator(self):
        config = self.load_config_file('deep_graph.yaml')

        graph, call_order = create_graph(config, self.fake_default_config, {})

        call_order_list = [n.name for n in call_order]

        # manually created from deep_graph.yaml
        # Note unlike below, the sort here is stable because the graph
        # doesn't have multiple paths with only one partition
        call_order_names = ['image0', 'root', 'mkfs_root',
                            'mount_mkfs_root',
                            'fstab_mount_mkfs_root']

        self.assertListEqual(call_order_list, call_order_names)
Пример #13
0
    def cmd_umount(self):
        """Unmounts the blockdevice and cleanup resources"""
        if self.state is None:
            logger.info("State already cleaned - no way to do anything here")
            return 0

        # Deleting must be done in reverse order
        dg, call_order = create_graph(self.config, self.params)
        reverse_order = reversed(call_order)

        if dg is None:
            return 0
        for node in reverse_order:
            node.umount(self.state)

        return 0
Пример #14
0
    def cmd_delete(self):
        """Cleanup all remaining relicts - in case of an error"""
        # State should have been created by prior calls; we only need
        # the dict
        state = BlockDeviceState(self.state_json_file_name)

        # Deleting must be done in reverse order
        dg, call_order = create_graph(self.config, self.params)
        reverse_order = reversed(call_order)

        for node in reverse_order:
            node.delete(state)

        logger.info("Removing temporary state dir [%s]", self.state_dir)
        shutil.rmtree(self.state_dir)

        return 0
Пример #15
0
    def cmd_umount(self):
        """Unmounts the blockdevice and cleanup resources"""

        # State should have been created by prior calls; we only need
        # the dict.  If it is not here, it has been cleaned up already
        # (? more details?)
        try:
            state = BlockDeviceState(self.state_json_file_name)
        except BlockDeviceSetupException:
            logger.info("State already cleaned - no way to do anything here")
            return 0

        # Deleting must be done in reverse order
        dg, call_order = create_graph(self.config, self.params)
        reverse_order = reversed(call_order)

        if dg is None:
            return 0
        for node in reverse_order:
            node.umount(state)

        return 0
Пример #16
0
    def test_multiple_partitions_graph_generator(self):
        config = self.load_config_file('multiple_partitions_graph.yaml')

        graph, call_order = create_graph(config, self.fake_default_config, {})
        call_order_list = [n.name for n in call_order]

        # The sort creating call_order_list is unstable.

        # We want to ensure we see the "partitions" object in
        # root->var->var_log order
        root_pos = call_order_list.index('root')
        var_pos = call_order_list.index('var')
        var_log_pos = call_order_list.index('var_log')
        self.assertGreater(var_pos, root_pos)
        self.assertGreater(var_log_pos, var_pos)

        # Ensure mkfs happens after partition
        mkfs_root_pos = call_order_list.index('mkfs_root')
        self.assertLess(root_pos, mkfs_root_pos)
        mkfs_var_pos = call_order_list.index('mkfs_var')
        self.assertLess(var_pos, mkfs_var_pos)
        mkfs_var_log_pos = call_order_list.index('mkfs_var_log')
        self.assertLess(var_log_pos, mkfs_var_log_pos)
Пример #17
0
    def test_multiple_partitions_graph_generator(self):
        config = self.load_config_file('multiple_partitions_graph.yaml')

        graph, call_order = create_graph(config, self.fake_default_config)
        call_order_list = [n.name for n in call_order]

        # The sort creating call_order_list is unstable.

        # We want to ensure we see the "partitions" object in
        # root->var->var_log order
        root_pos = call_order_list.index('root')
        var_pos = call_order_list.index('var')
        var_log_pos = call_order_list.index('var_log')
        self.assertGreater(var_pos, root_pos)
        self.assertGreater(var_log_pos, var_pos)

        # Ensure mkfs happens after partition
        mkfs_root_pos = call_order_list.index('mkfs_root')
        self.assertLess(root_pos, mkfs_root_pos)
        mkfs_var_pos = call_order_list.index('mkfs_var')
        self.assertLess(var_pos, mkfs_var_pos)
        mkfs_var_log_pos = call_order_list.index('mkfs_var_log')
        self.assertLess(var_log_pos, mkfs_var_log_pos)
Пример #18
0
    def cmd_create(self):
        """Creates the block device"""

        logger.info("create() called")
        logger.debug("Using config [%s]", self.config)

        rollback = []
        # Create a new, empty state
        state = BlockDeviceState()
        try:
            dg, call_order = create_graph(self.config, self.params)
            for node in call_order:
                node.create(state, rollback)
        except Exception:
            logger.exception("Create failed; rollback initiated")
            for rollback_cb in reversed(rollback):
                rollback_cb()
            sys.exit(1)

        state.save_state(self.state_json_file_name)

        logger.info("create() finished")
        return 0
    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'}

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

        # 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'])
        ]
        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)
Пример #20
0
    def test_lvm_multi_pv_vg(self):
        # Test the command-sequence for a more complicated LVM setup
        tree = self.load_config_file('lvm_tree_multiple_pv_vg.yaml')
        config = config_tree_to_graph(tree)

        state = BlockDeviceState()

        graph, call_order = create_graph(config, self.fake_default_config,
                                         state)

        # XXX: todo; test call_order.  Make sure PV's come before, VG;
        # VG before LV, and that mounts/etc happen afterwards.

        # Fake state for the two PV's specified by this config
        state['blockdev'] = {}
        state['blockdev']['root'] = {}
        state['blockdev']['root']['device'] = '/dev/fake/root'
        state['blockdev']['data'] = {}
        state['blockdev']['data']['device'] = '/dev/fake/data'

        # We mock patch this ... it's just a little long!
        exec_sudo = 'diskimage_builder.block_device.level1.lvm.exec_sudo'

        #
        # Creation test
        #
        with mock.patch(exec_sudo) as mock_exec_sudo:

            for node in call_order:
                # XXX: This has not mocked out the "lower" layers of
                # creating the devices, which we're assuming works OK, nor
                # the upper layers.
                if isinstance(node, (LVMNode, PvsNode,
                                     VgsNode, LvsNode)):
                    # only the PvsNode actually does anything here...
                    node.create()

            # ensure the sequence of calls correctly setup the devices
            cmd_sequence = [
                # create the pv's on the faked out block devices
                mock.call(['pvcreate', '/dev/fake/root', '--force']),
                mock.call(['pvcreate', '/dev/fake/data', '--force']),
                # create a volume called "vg" out of these two pv's
                mock.call(['vgcreate', 'vg1',
                           '/dev/fake/root', '--force']),
                mock.call(['vgcreate', 'vg2',
                           '/dev/fake/data', '--force']),
                # create a bunch of lv's on vg
                mock.call(['lvcreate', '--name', 'lv_root',
                           '-L', '1800M', 'vg1']),
                mock.call(['lvcreate', '--name', 'lv_tmp',
                           '-L', '100M', 'vg1']),
                mock.call(['lvcreate', '--name', 'lv_var',
                           '-L', '500M', 'vg2']),
                mock.call(['lvcreate', '--name', 'lv_log',
                           '-L', '100M', 'vg2']),
                mock.call(['lvcreate', '--name', 'lv_audit',
                           '-L', '100M', 'vg2']),
                mock.call(['lvcreate', '--name', 'lv_home',
                           '-L', '200M', 'vg2'])]

            self.assertListEqual(mock_exec_sudo.call_args_list,
                                 cmd_sequence)

            # Ensure the correct LVM state was preserved
            blockdev_state = {
                'data': {'device': '/dev/fake/data'},
                'root': {'device': '/dev/fake/root'},
                'lv_audit': {
                    'device': '/dev/mapper/vg2-lv_audit',
                    'extents': None,
                    'opts': None,
                    'size': '100M',
                    'vgs': 'vg2'
                },
                'lv_home': {
                    'device': '/dev/mapper/vg2-lv_home',
                    'extents': None,
                    'opts': None,
                    'size': '200M',
                    'vgs': 'vg2'
                },
                'lv_log': {
                    'device': '/dev/mapper/vg2-lv_log',
                    'extents': None,
                    'opts': None,
                    'size': '100M',
                    'vgs': 'vg2'
                },
                'lv_root': {
                    'device': '/dev/mapper/vg1-lv_root',
                    'extents': None,
                    'opts': None,
                    'size': '1800M',
                    'vgs': 'vg1'
                },
                'lv_tmp': {
                    'device': '/dev/mapper/vg1-lv_tmp',
                    'extents': None,
                    'opts': None,
                    'size': '100M',
                    'vgs': 'vg1'
                },
                'lv_var': {
                    'device': '/dev/mapper/vg2-lv_var',
                    'extents': None,
                    'opts': None,
                    'size': '500M',
                    'vgs': 'vg2'
                },
            }

            # state.debug_dump()
            self.assertDictEqual(state['blockdev'], blockdev_state)

        #
        # Umount test
        #
        with mock.patch(exec_sudo) as mock_exec_sudo, \
             mock.patch('tempfile.NamedTemporaryFile') as mock_temp, \
             mock.patch('os.unlink'):

            # each call to tempfile.NamedTemporaryFile will return a
            # new mock with a unique filename, which we store in
            # tempfiles
            tempfiles = []

            def new_tempfile(*args, **kwargs):
                n = '/tmp/files%s' % len(tempfiles)
                # trap! note mock.Mock(name = n) doesn't work like you
                # think it would, since mock has a name attribute.
                # That's why we override it with the configure_mock
                # (this is mentioned in mock documentation if you read
                # it :)
                r = mock.Mock()
                r.configure_mock(name=n)
                tempfiles.append(n)
                return r
            mock_temp.side_effect = new_tempfile

            def run_it(phase):
                reverse_order = reversed(call_order)
                for node in reverse_order:
                    if isinstance(node, (LVMNode, PvsNode, VgsNode, LvsNode)):
                        getattr(node, phase)()
                    else:
                        logger.debug("Skipping node for test: %s", node)

            run_it('umount')
            run_it('cleanup')

            cmd_sequence = [
                # delete the lv's
                mock.call(['lvchange', '-an', '/dev/vg1/lv_root']),
                mock.call(['lvchange', '-an', '/dev/vg1/lv_tmp']),
                mock.call(['lvchange', '-an', '/dev/vg2/lv_var']),
                mock.call(['lvchange', '-an', '/dev/vg2/lv_log']),
                mock.call(['lvchange', '-an', '/dev/vg2/lv_audit']),
                mock.call(['lvchange', '-an', '/dev/vg2/lv_home']),
                # delete the vg's
                mock.call(['vgchange', '-an', 'vg1']),
                mock.call(['vgchange', '-an', 'vg2']),
                mock.call(['udevadm', 'settle']),
                mock.call(['pvscan', '--cache']),
            ]

            self.assertListEqual(mock_exec_sudo.call_args_list, cmd_sequence)
Пример #21
0
    def test_lvm_spanned_vg(self):

        # Test when a volume group spans some partitions

        tree = self.load_config_file('lvm_tree_spanned_vg.yaml')
        config = config_tree_to_graph(tree)

        state = BlockDeviceState()

        graph, call_order = create_graph(config, self.fake_default_config,
                                         state)

        # XXX: todo; test call_order.  Make sure PV's come before, VG;
        # VG before LV, and that mounts/etc happen afterwards.

        # Fake state for the two PV's specified by this config
        state['blockdev'] = {}
        state['blockdev']['root'] = {}
        state['blockdev']['root']['device'] = '/dev/fake/root'
        state['blockdev']['data1'] = {}
        state['blockdev']['data1']['device'] = '/dev/fake/data1'
        state['blockdev']['data2'] = {}
        state['blockdev']['data2']['device'] = '/dev/fake/data2'

        # We mock patch this ... it's just a little long!
        exec_sudo = 'diskimage_builder.block_device.level1.lvm.exec_sudo'

        #
        # Creation test
        #
        with mock.patch(exec_sudo) as mock_exec_sudo:

            for node in call_order:
                # XXX: This has not mocked out the "lower" layers of
                # creating the devices, which we're assuming works OK, nor
                # the upper layers.
                if isinstance(node, (LVMNode, PvsNode, VgsNode, LvsNode)):
                    # only the LVMNode actually does anything here...
                    node.create()

            # ensure the sequence of calls correctly setup the devices
            cmd_sequence = [
                # create the pv's on the faked out block devices
                mock.call(['pvcreate', '/dev/fake/root', '--force']),
                mock.call(['pvcreate', '/dev/fake/data1', '--force']),
                mock.call(['pvcreate', '/dev/fake/data2', '--force']),
                # create a root and a data volume, with the data volume
                # spanning data1 & data2
                mock.call(['vgcreate', 'vg_root',
                           '/dev/fake/root', '--force']),
                mock.call(['vgcreate', 'vg_data',
                           '/dev/fake/data1', '/dev/fake/data2', '--force']),
                # create root and data volume
                mock.call(['lvcreate', '--name', 'lv_root',
                           '-L', '1800M', 'vg_root']),
                mock.call(['lvcreate', '--name', 'lv_data',
                           '-L', '2G', 'vg_data'])
            ]

            self.assertListEqual(mock_exec_sudo.call_args_list,
                                 cmd_sequence)

        with mock.patch(exec_sudo) as mock_exec_sudo, \
             mock.patch('tempfile.NamedTemporaryFile') as mock_temp, \
             mock.patch('os.unlink'):

            # see above ...
            tempfiles = []

            def new_tempfile(*args, **kwargs):
                n = '/tmp/files%s' % len(tempfiles)
                r = mock.Mock()
                r.configure_mock(name=n)
                tempfiles.append(n)
                return r
            mock_temp.side_effect = new_tempfile

            def run_it(phase):
                reverse_order = reversed(call_order)
                for node in reverse_order:
                    if isinstance(node, (LVMNode, PvsNode, VgsNode, LvsNode)):
                        getattr(node, phase)()
                    else:
                        logger.debug("Skipping node for test: %s", node)

            run_it('umount')
            run_it('cleanup')

            cmd_sequence = [
                # deactivate lv's
                mock.call(['lvchange', '-an', '/dev/vg_root/lv_root']),
                mock.call(['lvchange', '-an', '/dev/vg_data/lv_data']),

                # deactivate vg's
                mock.call(['vgchange', '-an', 'vg_root']),
                mock.call(['vgchange', '-an', 'vg_data']),

                mock.call(['udevadm', 'settle']),
                mock.call(['pvscan', '--cache']),
            ]

            self.assertListEqual(mock_exec_sudo.call_args_list, cmd_sequence)
Пример #22
0
    def test_lvm_multi_pv(self, mock_exec_sudo):
        # Test the command-sequence for a more complicated LVM setup
        tree = self.load_config_file('lvm_tree_multiple_pv.yaml')
        config = config_tree_to_graph(tree)

        state = BlockDeviceState()

        graph, call_order = create_graph(config, self.fake_default_config,
                                         state)

        # XXX: todo; test call_order.  Make sure PV's come before, VG;
        # VG before LV, and that mounts/etc happen afterwards.

        # Fake state for the two PV's specified by this config
        state['blockdev'] = {}
        state['blockdev']['root'] = {}
        state['blockdev']['root']['device'] = '/dev/fake/root'
        state['blockdev']['data'] = {}
        state['blockdev']['data']['device'] = '/dev/fake/data'

        for node in call_order:
            # XXX: This has not mocked out the "lower" layers of
            # creating the devices, which we're assuming works OK, nor
            # the upper layers.
            if isinstance(node, (LVMNode, PvsNode,
                                 VgsNode, LvsNode)):
                # only the LVMNode actually does anything here...
                node.create()

        # ensure the sequence of calls correctly setup the devices
        cmd_sequence = [
            # create the pv's on the faked out block devices
            mock.call(['pvcreate', '/dev/fake/root', '--force']),
            mock.call(['pvcreate', '/dev/fake/data', '--force']),
            # create a volume called "vg" out of these two pv's
            mock.call(['vgcreate', 'vg',
                       '/dev/fake/root', '/dev/fake/data', '--force']),
            # create a bunch of lv's on vg
            mock.call(['lvcreate', '--name', 'lv_root', '-L', '1799356416B',
                       'vg']),
            mock.call(['lvcreate', '--name', 'lv_tmp', '-L', '96468992B',
                       'vg']),
            mock.call(['lvcreate', '--name', 'lv_var', '-L', '499122176B',
                       'vg']),
            mock.call(['lvcreate', '--name', 'lv_log', '-L', '96468992B',
                       'vg']),
            mock.call(['lvcreate', '--name', 'lv_audit', '-L', '96468992B',
                       'vg']),
            mock.call(['lvcreate', '--name', 'lv_home', '-L', '197132288B',
                       'vg'])]

        self.assertEqual(mock_exec_sudo.call_count, len(cmd_sequence))
        mock_exec_sudo.assert_has_calls(cmd_sequence)

        # Ensure the correct LVM state was preserved
        blockdev_state = {
            'data': {'device': '/dev/fake/data'},
            'root': {'device': '/dev/fake/root'},
            'lv_audit': {
                'device': '/dev/mapper/vg-lv_audit',
                'extents': None,
                'opts': None,
                'size': '100M',
                'vgs': 'vg'
            },
            'lv_home': {
                'device': '/dev/mapper/vg-lv_home',
                'extents': None,
                'opts': None,
                'size': '200M',
                'vgs': 'vg'
            },
            'lv_log': {
                'device': '/dev/mapper/vg-lv_log',
                'extents': None,
                'opts': None,
                'size': '100M',
                'vgs': 'vg'
            },
            'lv_root': {
                'device': '/dev/mapper/vg-lv_root',
                'extents': None,
                'opts': None,
                'size': '1800M',
                'vgs': 'vg'
            },
            'lv_tmp': {
                'device': '/dev/mapper/vg-lv_tmp',
                'extents': None,
                'opts': None,
                'size': '100M',
                'vgs': 'vg'
            },
            'lv_var': {
                'device': '/dev/mapper/vg-lv_var',
                'extents': None,
                'opts': None,
                'size': '500M',
                'vgs': 'vg'
            },
        }

        # state.debug_dump()
        self.assertDictEqual(state['blockdev'], blockdev_state)
Пример #23
0
    def test_lvm_thin_provision(self, mock_exec_sudo):
        # Test the command-sequence for a more complicated LVM setup
        tree = self.load_config_file('lvm_tree_thin_provision.yaml')
        config = config_tree_to_graph(tree)

        state = BlockDeviceState()

        graph, call_order = create_graph(config, self.fake_default_config,
                                         state)

        # Fake state for the two PV's specified by this config
        state['blockdev'] = {}
        state['blockdev']['root'] = {}
        state['blockdev']['root']['device'] = '/dev/fake/root'

        for node in call_order:
            # XXX: This has not mocked out the "lower" layers of
            # creating the devices, which we're assuming works OK, nor
            # the upper layers.
            if isinstance(node, (LVMNode, PvsNode,
                                 VgsNode, LvsNode)):
                # only the LVMNode actually does anything here...
                node.create()

        # ensure the sequence of calls correctly setup the devices
        cmd_sequence = [
            # create the pv's on the faked out block devices
            mock.call(['pvcreate', '/dev/fake/root', '--force']),
            # create a volume called "vg" out of these two pv's
            mock.call(['vgcreate', 'vg', '/dev/fake/root', '--force']),
            mock.call(['lvcreate', '--name', 'lv_thinpool',
                      '--type', 'thin-pool', '-L', '2936012800B', 'vg']),
            # create a bunch of lv's on vg using the pool
            mock.call(['lvcreate', '--name', 'lv_root', '--type', 'thin',
                      '--thin-pool', 'lv_thinpool', '-V', '1887436800B',
                       'vg']),
            mock.call(['lvcreate', '--name', 'lv_tmp', '--type', 'thin',
                      '--thin-pool', 'lv_thinpool', '-V', '104857600B', 'vg']),
            mock.call(['lvcreate', '--name', 'lv_var', '--type', 'thin',
                      '--thin-pool', 'lv_thinpool', '-V', '524288000B', 'vg']),
            mock.call(['lvcreate', '--name', 'lv_log', '--type', 'thin',
                      '--thin-pool', 'lv_thinpool', '-V', '104857600B', 'vg']),
            mock.call(['lvcreate', '--name', 'lv_audit', '--type', 'thin',
                      '--thin-pool', 'lv_thinpool', '-V', '104857600B', 'vg']),
            mock.call(['lvcreate', '--name', 'lv_home', '--type', 'thin',
                      '--thin-pool', 'lv_thinpool', '-V', '209715200B', 'vg'])]

        self.assertEqual(mock_exec_sudo.call_count, len(cmd_sequence))
        mock_exec_sudo.assert_has_calls(cmd_sequence)

        # Ensure the correct LVM state was preserved
        blockdev_state = {
            'root': {'device': '/dev/fake/root'},
            'lv_thinpool': {
                'vgs': 'vg',
                'size': '2800MiB',
                'extents': None,
                'opts': None,
                'device': '/dev/mapper/vg-lv_thinpool'
            },
            'lv_root': {
                'vgs': 'vg',
                'size': '1800MiB',
                'extents': None,
                'opts': None,
                'device': '/dev/mapper/vg-lv_root'
            },
            'lv_tmp': {
                'vgs': 'vg',
                'size': '100MiB',
                'extents': None,
                'opts': None,
                'device': '/dev/mapper/vg-lv_tmp'
            },
            'lv_var': {
                'vgs': 'vg',
                'size': '500MiB',
                'extents': None,
                'opts': None,
                'device': '/dev/mapper/vg-lv_var'
            },
            'lv_log': {
                'vgs': 'vg',
                'size': '100MiB',
                'extents': None,
                'opts': None,
                'device': '/dev/mapper/vg-lv_log'
            },
            'lv_audit': {
                'vgs': 'vg',
                'size': '100MiB',
                'extents': None,
                'opts': None,
                'device': '/dev/mapper/vg-lv_audit'
            },
            'lv_home': {
                'vgs': 'vg',
                'size': '200MiB',
                'extents': None,
                'opts': None,
                'device': '/dev/mapper/vg-lv_home'
            }
        }

        self.assertDictEqual(state['blockdev'], blockdev_state)
Пример #24
0
    def test_lvm_multiple_partitions(self):
        # Test the command-sequence for several partitions, one containing
        # volumes on it
        tree = self.load_config_file('lvm_tree_multiple_partitions.yaml')
        config = config_tree_to_graph(tree)

        state = BlockDeviceState()

        graph, call_order = create_graph(config, self.fake_default_config,
                                         state)

        # Fake state for the partitions on this config
        state['blockdev'] = {}
        state['blockdev']['image0'] = {}
        state['blockdev']['image0']['device'] = '/dev/fake/image0'
        state['blockdev']['image0']['image'] = 'image'
        state['blockdev']['root'] = {}
        state['blockdev']['root']['device'] = '/dev/fake/root'
        state['blockdev']['ESP'] = {}
        state['blockdev']['ESP']['device'] = '/dev/fake/ESP'
        state['blockdev']['BSP'] = {}
        state['blockdev']['BSP']['device'] = '/dev/fake/BSP'

        #
        # Creation test
        #

        # We mock out the following exec_sudo and other related calls
        # calls for the layers we are testing.
        exec_sudo_lvm = 'diskimage_builder.block_device.level1.lvm.exec_sudo'
        exec_sudo_part = ('diskimage_builder.block_device.'
                          'level1.partitioning.exec_sudo')
        exec_sudo_loop = ('diskimage_builder.block_device.'
                          'level0.localloop.exec_sudo')
        image_create = ('diskimage_builder.block_device.level0.'
                        'localloop.LocalLoopNode.create')
        size_of_block = ('diskimage_builder.block_device.level1.'
                         'partitioning.Partitioning._size_of_block_dev')
        create_mbr = ('diskimage_builder.block_device.level1.'
                      'partitioning.Partitioning._create_mbr')

        manager = mock.MagicMock()
        with mock.patch(exec_sudo_lvm) as mock_sudo_lvm, \
             mock.patch(exec_sudo_part) as mock_sudo_part, \
             mock.patch(exec_sudo_loop) as mock_sudo_loop, \
             mock.patch(image_create) as mock_image_create, \
             mock.patch(size_of_block) as mock_size_of_block, \
             mock.patch(create_mbr) as mock_create_mbr:

            manager.attach_mock(mock_sudo_lvm, 'sudo_lvm')
            manager.attach_mock(mock_sudo_part, 'sudo_part')
            manager.attach_mock(mock_sudo_loop, 'sudo_loop')
            manager.attach_mock(mock_image_create, 'image_create')
            manager.attach_mock(mock_size_of_block, 'size_of_block')
            manager.attach_mock(mock_create_mbr, 'create_mbr')

            for node in call_order:
                # We're just keeping this to the partition setup and
                # LVM creation; i.e. skipping mounting, mkfs, etc.
                if isinstance(node, (LVMNode, PvsNode,
                                     VgsNode, LvsNode,
                                     LocalLoopNode, PartitionNode)):
                    node.create()
                else:
                    logger.debug("Skipping node for test: %s", node)

            cmd_sequence = [
                # create the underlying block device
                mock.call.image_create(),
                mock.call.size_of_block('image'),
                # write out partition table
                mock.call.create_mbr(),
                # now mount partitions
                mock.call.sudo_part(['sync']),
                mock.call.sudo_part(['kpartx', '-uvs', '/dev/fake/image0']),
                # now create lvm environment
                mock.call.sudo_lvm(['pvcreate', '/dev/fake/root', '--force']),
                mock.call.sudo_lvm(
                    ['vgcreate', 'vg', '/dev/fake/root', '--force']),
                mock.call.sudo_lvm(
                    ['lvcreate', '--name', 'lv_root', '-l', '28%VG', 'vg']),
                mock.call.sudo_lvm(
                    ['lvcreate', '--name', 'lv_tmp', '-l', '4%VG', 'vg']),
                mock.call.sudo_lvm(
                    ['lvcreate', '--name', 'lv_var', '-l', '40%VG', 'vg']),
                mock.call.sudo_lvm(
                    ['lvcreate', '--name', 'lv_log', '-l', '23%VG', 'vg']),
                mock.call.sudo_lvm(
                    ['lvcreate', '--name', 'lv_audit', '-l', '4%VG', 'vg']),
                mock.call.sudo_lvm(
                    ['lvcreate', '--name', 'lv_home', '-l', '1%VG', 'vg']),
            ]
            manager.assert_has_calls(cmd_sequence)

        #
        # Umount/cleanup test
        #
        manager = mock.MagicMock()
        with mock.patch(exec_sudo_lvm) as mock_sudo_lvm, \
             mock.patch(exec_sudo_part) as mock_sudo_part, \
             mock.patch(exec_sudo_loop) as mock_sudo_loop:

            manager.attach_mock(mock_sudo_lvm, 'sudo_lvm')
            manager.attach_mock(mock_sudo_part, 'sudo_part')
            manager.attach_mock(mock_sudo_loop, 'sudo_loop')

            def run_it(phase):
                reverse_order = reversed(call_order)
                for node in reverse_order:
                    if isinstance(node, (LVMNode, PvsNode,
                                         VgsNode, LvsNode,
                                         LocalLoopNode, PartitionNode)):
                        getattr(node, phase)()
                    else:
                        logger.debug("Skipping node for test: %s", node)

            run_it('umount')
            run_it('cleanup')

            cmd_sequence = [
                # deactivate LVM first
                mock.call.sudo_lvm(['lvchange', '-an', '/dev/vg/lv_root']),
                mock.call.sudo_lvm(['lvchange', '-an', '/dev/vg/lv_tmp']),
                mock.call.sudo_lvm(['lvchange', '-an', '/dev/vg/lv_var']),
                mock.call.sudo_lvm(['lvchange', '-an', '/dev/vg/lv_log']),
                mock.call.sudo_lvm(['lvchange', '-an', '/dev/vg/lv_audit']),
                mock.call.sudo_lvm(['lvchange', '-an', '/dev/vg/lv_home']),
                mock.call.sudo_lvm(['vgchange', '-an', 'vg']),
                mock.call.sudo_lvm(['udevadm', 'settle']),
                # now remove partitions (note has to happen after lvm removal)
                mock.call.sudo_part(['kpartx', '-d', '/dev/fake/image0']),
                # now remove loopback device
                mock.call.sudo_loop(['losetup', '-d', '/dev/fake/image0']),
                # now final LVM cleanup call
                mock.call.sudo_lvm(['pvscan', '--cache']),
            ]

            manager.assert_has_calls(cmd_sequence)
Пример #25
0
    def test_lvm_spanned_vg(self):

        # Test when a volume group spans some partitions

        tree = self.load_config_file('lvm_tree_spanned_vg.yaml')
        config = config_tree_to_graph(tree)

        state = BlockDeviceState()

        graph, call_order = create_graph(config, self.fake_default_config,
                                         state)

        # XXX: todo; test call_order.  Make sure PV's come before, VG;
        # VG before LV, and that mounts/etc happen afterwards.

        # Fake state for the two PV's specified by this config
        state['blockdev'] = {}
        state['blockdev']['root'] = {}
        state['blockdev']['root']['device'] = '/dev/fake/root'
        state['blockdev']['data1'] = {}
        state['blockdev']['data1']['device'] = '/dev/fake/data1'
        state['blockdev']['data2'] = {}
        state['blockdev']['data2']['device'] = '/dev/fake/data2'

        # We mock patch this ... it's just a little long!
        exec_sudo = 'diskimage_builder.block_device.level1.lvm.exec_sudo'

        #
        # Creation test
        #
        with mock.patch(exec_sudo) as mock_exec_sudo:

            for node in call_order:
                # XXX: This has not mocked out the "lower" layers of
                # creating the devices, which we're assuming works OK, nor
                # the upper layers.
                if isinstance(node, (LVMNode, PvsNode, VgsNode, LvsNode)):
                    # only the LVMNode actually does anything here...
                    node.create()

            # ensure the sequence of calls correctly setup the devices
            cmd_sequence = [
                # create the pv's on the faked out block devices
                mock.call(['pvcreate', '/dev/fake/root', '--force']),
                mock.call(['pvcreate', '/dev/fake/data1', '--force']),
                mock.call(['pvcreate', '/dev/fake/data2', '--force']),
                # create a root and a data volume, with the data volume
                # spanning data1 & data2
                mock.call(['vgcreate', 'vg_root',
                           '/dev/fake/root', '--force']),
                mock.call(['vgcreate', 'vg_data',
                           '/dev/fake/data1', '/dev/fake/data2', '--force']),
                # create root and data volume
                mock.call(['lvcreate', '--name', 'lv_root',
                           '-L', '1799356416B', 'vg_root']),
                mock.call(['lvcreate', '--name', 'lv_data',
                           '-L', '1996488704B', 'vg_data'])
            ]

            self.assertListEqual(mock_exec_sudo.call_args_list,
                                 cmd_sequence)

        with mock.patch(exec_sudo) as mock_exec_sudo, \
             mock.patch('tempfile.NamedTemporaryFile') as mock_temp, \
             mock.patch('os.unlink'):

            # see above ...
            tempfiles = []

            def new_tempfile(*args, **kwargs):
                n = '/tmp/files%s' % len(tempfiles)
                r = mock.Mock()
                r.configure_mock(name=n)
                tempfiles.append(n)
                return r
            mock_temp.side_effect = new_tempfile

            def run_it(phase):
                reverse_order = reversed(call_order)
                for node in reverse_order:
                    if isinstance(node, (LVMNode, PvsNode, VgsNode, LvsNode)):
                        getattr(node, phase)()
                    else:
                        logger.debug("Skipping node for test: %s", node)

            run_it('umount')
            run_it('cleanup')

            cmd_sequence = [
                # deactivate lv's
                mock.call(['lvchange', '-an', '/dev/vg_root/lv_root']),
                mock.call(['lvchange', '-an', '/dev/vg_data/lv_data']),

                # deactivate vg's
                mock.call(['vgchange', '-an', 'vg_root']),
                mock.call(['vgchange', '-an', 'vg_data']),

                mock.call(['udevadm', 'settle']),
                mock.call(['pvscan', '--cache']),
            ]

            self.assertListEqual(mock_exec_sudo.call_args_list, cmd_sequence)
Пример #26
0
    def test_lvm_multi_pv_vg(self):
        # Test the command-sequence for a more complicated LVM setup
        tree = self.load_config_file('lvm_tree_multiple_pv_vg.yaml')
        config = config_tree_to_graph(tree)

        state = BlockDeviceState()

        graph, call_order = create_graph(config, self.fake_default_config,
                                         state)

        # XXX: todo; test call_order.  Make sure PV's come before, VG;
        # VG before LV, and that mounts/etc happen afterwards.

        # Fake state for the two PV's specified by this config
        state['blockdev'] = {}
        state['blockdev']['root'] = {}
        state['blockdev']['root']['device'] = '/dev/fake/root'
        state['blockdev']['data'] = {}
        state['blockdev']['data']['device'] = '/dev/fake/data'

        # We mock patch this ... it's just a little long!
        exec_sudo = 'diskimage_builder.block_device.level1.lvm.exec_sudo'

        #
        # Creation test
        #
        with mock.patch(exec_sudo) as mock_exec_sudo:

            for node in call_order:
                # XXX: This has not mocked out the "lower" layers of
                # creating the devices, which we're assuming works OK, nor
                # the upper layers.
                if isinstance(node, (LVMNode, PvsNode,
                                     VgsNode, LvsNode)):
                    # only the PvsNode actually does anything here...
                    node.create()

            # ensure the sequence of calls correctly setup the devices
            cmd_sequence = [
                # create the pv's on the faked out block devices
                mock.call(['pvcreate', '/dev/fake/root', '--force']),
                mock.call(['pvcreate', '/dev/fake/data', '--force']),
                # create a volume called "vg" out of these two pv's
                mock.call(['vgcreate', 'vg1',
                           '/dev/fake/root', '--force']),
                mock.call(['vgcreate', 'vg2',
                           '/dev/fake/data', '--force']),
                # create a bunch of lv's on vg
                mock.call(['lvcreate', '--name', 'lv_root',
                           '-L', '1799356416B', 'vg1']),
                mock.call(['lvcreate', '--name', 'lv_tmp',
                           '-L', '96468992B', 'vg1']),
                mock.call(['lvcreate', '--name', 'lv_var',
                           '-L', '499122176B', 'vg2']),
                mock.call(['lvcreate', '--name', 'lv_log',
                           '-L', '96468992B', 'vg2']),
                mock.call(['lvcreate', '--name', 'lv_audit',
                           '-L', '96468992B', 'vg2']),
                mock.call(['lvcreate', '--name', 'lv_home',
                           '-L', '197132288B', 'vg2'])]

            self.assertListEqual(mock_exec_sudo.call_args_list,
                                 cmd_sequence)

            # Ensure the correct LVM state was preserved
            blockdev_state = {
                'data': {'device': '/dev/fake/data'},
                'root': {'device': '/dev/fake/root'},
                'lv_audit': {
                    'device': '/dev/mapper/vg2-lv_audit',
                    'extents': None,
                    'opts': None,
                    'size': '100M',
                    'vgs': 'vg2'
                },
                'lv_home': {
                    'device': '/dev/mapper/vg2-lv_home',
                    'extents': None,
                    'opts': None,
                    'size': '200M',
                    'vgs': 'vg2'
                },
                'lv_log': {
                    'device': '/dev/mapper/vg2-lv_log',
                    'extents': None,
                    'opts': None,
                    'size': '100M',
                    'vgs': 'vg2'
                },
                'lv_root': {
                    'device': '/dev/mapper/vg1-lv_root',
                    'extents': None,
                    'opts': None,
                    'size': '1800M',
                    'vgs': 'vg1'
                },
                'lv_tmp': {
                    'device': '/dev/mapper/vg1-lv_tmp',
                    'extents': None,
                    'opts': None,
                    'size': '100M',
                    'vgs': 'vg1'
                },
                'lv_var': {
                    'device': '/dev/mapper/vg2-lv_var',
                    'extents': None,
                    'opts': None,
                    'size': '500M',
                    'vgs': 'vg2'
                },
            }

            # state.debug_dump()
            self.assertDictEqual(state['blockdev'], blockdev_state)

        #
        # Umount test
        #
        with mock.patch(exec_sudo) as mock_exec_sudo, \
             mock.patch('tempfile.NamedTemporaryFile') as mock_temp, \
             mock.patch('os.unlink'):

            # each call to tempfile.NamedTemporaryFile will return a
            # new mock with a unique filename, which we store in
            # tempfiles
            tempfiles = []

            def new_tempfile(*args, **kwargs):
                n = '/tmp/files%s' % len(tempfiles)
                # trap! note mock.Mock(name = n) doesn't work like you
                # think it would, since mock has a name attribute.
                # That's why we override it with the configure_mock
                # (this is mentioned in mock documentation if you read
                # it :)
                r = mock.Mock()
                r.configure_mock(name=n)
                tempfiles.append(n)
                return r
            mock_temp.side_effect = new_tempfile

            def run_it(phase):
                reverse_order = reversed(call_order)
                for node in reverse_order:
                    if isinstance(node, (LVMNode, PvsNode, VgsNode, LvsNode)):
                        getattr(node, phase)()
                    else:
                        logger.debug("Skipping node for test: %s", node)

            run_it('umount')
            run_it('cleanup')

            cmd_sequence = [
                # delete the lv's
                mock.call(['lvchange', '-an', '/dev/vg1/lv_root']),
                mock.call(['lvchange', '-an', '/dev/vg1/lv_tmp']),
                mock.call(['lvchange', '-an', '/dev/vg2/lv_var']),
                mock.call(['lvchange', '-an', '/dev/vg2/lv_log']),
                mock.call(['lvchange', '-an', '/dev/vg2/lv_audit']),
                mock.call(['lvchange', '-an', '/dev/vg2/lv_home']),
                # delete the vg's
                mock.call(['vgchange', '-an', 'vg1']),
                mock.call(['vgchange', '-an', 'vg2']),
                mock.call(['udevadm', 'settle']),
                mock.call(['pvscan', '--cache']),
            ]

            self.assertListEqual(mock_exec_sudo.call_args_list, cmd_sequence)
Пример #27
0
    def test_lvm_multiple_partitions(self):
        # Test the command-sequence for several partitions, one containing
        # volumes on it
        tree = self.load_config_file('lvm_tree_multiple_partitions.yaml')
        config = config_tree_to_graph(tree)

        state = BlockDeviceState()

        graph, call_order = create_graph(config, self.fake_default_config,
                                         state)

        # Fake state for the partitions on this config
        state['blockdev'] = {}
        state['blockdev']['image0'] = {}
        state['blockdev']['image0']['device'] = '/dev/fake/image0'
        state['blockdev']['image0']['image'] = 'image'
        state['blockdev']['root'] = {}
        state['blockdev']['root']['device'] = '/dev/fake/root'
        state['blockdev']['ESP'] = {}
        state['blockdev']['ESP']['device'] = '/dev/fake/ESP'
        state['blockdev']['BSP'] = {}
        state['blockdev']['BSP']['device'] = '/dev/fake/BSP'

        #
        # Creation test
        #

        # We mock out the following exec_sudo and other related calls
        # calls for the layers we are testing.
        exec_sudo_lvm = 'diskimage_builder.block_device.level1.lvm.exec_sudo'
        exec_sudo_part = ('diskimage_builder.block_device.'
                          'level1.partitioning.exec_sudo')
        exec_sudo_loop = ('diskimage_builder.block_device.'
                          'level0.localloop.exec_sudo')
        image_create = ('diskimage_builder.block_device.level0.'
                        'localloop.LocalLoopNode.create')
        size_of_block = ('diskimage_builder.block_device.level1.'
                         'partitioning.Partitioning._size_of_block_dev')
        create_mbr = ('diskimage_builder.block_device.level1.'
                      'partitioning.Partitioning._create_mbr')

        manager = mock.MagicMock()
        with mock.patch(exec_sudo_lvm) as mock_sudo_lvm, \
             mock.patch(exec_sudo_part) as mock_sudo_part, \
             mock.patch(exec_sudo_loop) as mock_sudo_loop, \
             mock.patch(image_create) as mock_image_create, \
             mock.patch(size_of_block) as mock_size_of_block, \
             mock.patch(create_mbr) as mock_create_mbr:

            manager.attach_mock(mock_sudo_lvm, 'sudo_lvm')
            manager.attach_mock(mock_sudo_part, 'sudo_part')
            manager.attach_mock(mock_sudo_loop, 'sudo_loop')
            manager.attach_mock(mock_image_create, 'image_create')
            manager.attach_mock(mock_size_of_block, 'size_of_block')
            manager.attach_mock(mock_create_mbr, 'create_mbr')

            for node in call_order:
                # We're just keeping this to the partition setup and
                # LVM creation; i.e. skipping mounting, mkfs, etc.
                if isinstance(node, (LVMNode, PvsNode,
                                     VgsNode, LvsNode,
                                     LocalLoopNode, PartitionNode)):
                    node.create()
                else:
                    logger.debug("Skipping node for test: %s", node)

            cmd_sequence = [
                # create the underlying block device
                mock.call.image_create(),
                mock.call.size_of_block('image'),
                # write out partition table
                mock.call.create_mbr(),
                # now mount partitions
                mock.call.sudo_part(['sync']),
                mock.call.sudo_part(['kpartx', '-avs', '/dev/fake/image0']),
                # now create lvm environment
                mock.call.sudo_lvm(['pvcreate', '/dev/fake/root', '--force']),
                mock.call.sudo_lvm(
                    ['vgcreate', 'vg', '/dev/fake/root', '--force']),
                mock.call.sudo_lvm(
                    ['lvcreate', '--name', 'lv_root', '-l', '28%VG', 'vg']),
                mock.call.sudo_lvm(
                    ['lvcreate', '--name', 'lv_tmp', '-l', '4%VG', 'vg']),
                mock.call.sudo_lvm(
                    ['lvcreate', '--name', 'lv_var', '-l', '40%VG', 'vg']),
                mock.call.sudo_lvm(
                    ['lvcreate', '--name', 'lv_log', '-l', '23%VG', 'vg']),
                mock.call.sudo_lvm(
                    ['lvcreate', '--name', 'lv_audit', '-l', '4%VG', 'vg']),
                mock.call.sudo_lvm(
                    ['lvcreate', '--name', 'lv_home', '-l', '1%VG', 'vg']),
            ]
            manager.assert_has_calls(cmd_sequence)

        #
        # Umount/cleanup test
        #
        manager = mock.MagicMock()
        with mock.patch(exec_sudo_lvm) as mock_sudo_lvm, \
             mock.patch(exec_sudo_part) as mock_sudo_part, \
             mock.patch(exec_sudo_loop) as mock_sudo_loop:

            manager.attach_mock(mock_sudo_lvm, 'sudo_lvm')
            manager.attach_mock(mock_sudo_part, 'sudo_part')
            manager.attach_mock(mock_sudo_loop, 'sudo_loop')

            def run_it(phase):
                reverse_order = reversed(call_order)
                for node in reverse_order:
                    if isinstance(node, (LVMNode, PvsNode,
                                         VgsNode, LvsNode,
                                         LocalLoopNode, PartitionNode)):
                        getattr(node, phase)()
                    else:
                        logger.debug("Skipping node for test: %s", node)

            run_it('umount')
            run_it('cleanup')

            cmd_sequence = [
                # deactivate LVM first
                mock.call.sudo_lvm(['lvchange', '-an', '/dev/vg/lv_root']),
                mock.call.sudo_lvm(['lvchange', '-an', '/dev/vg/lv_tmp']),
                mock.call.sudo_lvm(['lvchange', '-an', '/dev/vg/lv_var']),
                mock.call.sudo_lvm(['lvchange', '-an', '/dev/vg/lv_log']),
                mock.call.sudo_lvm(['lvchange', '-an', '/dev/vg/lv_audit']),
                mock.call.sudo_lvm(['lvchange', '-an', '/dev/vg/lv_home']),
                mock.call.sudo_lvm(['vgchange', '-an', 'vg']),
                mock.call.sudo_lvm(['udevadm', 'settle']),
                # now remove partitions (note has to happen after lvm removal)
                mock.call.sudo_part(['kpartx', '-d', '/dev/fake/image0']),
                # now remove loopback device
                mock.call.sudo_loop(['losetup', '-d', '/dev/fake/image0']),
                # now final LVM cleanup call
                mock.call.sudo_lvm(['pvscan', '--cache']),
            ]

            manager.assert_has_calls(cmd_sequence)
Пример #28
0
    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'}

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

        # 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'])
        ]
        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)
Пример #29
0
    def test_lvm_multi_pv(self, mock_exec_sudo):
        # Test the command-sequence for a more complicated LVM setup
        tree = self.load_config_file('lvm_tree_multiple_pv.yaml')
        config = config_tree_to_graph(tree)

        state = BlockDeviceState()

        graph, call_order = create_graph(config, self.fake_default_config,
                                         state)

        # XXX: todo; test call_order.  Make sure PV's come before, VG;
        # VG before LV, and that mounts/etc happen afterwards.

        # Fake state for the two PV's specified by this config
        state['blockdev'] = {}
        state['blockdev']['root'] = {}
        state['blockdev']['root']['device'] = '/dev/fake/root'
        state['blockdev']['data'] = {}
        state['blockdev']['data']['device'] = '/dev/fake/data'

        for node in call_order:
            # XXX: This has not mocked out the "lower" layers of
            # creating the devices, which we're assuming works OK, nor
            # the upper layers.
            if isinstance(node, (LVMNode, PvsNode,
                                 VgsNode, LvsNode)):
                # only the LVMNode actually does anything here...
                node.create()

        # ensure the sequence of calls correctly setup the devices
        cmd_sequence = [
            # create the pv's on the faked out block devices
            mock.call(['pvcreate', '/dev/fake/root', '--force']),
            mock.call(['pvcreate', '/dev/fake/data', '--force']),
            # create a volume called "vg" out of these two pv's
            mock.call(['vgcreate', 'vg',
                       '/dev/fake/root', '/dev/fake/data', '--force']),
            # create a bunch of lv's on vg
            mock.call(['lvcreate', '--name', 'lv_root', '-L', '1800M', 'vg']),
            mock.call(['lvcreate', '--name', 'lv_tmp', '-L', '100M', 'vg']),
            mock.call(['lvcreate', '--name', 'lv_var', '-L', '500M', 'vg']),
            mock.call(['lvcreate', '--name', 'lv_log', '-L', '100M', 'vg']),
            mock.call(['lvcreate', '--name', 'lv_audit', '-L', '100M', 'vg']),
            mock.call(['lvcreate', '--name', 'lv_home', '-L', '200M', 'vg'])]

        self.assertEqual(mock_exec_sudo.call_count, len(cmd_sequence))
        mock_exec_sudo.assert_has_calls(cmd_sequence)

        # Ensure the correct LVM state was preserved
        blockdev_state = {
            'data': {'device': '/dev/fake/data'},
            'root': {'device': '/dev/fake/root'},
            'lv_audit': {
                'device': '/dev/mapper/vg-lv_audit',
                'extents': None,
                'opts': None,
                'size': '100M',
                'vgs': 'vg'
            },
            'lv_home': {
                'device': '/dev/mapper/vg-lv_home',
                'extents': None,
                'opts': None,
                'size': '200M',
                'vgs': 'vg'
            },
            'lv_log': {
                'device': '/dev/mapper/vg-lv_log',
                'extents': None,
                'opts': None,
                'size': '100M',
                'vgs': 'vg'
            },
            'lv_root': {
                'device': '/dev/mapper/vg-lv_root',
                'extents': None,
                'opts': None,
                'size': '1800M',
                'vgs': 'vg'
            },
            'lv_tmp': {
                'device': '/dev/mapper/vg-lv_tmp',
                'extents': None,
                'opts': None,
                'size': '100M',
                'vgs': 'vg'
            },
            'lv_var': {
                'device': '/dev/mapper/vg-lv_var',
                'extents': None,
                'opts': None,
                'size': '500M',
                'vgs': 'vg'
            },
        }

        # state.debug_dump()
        self.assertDictEqual(state['blockdev'], blockdev_state)
Пример #30
0
 def create(self, result, rollback):
     dg, call_order = create_graph(self.config, self.params)
     for node in call_order:
         node.create(result, rollback)