Пример #1
0
 def setup(self, mock_path):
     self.context_manager_mock = mock.Mock()
     self.file_mock = mock.Mock()
     self.enter_mock = mock.Mock()
     self.exit_mock = mock.Mock()
     self.enter_mock.return_value = self.file_mock
     setattr(self.context_manager_mock, '__enter__', self.enter_mock)
     setattr(self.context_manager_mock, '__exit__', self.exit_mock)
     self.volume_type = namedtuple(
         'volume_type', [
             'name',
             'size',
             'realpath',
             'mountpoint',
             'fullsize',
             'attributes'
         ]
     )
     self.volumes = [
         self.volume_type(
             name='LVRoot', size='freespace:100', realpath='/',
             mountpoint=None, fullsize=False,
             attributes=[]
         ),
         self.volume_type(
             name='LVetc', size='freespace:200', realpath='/etc',
             mountpoint='/etc', fullsize=False,
             attributes=[]
         ),
         self.volume_type(
             name='myvol', size='size:500', realpath='/data',
             mountpoint='LVdata', fullsize=False,
             attributes=[]
         ),
         self.volume_type(
             name='LVhome', size=None, realpath='/home',
             mountpoint='/home', fullsize=True,
             attributes=[]
         )
     ]
     mock_path.return_value = True
     self.device_provider = mock.Mock()
     self.device_provider.is_loop = mock.Mock(
         return_value=True
     )
     self.device_provider.get_device = mock.Mock(
         return_value='/dev/storage'
     )
     self.volume_manager = VolumeManagerBtrfs(
         self.device_provider, 'root_dir', self.volumes
     )
Пример #2
0
 def setup(self, mock_path):
     self.volume_type = namedtuple(
         'volume_type', [
             'name',
             'size',
             'realpath',
             'mountpoint',
             'fullsize',
             'attributes'
         ]
     )
     self.volumes = [
         self.volume_type(
             name='LVRoot', size='freespace:100', realpath='/',
             mountpoint=None, fullsize=False,
             attributes=[]
         ),
         self.volume_type(
             name='LVetc', size='freespace:200', realpath='/etc',
             mountpoint='/etc', fullsize=False,
             attributes=[]
         ),
         self.volume_type(
             name='myvol', size='size:500', realpath='/data',
             mountpoint='LVdata', fullsize=False,
             attributes=[]
         ),
         self.volume_type(
             name='LVhome', size=None, realpath='/home',
             mountpoint='/home', fullsize=True,
             attributes=[]
         )
     ]
     mock_path.return_value = True
     self.device_map = {
         'root': Mock()
     }
     self.device_map['root'].is_loop = Mock(
         return_value=True
     )
     self.device_map['root'].get_device = Mock(
         return_value='/dev/storage'
     )
     self.volume_manager = VolumeManagerBtrfs(
         self.device_map, 'root_dir', self.volumes
     )
Пример #3
0
 def setup(self, mock_path):
     self.volume_type = namedtuple(
         'volume_type', [
             'name',
             'size',
             'realpath',
             'mountpoint',
             'fullsize'
         ]
     )
     self.volumes = [
         self.volume_type(
             name='LVRoot', size='freespace:100', realpath='/',
             mountpoint=None, fullsize=False
         ),
         self.volume_type(
             name='LVetc', size='freespace:200', realpath='/etc',
             mountpoint='/etc', fullsize=False
         ),
         self.volume_type(
             name='myvol', size='size:500', realpath='/data',
             mountpoint='LVdata', fullsize=False
         ),
         self.volume_type(
             name='LVhome', size=None, realpath='/home',
             mountpoint='/home', fullsize=True
         ),
     ]
     mock_path.return_value = True
     self.device_provider = mock.Mock()
     self.device_provider.is_loop = mock.Mock(
         return_value=True
     )
     self.device_provider.get_device = mock.Mock(
         return_value='/dev/storage'
     )
     self.volume_manager = VolumeManagerBtrfs(
         self.device_provider, 'root_dir', self.volumes
     )
Пример #4
0
 def __new__(
     self, name, device_provider, root_dir, volumes, custom_args=None
 ):
     if name == 'lvm':
         return VolumeManagerLVM(
             device_provider, root_dir, volumes, custom_args
         )
     elif name == 'btrfs':
         return VolumeManagerBtrfs(
             device_provider, root_dir, volumes, custom_args
         )
     else:
         raise KiwiVolumeManagerSetupError(
             'Support for %s volume manager not implemented' % name
         )
Пример #5
0
 def setup(self, mock_mkdtemp, mock_path):
     mock_mkdtemp.return_value = 'tmpdir'
     self.volume_type = namedtuple(
         'volume_type', [
             'name',
             'size',
             'realpath',
             'mountpoint',
             'fullsize'
         ]
     )
     self.volumes = [
         self.volume_type(
             name='LVRoot', size='freespace:100', realpath='/',
             mountpoint=None, fullsize=False
         ),
         self.volume_type(
             name='LVetc', size='freespace:200', realpath='/etc',
             mountpoint='/etc', fullsize=False
         ),
         self.volume_type(
             name='myvol', size='size:500', realpath='/data',
             mountpoint='LVdata', fullsize=False
         ),
         self.volume_type(
             name='LVhome', size=None, realpath='/home',
             mountpoint='/home', fullsize=True
         ),
     ]
     mock_path.return_value = True
     self.device_provider = mock.Mock()
     self.device_provider.is_loop = mock.Mock(
         return_value=True
     )
     self.device_provider.get_device = mock.Mock(
         return_value='/dev/storage'
     )
     self.volume_manager = VolumeManagerBtrfs(
         self.device_provider, 'root_dir', self.volumes
     )
Пример #6
0
class TestVolumeManagerBtrfs(object):
    @patch('os.path.exists')
    def setup(self, mock_path):
        self.context_manager_mock = mock.Mock()
        self.file_mock = mock.Mock()
        self.enter_mock = mock.Mock()
        self.exit_mock = mock.Mock()
        self.enter_mock.return_value = self.file_mock
        setattr(self.context_manager_mock, '__enter__', self.enter_mock)
        setattr(self.context_manager_mock, '__exit__', self.exit_mock)
        self.volume_type = namedtuple(
            'volume_type', [
                'name',
                'size',
                'realpath',
                'mountpoint',
                'fullsize',
                'attributes'
            ]
        )
        self.volumes = [
            self.volume_type(
                name='LVRoot', size='freespace:100', realpath='/',
                mountpoint=None, fullsize=False,
                attributes=[]
            ),
            self.volume_type(
                name='LVetc', size='freespace:200', realpath='/etc',
                mountpoint='/etc', fullsize=False,
                attributes=[]
            ),
            self.volume_type(
                name='myvol', size='size:500', realpath='/data',
                mountpoint='LVdata', fullsize=False,
                attributes=[]
            ),
            self.volume_type(
                name='LVhome', size=None, realpath='/home',
                mountpoint='/home', fullsize=True,
                attributes=[]
            )
        ]
        mock_path.return_value = True
        self.device_provider = mock.Mock()
        self.device_provider.is_loop = mock.Mock(
            return_value=True
        )
        self.device_provider.get_device = mock.Mock(
            return_value='/dev/storage'
        )
        self.volume_manager = VolumeManagerBtrfs(
            self.device_provider, 'root_dir', self.volumes
        )

    def test_post_init(self):
        self.volume_manager.post_init({'some-arg': 'some-val'})
        assert self.volume_manager.custom_args['some-arg'] == 'some-val'

    @patch('os.path.exists')
    @patch('kiwi.volume_manager.btrfs.Command.run')
    @patch('kiwi.volume_manager.btrfs.FileSystem')
    @patch('kiwi.volume_manager.btrfs.MappedDevice')
    @patch('kiwi.volume_manager.btrfs.MountManager')
    @patch('kiwi.volume_manager.base.mkdtemp')
    def test_setup_no_snapshot(
        self, mock_mkdtemp, mock_mount, mock_mapped_device, mock_fs,
        mock_command, mock_os_exists
    ):
        mock_mkdtemp.return_value = 'tmpdir'
        toplevel_mount = mock.Mock()
        mock_mount.return_value = toplevel_mount
        command_call = mock.Mock()
        command_call.output = 'ID 256 gen 23 top level 5 path @'
        mock_mapped_device.return_value = 'mapped_device'
        mock_os_exists.return_value = False
        mock_command.return_value = command_call

        self.volume_manager.setup()

        mock_mount.assert_called_once_with(
            device='/dev/storage', mountpoint='tmpdir'
        )
        toplevel_mount.mount.assert_called_once_with([])
        assert mock_command.call_args_list == [
            call(['btrfs', 'subvolume', 'create', 'tmpdir/@']),
            call(['btrfs', 'subvolume', 'list', 'tmpdir']),
            call(['btrfs', 'subvolume', 'set-default', '256', 'tmpdir'])
        ]

    @patch('os.path.exists')
    @patch('kiwi.volume_manager.btrfs.Command.run')
    @patch('kiwi.volume_manager.btrfs.FileSystem')
    @patch('kiwi.volume_manager.btrfs.MappedDevice')
    @patch('kiwi.volume_manager.btrfs.MountManager')
    @patch('kiwi.volume_manager.base.mkdtemp')
    def test_setup_with_snapshot(
        self, mock_mkdtemp, mock_mount, mock_mapped_device, mock_fs,
        mock_command, mock_os_exists
    ):
        mock_mkdtemp.return_value = 'tmpdir'
        toplevel_mount = mock.Mock()
        mock_mount.return_value = toplevel_mount
        command_call = mock.Mock()
        command_call.output = \
            'ID 258 gen 26 top level 257 path @/.snapshots/1/snapshot'
        mock_mapped_device.return_value = 'mapped_device'
        mock_os_exists.return_value = False
        mock_command.return_value = command_call
        self.volume_manager.custom_args['root_is_snapshot'] = True

        self.volume_manager.setup()

        mock_mount.assert_called_once_with(
            device='/dev/storage', mountpoint='tmpdir'
        )
        toplevel_mount.mount.assert_called_once_with([])
        assert mock_command.call_args_list == [
            call(['btrfs', 'subvolume', 'create', 'tmpdir/@']),
            call(['btrfs', 'subvolume', 'create', 'tmpdir/@/.snapshots']),
            call(['mkdir', '-p', 'tmpdir/@/.snapshots/1']),
            call([
                'btrfs', 'subvolume', 'snapshot', 'tmpdir/@',
                'tmpdir/@/.snapshots/1/snapshot'
            ]),
            call(['btrfs', 'subvolume', 'list', 'tmpdir']),
            call(['btrfs', 'subvolume', 'set-default', '258', 'tmpdir'])
        ]

    @raises(KiwiVolumeRootIDError)
    @patch('os.path.exists')
    @patch('kiwi.volume_manager.btrfs.Command.run')
    @patch('kiwi.volume_manager.btrfs.FileSystem')
    @patch('kiwi.volume_manager.btrfs.MappedDevice')
    @patch('kiwi.volume_manager.btrfs.MountManager')
    def test_setup_volume_id_not_detected(
        self, mock_mount, mock_mapped_device, mock_fs,
        mock_command, mock_os_exists
    ):
        command_call = mock.Mock()
        command_call.output = 'id-string-invalid'
        mock_mapped_device.return_value = 'mapped_device'
        mock_os_exists.return_value = False
        mock_command.return_value = command_call
        self.volume_manager.setup()

    @patch('os.path.exists')
    @patch('kiwi.volume_manager.btrfs.Command.run')
    @patch('kiwi.volume_manager.btrfs.MountManager')
    @patch('kiwi.volume_manager.btrfs.Path.create')
    @patch('kiwi.volume_manager.base.VolumeManagerBase.apply_attributes_on_volume')
    def test_create_volumes(
        self, mock_attrs, mock_path, mock_mount, mock_command, mock_os_exists
    ):
        volume_mount = mock.Mock()
        mock_mount.return_value = volume_mount
        self.volume_manager.mountpoint = 'tmpdir'
        self.volume_manager.custom_args['root_is_snapshot'] = True
        mock_os_exists.return_value = False

        self.volume_manager.create_volumes('btrfs')

        assert mock_attrs.call_args_list == [
            call(
                'tmpdir/@/', self.volume_type(
                    name='myvol', size='size:500', realpath='/data',
                    mountpoint='LVdata', fullsize=False, attributes=[])
            ),
            call('tmpdir/@/', self.volume_type(
                name='LVetc', size='freespace:200', realpath='/etc',
                mountpoint='/etc', fullsize=False, attributes=[])
            ),
            call('tmpdir/@/', self.volume_type(
                name='LVhome', size=None, realpath='/home',
                mountpoint='/home', fullsize=True, attributes=[])
            )
        ]
        assert mock_path.call_args_list == [
            call('root_dir/etc'),
            call('root_dir/data'),
            call('root_dir/home'),
            call('tmpdir/@'),
            call('tmpdir/@'),
            call('tmpdir/@')
        ]
        assert mock_command.call_args_list == [
            call(['btrfs', 'subvolume', 'create', 'tmpdir/@/data']),
            call(['btrfs', 'subvolume', 'create', 'tmpdir/@/etc']),
            call(['btrfs', 'subvolume', 'create', 'tmpdir/@/home'])
        ]
        assert mock_mount.call_args_list == [
            call(
                device='/dev/storage',
                mountpoint='tmpdir/@/.snapshots/1/snapshot/data'
            ),
            call(
                device='/dev/storage',
                mountpoint='tmpdir/@/.snapshots/1/snapshot/etc'
            ),
            call(
                device='/dev/storage',
                mountpoint='tmpdir/@/.snapshots/1/snapshot/home'
            )
        ]

    def test_get_volumes(self):
        volume_mount = mock.Mock()
        volume_mount.mountpoint = \
            '/tmp/kiwi_volumes.xx/@/.snapshots/1/snapshot/boot/grub2'
        volume_mount.device = 'device'
        self.volume_manager.toplevel_volume = '@/.snapshots/1/snapshot'
        self.volume_manager.subvol_mount_list = [volume_mount]
        self.volume_manager.custom_args['root_is_snapshot'] = True
        assert self.volume_manager.get_volumes() == {
            '/boot/grub2': {
                'volume_options': 'subvol=@/boot/grub2',
                'volume_device': 'device'
            }
        }

    @patch('kiwi.volume_manager.btrfs.Command.run')
    def test_get_fstab(self, mock_command):
        blkid_result = mock.Mock()
        blkid_result.output = 'id'
        mock_command.return_value = blkid_result
        volume_mount = mock.Mock()
        volume_mount.mountpoint = \
            '/tmp/kiwi_volumes.xx/@/.snapshots/1/snapshot/var/tmp'
        volume_mount.device = 'device'
        self.volume_manager.toplevel_volume = '@/.snapshots/1/snapshot'
        self.volume_manager.subvol_mount_list = [volume_mount]
        self.volume_manager.custom_args['root_is_snapshot'] = True
        assert self.volume_manager.get_fstab() == [
            'LABEL=id /.snapshots btrfs defaults,subvol=@/.snapshots 0 0',
            'LABEL=id /var/tmp btrfs defaults,subvol=@/var/tmp 0 0'
        ]

    @patch('os.path.exists')
    @patch('kiwi.volume_manager.btrfs.Path.create')
    def test_mount_volumes(self, mock_path, mock_os_exists):
        self.volume_manager.toplevel_mount = mock.Mock()
        self.volume_manager.toplevel_mount.is_mounted = mock.Mock(
            return_value=False
        )
        mock_os_exists.return_value = False
        volume_mount = mock.Mock()
        volume_mount.mountpoint = \
            '/tmp/kiwi_volumes.xx/@/.snapshots/1/snapshot/var/tmp'
        self.volume_manager.toplevel_volume = '@/.snapshots/1/snapshot'
        self.volume_manager.custom_args['root_is_snapshot'] = True
        self.volume_manager.subvol_mount_list = [volume_mount]

        self.volume_manager.mount_volumes()

        self.volume_manager.toplevel_mount.mount.assert_called_once_with([])
        mock_path.assert_called_once_with(volume_mount.mountpoint)
        volume_mount.mount.assert_called_once_with(
            options=['subvol=@/var/tmp']
        )

    @patch('os.path.exists')
    @patch('kiwi.volume_manager.btrfs.Command.run')
    @patch('kiwi.volume_manager.btrfs.FileSystem')
    @patch('kiwi.volume_manager.btrfs.MappedDevice')
    @patch('kiwi.volume_manager.btrfs.MountManager')
    @patch('kiwi.volume_manager.base.mkdtemp')
    def test_remount_volumes(
        self, mock_mkdtemp, mock_mount, mock_mapped_device, mock_fs,
        mock_command, mock_os_exists
    ):
        mock_mkdtemp.return_value = '/tmp/kiwi_volumes.xx'
        toplevel_mount = mock.Mock()
        toplevel_mount.is_mounted = mock.Mock(
            return_value=False
        )
        mock_mount.return_value = toplevel_mount
        command_call = mock.Mock()
        command_call.output = \
            'ID 258 gen 26 top level 257 path @/.snapshots/1/snapshot'
        mock_mapped_device.return_value = 'mapped_device'
        mock_os_exists.return_value = False
        mock_command.return_value = command_call
        self.volume_manager.custom_args['root_is_snapshot'] = True

        self.volume_manager.setup()

        mock_os_exists.return_value = True
        volume_mount = mock.Mock()
        volume_mount.mountpoint = \
            '/tmp/kiwi_volumes.xx/@/.snapshots/1/snapshot/var/tmp'
        self.volume_manager.subvol_mount_list = [volume_mount]

        self.volume_manager.mount_volumes()
        self.volume_manager.umount_volumes()
        self.volume_manager.mount_volumes()

        assert volume_mount.mountpoint == '/tmp/kiwi_volumes.xx/var/tmp'

    def test_umount_volumes(self):
        self.volume_manager.toplevel_mount = mock.Mock()
        volume_mount = mock.Mock()
        volume_mount.is_mounted = mock.Mock(
            return_value=True
        )
        self.volume_manager.toplevel_mount.is_mounted = mock.Mock(
            return_value=True
        )
        self.volume_manager.subvol_mount_list = [volume_mount]
        self.volume_manager.umount_volumes()
        volume_mount.is_mounted.assert_called_once_with()
        volume_mount.umount.assert_called_once_with()
        self.volume_manager.toplevel_mount.is_mounted.assert_called_once_with()
        self.volume_manager.toplevel_mount.umount.assert_called_once_with()

    def test_umount_sub_volumes_busy(self):
        self.volume_manager.toplevel_mount = mock.Mock()
        volume_mount = mock.Mock()
        volume_mount.is_mounted = mock.Mock(
            return_value=True
        )
        volume_mount.umount = mock.Mock(
            return_value=False
        )
        self.volume_manager.subvol_mount_list = [volume_mount]
        assert self.volume_manager.umount_volumes() is False

    def test_umount_toplevel_busy(self):
        self.volume_manager.toplevel_mount = mock.Mock()
        volume_mount = mock.Mock()
        volume_mount.is_mounted = mock.Mock(
            return_value=True
        )
        self.volume_manager.toplevel_mount.is_mounted = mock.Mock(
            return_value=True
        )
        self.volume_manager.toplevel_mount.umount = mock.Mock(
            return_value=False
        )
        assert self.volume_manager.umount_volumes() is False

    @patch_open
    @patch('kiwi.volume_manager.btrfs.DataSync')
    @patch.object(datetime, 'datetime', mock.Mock(wraps=datetime.datetime))
    def test_sync_data(self, mock_sync, mock_open):
        xml_info = etree.tostring(etree.parse(
            '../data/info.xml', etree.XMLParser(remove_blank_text=True)
        ))
        datetime.datetime.now.return_value = datetime.datetime(2016, 1, 1)
        mock_open.return_value = self.context_manager_mock
        self.volume_manager.toplevel_mount = mock.Mock()
        self.volume_manager.mountpoint = 'tmpdir'
        self.volume_manager.custom_args['root_is_snapshot'] = True
        sync = mock.Mock()
        mock_sync.return_value = sync

        self.volume_manager.sync_data(['exclude_me'])

        mock_sync.assert_called_once_with(
            'root_dir', 'tmpdir/@/.snapshots/1/snapshot'
        )
        sync.sync_data.assert_called_once_with(
            exclude=['exclude_me'],
            options=['-a', '-H', '-X', '-A', '--one-file-system']
        )
        mock_open.assert_called_once_with(
            'tmpdir/@/.snapshots/1/info.xml', 'w'
        )
        self.file_mock.write.assert_called_once_with(
            minidom.parseString(xml_info).toprettyxml(indent="    ")
        )

    @patch('kiwi.volume_manager.btrfs.Command.run')
    def test_set_property_readonly_root(self, mock_command):
        self.volume_manager.mountpoint = 'tmpdir'
        self.volume_manager.custom_args['root_is_snapshot'] = True
        self.volume_manager.custom_args['root_is_readonly_snapshot'] = True
        self.volume_manager.set_property_readonly_root()
        mock_command.assert_called_once_with(
            [
                'btrfs', 'property', 'set', 'tmpdir', 'ro', 'true'
            ]
        )

    @patch('kiwi.volume_manager.btrfs.VolumeManagerBtrfs.umount_volumes')
    @patch('kiwi.volume_manager.btrfs.Path.wipe')
    def test_destructor(self, mock_wipe, mock_umount_volumes):
        mock_umount_volumes.return_value = True
        self.volume_manager.toplevel_mount = mock.Mock()
        self.volume_manager.__del__()
        mock_umount_volumes.assert_called_once_with()
        mock_wipe.assert_called_once_with(self.volume_manager.mountpoint)
Пример #7
0
class TestVolumeManagerBtrfs(object):
    @patch('os.path.exists')
    @patch('kiwi.volume_manager.base.mkdtemp')
    def setup(self, mock_mkdtemp, mock_path):
        mock_mkdtemp.return_value = 'tmpdir'
        self.volume_type = namedtuple(
            'volume_type', [
                'name',
                'size',
                'realpath',
                'mountpoint',
                'fullsize'
            ]
        )
        self.volumes = [
            self.volume_type(
                name='LVRoot', size='freespace:100', realpath='/',
                mountpoint=None, fullsize=False
            ),
            self.volume_type(
                name='LVetc', size='freespace:200', realpath='/etc',
                mountpoint='/etc', fullsize=False
            ),
            self.volume_type(
                name='myvol', size='size:500', realpath='/data',
                mountpoint='LVdata', fullsize=False
            ),
            self.volume_type(
                name='LVhome', size=None, realpath='/home',
                mountpoint='/home', fullsize=True
            ),
        ]
        mock_path.return_value = True
        self.device_provider = mock.Mock()
        self.device_provider.is_loop = mock.Mock(
            return_value=True
        )
        self.device_provider.get_device = mock.Mock(
            return_value='/dev/storage'
        )
        self.volume_manager = VolumeManagerBtrfs(
            self.device_provider, 'root_dir', self.volumes
        )

    def test_post_init(self):
        self.volume_manager.post_init({'some-arg': 'some-val'})
        assert self.volume_manager.custom_args['some-arg'] == 'some-val'

    @patch('os.path.exists')
    @patch('kiwi.volume_manager.btrfs.Command.run')
    @patch('kiwi.volume_manager.btrfs.FileSystem')
    @patch('kiwi.volume_manager.btrfs.MappedDevice')
    @patch('kiwi.volume_manager.btrfs.MountManager')
    def test_setup_no_snapshot(
        self, mock_mount, mock_mapped_device, mock_fs,
        mock_command, mock_os_exists
    ):
        toplevel_mount = mock.Mock()
        mock_mount.return_value = toplevel_mount
        command_call = mock.Mock()
        command_call.output = 'ID 256 gen 23 top level 5 path @'
        mock_mapped_device.return_value = 'mapped_device'
        mock_os_exists.return_value = False
        mock_command.return_value = command_call

        self.volume_manager.setup()

        mock_mount.assert_called_once_with(
            device='/dev/storage', mountpoint='tmpdir'
        )
        toplevel_mount.mount.assert_called_once_with()
        assert mock_command.call_args_list == [
            call(['btrfs', 'subvolume', 'create', 'tmpdir/@']),
            call(['btrfs', 'subvolume', 'list', 'tmpdir']),
            call(['btrfs', 'subvolume', 'set-default', '256', 'tmpdir'])
        ]

    @patch('os.path.exists')
    @patch('kiwi.volume_manager.btrfs.Command.run')
    @patch('kiwi.volume_manager.btrfs.FileSystem')
    @patch('kiwi.volume_manager.btrfs.MappedDevice')
    @patch('kiwi.volume_manager.btrfs.MountManager')
    def test_setup_with_snapshot(
        self, mock_mount, mock_mapped_device, mock_fs,
        mock_command, mock_os_exists
    ):
        toplevel_mount = mock.Mock()
        mock_mount.return_value = toplevel_mount
        command_call = mock.Mock()
        command_call.output = \
            'ID 258 gen 26 top level 257 path @/.snapshots/1/snapshot'
        mock_mapped_device.return_value = 'mapped_device'
        mock_os_exists.return_value = False
        mock_command.return_value = command_call
        self.volume_manager.custom_args['root_is_snapshot'] = True

        self.volume_manager.setup()

        mock_mount.assert_called_once_with(
            device='/dev/storage', mountpoint='tmpdir'
        )
        toplevel_mount.mount.assert_called_once_with()
        assert mock_command.call_args_list == [
            call(['btrfs', 'subvolume', 'create', 'tmpdir/@']),
            call(['btrfs', 'subvolume', 'create', 'tmpdir/@/.snapshots']),
            call(['mkdir', '-p', 'tmpdir/@/.snapshots/1']),
            call([
                'btrfs', 'subvolume', 'snapshot', 'tmpdir/@',
                'tmpdir/@/.snapshots/1/snapshot'
            ]),
            call(['btrfs', 'subvolume', 'list', 'tmpdir']),
            call(['btrfs', 'subvolume', 'set-default', '258', 'tmpdir'])
        ]

    @raises(KiwiVolumeRootIDError)
    @patch('os.path.exists')
    @patch('kiwi.volume_manager.btrfs.Command.run')
    @patch('kiwi.volume_manager.btrfs.FileSystem')
    @patch('kiwi.volume_manager.btrfs.MappedDevice')
    @patch('kiwi.volume_manager.btrfs.MountManager')
    def test_setup_volume_id_not_detected(
        self, mock_mount, mock_mapped_device, mock_fs,
        mock_command, mock_os_exists
    ):
        command_call = mock.Mock()
        command_call.output = 'id-string-invalid'
        mock_mapped_device.return_value = 'mapped_device'
        mock_os_exists.return_value = False
        mock_command.return_value = command_call
        self.volume_manager.setup()

    @patch('os.path.exists')
    @patch('kiwi.volume_manager.btrfs.Command.run')
    @patch('kiwi.volume_manager.btrfs.MountManager')
    @patch('kiwi.volume_manager.btrfs.Path.create')
    def test_create_volumes(
        self, mock_path, mock_mount, mock_command, mock_os_exists
    ):
        volume_mount = mock.Mock()
        mock_mount.return_value = volume_mount
        self.volume_manager.mountpoint = 'tmpdir'
        self.volume_manager.custom_args['root_is_snapshot'] = True
        mock_os_exists.return_value = False

        self.volume_manager.create_volumes('btrfs')

        assert mock_path.call_args_list == [
            call('root_dir/etc'),
            call('root_dir/data'),
            call('root_dir/home'),
            call('tmpdir/@'),
            call('tmpdir/@'),
            call('tmpdir/@')
        ]
        assert mock_command.call_args_list == [
            call(['btrfs', 'subvolume', 'create', 'tmpdir/@/data']),
            call(['btrfs', 'subvolume', 'create', 'tmpdir/@/etc']),
            call(['btrfs', 'subvolume', 'create', 'tmpdir/@/home'])
        ]
        assert mock_mount.call_args_list == [
            call(
               device='/dev/storage',
               mountpoint='tmpdir/@/.snapshots/1/snapshot/data'
            ),
            call(
                device='/dev/storage',
                mountpoint='tmpdir/@/.snapshots/1/snapshot/etc'
            ),
            call(
                device='/dev/storage',
                mountpoint='tmpdir/@/.snapshots/1/snapshot/home'
            )
        ]

    @patch('os.path.exists')
    @patch('kiwi.volume_manager.btrfs.Path.create')
    def test_mount_volumes(self, mock_path, mock_os_exists):
        mock_os_exists.return_value = False
        volume_mount = mock.Mock()
        volume_mount.mountpoint = 'tmpdir/@/.snapshots/1/snapshot/subvol'
        self.volume_manager.subvol_mount_list = [volume_mount]

        self.volume_manager.mount_volumes()

        mock_path.assert_called_once_with(volume_mount.mountpoint)
        volume_mount.mount.assert_called_once_with(
            options=['subvol=@/subvol']
        )

    def test_umount_volumes(self):
        self.volume_manager.toplevel_mount = mock.Mock()
        volume_mount = mock.Mock()
        self.volume_manager.subvol_mount_list = [volume_mount]
        self.volume_manager.umount_volumes()
        volume_mount.umount.assert_called_once_with(
            delete_mountpoint=False
        )
        self.volume_manager.toplevel_mount.umount.assert_called_once_with()

    @patch('kiwi.volume_manager.btrfs.DataSync')
    def test_sync_data(self, mock_sync):
        self.volume_manager.toplevel_mount = mock.Mock()
        self.volume_manager.mountpoint = 'tmpdir'
        self.volume_manager.custom_args['root_is_snapshot'] = True
        sync = mock.Mock()
        mock_sync.return_value = sync

        self.volume_manager.sync_data(['exclude_me'])

        mock_sync.assert_called_once_with(
            'root_dir', 'tmpdir/@/.snapshots/1/snapshot'
        )
        sync.sync_data.assert_called_once_with(
            exclude=['exclude_me'],
            options=['-a', '-H', '-X', '-A', '--one-file-system']
        )

    @patch('kiwi.volume_manager.btrfs.VolumeManagerBtrfs.umount_volumes')
    def test_destructor(self, mock_umount_volumes):
        self.volume_manager.toplevel_mount = mock.Mock()
        self.volume_manager.__del__()
        mock_umount_volumes.assert_called_once_with()
Пример #8
0
class TestVolumeManagerBtrfs:
    @patch('os.path.exists')
    def setup(self, mock_path):
        self.volume_type = namedtuple(
            'volume_type', [
                'name',
                'size',
                'realpath',
                'mountpoint',
                'fullsize',
                'attributes'
            ]
        )
        self.volumes = [
            self.volume_type(
                name='LVRoot', size='freespace:100', realpath='/',
                mountpoint=None, fullsize=False,
                attributes=[]
            ),
            self.volume_type(
                name='LVetc', size='freespace:200', realpath='/etc',
                mountpoint='/etc', fullsize=False,
                attributes=[]
            ),
            self.volume_type(
                name='myvol', size='size:500', realpath='/data',
                mountpoint='LVdata', fullsize=False,
                attributes=[]
            ),
            self.volume_type(
                name='LVhome', size=None, realpath='/home',
                mountpoint='/home', fullsize=True,
                attributes=[]
            )
        ]
        mock_path.return_value = True
        self.device_provider = Mock()
        self.device_provider.is_loop = Mock(
            return_value=True
        )
        self.device_provider.get_device = Mock(
            return_value='/dev/storage'
        )
        self.volume_manager = VolumeManagerBtrfs(
            self.device_provider, 'root_dir', self.volumes
        )

    def test_post_init(self):
        self.volume_manager.post_init({'some-arg': 'some-val'})
        assert self.volume_manager.custom_args['some-arg'] == 'some-val'

    @patch('os.path.exists')
    @patch('kiwi.volume_manager.btrfs.Command.run')
    @patch('kiwi.volume_manager.btrfs.FileSystem')
    @patch('kiwi.volume_manager.btrfs.MappedDevice')
    @patch('kiwi.volume_manager.btrfs.MountManager')
    @patch('kiwi.volume_manager.base.mkdtemp')
    def test_setup_no_snapshot(
        self, mock_mkdtemp, mock_mount, mock_mapped_device, mock_fs,
        mock_command, mock_os_exists
    ):
        mock_mkdtemp.return_value = 'tmpdir'
        toplevel_mount = Mock()
        mock_mount.return_value = toplevel_mount
        command_call = Mock()
        command_call.output = 'ID 256 gen 23 top level 5 path @'
        mock_mapped_device.return_value = 'mapped_device'
        mock_os_exists.return_value = False
        mock_command.return_value = command_call

        self.volume_manager.setup()

        mock_mount.assert_called_once_with(
            device='/dev/storage', mountpoint='tmpdir'
        )
        toplevel_mount.mount.assert_called_once_with([])
        assert mock_command.call_args_list == [
            call(['btrfs', 'subvolume', 'create', 'tmpdir/@']),
            call(['btrfs', 'subvolume', 'list', 'tmpdir']),
            call(['btrfs', 'subvolume', 'set-default', '256', 'tmpdir'])
        ]

    @patch('os.path.exists')
    @patch('kiwi.volume_manager.btrfs.Command.run')
    @patch('kiwi.volume_manager.btrfs.FileSystem')
    @patch('kiwi.volume_manager.btrfs.MappedDevice')
    @patch('kiwi.volume_manager.btrfs.MountManager')
    @patch('kiwi.volume_manager.base.mkdtemp')
    def test_setup_with_snapshot(
        self, mock_mkdtemp, mock_mount, mock_mapped_device, mock_fs,
        mock_command, mock_os_exists
    ):
        mock_mkdtemp.return_value = 'tmpdir'
        toplevel_mount = Mock()
        mock_mount.return_value = toplevel_mount
        command_call = Mock()
        command_call.output = \
            'ID 258 gen 26 top level 257 path @/.snapshots/1/snapshot'
        mock_mapped_device.return_value = 'mapped_device'
        mock_os_exists.return_value = False
        mock_command.return_value = command_call
        self.volume_manager.custom_args['root_is_snapshot'] = True
        self.volume_manager.custom_args['quota_groups'] = True

        self.volume_manager.setup()

        assert mock_mount.call_args_list == [
            call(device='/dev/storage', mountpoint='tmpdir'),
            call(device='/dev/storage', mountpoint='tmpdir/.snapshots')
        ]
        toplevel_mount.mount.assert_called_once_with([])
        assert mock_command.call_args_list == [
            call(['btrfs', 'quota', 'enable', 'tmpdir']),
            call(['btrfs', 'subvolume', 'create', 'tmpdir/@']),
            call(['btrfs', 'subvolume', 'create', 'tmpdir/@/.snapshots']),
            call(['mkdir', '-p', 'tmpdir/@/.snapshots/1']),
            call([
                'btrfs', 'subvolume', 'snapshot', 'tmpdir/@',
                'tmpdir/@/.snapshots/1/snapshot'
            ]),
            call(['btrfs', 'subvolume', 'list', 'tmpdir']),
            call(['btrfs', 'subvolume', 'set-default', '258', 'tmpdir'])
        ]

    @patch('os.path.exists')
    @patch('kiwi.volume_manager.btrfs.Command.run')
    @patch('kiwi.volume_manager.btrfs.FileSystem')
    @patch('kiwi.volume_manager.btrfs.MappedDevice')
    @patch('kiwi.volume_manager.btrfs.MountManager')
    def test_setup_volume_id_not_detected(
        self, mock_mount, mock_mapped_device, mock_fs,
        mock_command, mock_os_exists
    ):
        command_call = Mock()
        command_call.output = 'id-string-invalid'
        mock_mapped_device.return_value = 'mapped_device'
        mock_os_exists.return_value = False
        mock_command.return_value = command_call
        with raises(KiwiVolumeRootIDError):
            self.volume_manager.setup()

    @patch('os.path.exists')
    @patch('kiwi.volume_manager.btrfs.Command.run')
    @patch('kiwi.volume_manager.btrfs.MountManager')
    @patch('kiwi.volume_manager.btrfs.Path.create')
    @patch('kiwi.volume_manager.base.VolumeManagerBase.apply_attributes_on_volume')
    def test_create_volumes(
        self, mock_attrs, mock_path, mock_mount, mock_command, mock_os_exists
    ):
        volume_mount = Mock()
        mock_mount.return_value = volume_mount
        self.volume_manager.mountpoint = 'tmpdir'
        self.volume_manager.custom_args['root_is_snapshot'] = True
        mock_os_exists.return_value = False

        self.volume_manager.create_volumes('btrfs')

        assert mock_attrs.call_args_list == [
            call(
                'tmpdir/@/', self.volume_type(
                    name='myvol', size='size:500', realpath='/data',
                    mountpoint='LVdata', fullsize=False, attributes=[])
            ),
            call('tmpdir/@/', self.volume_type(
                name='LVetc', size='freespace:200', realpath='/etc',
                mountpoint='/etc', fullsize=False, attributes=[])
            ),
            call('tmpdir/@/', self.volume_type(
                name='LVhome', size=None, realpath='/home',
                mountpoint='/home', fullsize=True, attributes=[])
            )
        ]
        assert mock_path.call_args_list == [
            call('root_dir/etc'),
            call('root_dir/data'),
            call('root_dir/home'),
            call('tmpdir/@'),
            call('tmpdir/@'),
            call('tmpdir/@')
        ]
        assert mock_command.call_args_list == [
            call(['btrfs', 'subvolume', 'create', 'tmpdir/@/data']),
            call(['btrfs', 'subvolume', 'create', 'tmpdir/@/etc']),
            call(['btrfs', 'subvolume', 'create', 'tmpdir/@/home'])
        ]
        assert mock_mount.call_args_list == [
            call(
                device='/dev/storage',
                mountpoint='tmpdir/@/.snapshots/1/snapshot/data'
            ),
            call(
                device='/dev/storage',
                mountpoint='tmpdir/@/.snapshots/1/snapshot/etc'
            ),
            call(
                device='/dev/storage',
                mountpoint='tmpdir/@/.snapshots/1/snapshot/home'
            )
        ]

    def test_get_volumes(self):
        volume_mount = Mock()
        volume_mount.mountpoint = \
            '/tmp/kiwi_volumes.xx/@/.snapshots/1/snapshot/boot/grub2'
        volume_mount.device = 'device'
        self.volume_manager.toplevel_volume = '@/.snapshots/1/snapshot'
        self.volume_manager.subvol_mount_list = [volume_mount]
        self.volume_manager.custom_args['root_is_snapshot'] = True
        assert self.volume_manager.get_volumes() == {
            '/boot/grub2': {
                'volume_options': 'subvol=@/boot/grub2',
                'volume_device': 'device'
            }
        }

    @patch('kiwi.volume_manager.btrfs.Command.run')
    def test_get_fstab(self, mock_command):
        blkid_result = Mock()
        blkid_result.output = 'id'
        mock_command.return_value = blkid_result
        volume_mount = Mock()
        volume_mount.mountpoint = \
            '/tmp/kiwi_volumes.xx/@/.snapshots/1/snapshot/var/tmp'
        volume_mount.device = 'device'
        self.volume_manager.toplevel_volume = '@/.snapshots/1/snapshot'
        self.volume_manager.subvol_mount_list = [volume_mount]
        assert self.volume_manager.get_fstab() == [
            'LABEL=id /var/tmp btrfs defaults,subvol=@/var/tmp 0 0'
        ]

    @patch('os.path.exists')
    @patch('kiwi.volume_manager.btrfs.Path.create')
    def test_mount_volumes(self, mock_path, mock_os_exists):
        self.volume_manager.toplevel_mount = Mock()
        self.volume_manager.toplevel_mount.is_mounted = Mock(
            return_value=False
        )
        mock_os_exists.return_value = False
        volume_mount = Mock()
        volume_mount.mountpoint = \
            '/tmp/kiwi_volumes.xx/@/.snapshots/1/snapshot/var/tmp'
        self.volume_manager.toplevel_volume = '@/.snapshots/1/snapshot'
        self.volume_manager.custom_args['root_is_snapshot'] = True
        self.volume_manager.subvol_mount_list = [volume_mount]

        self.volume_manager.mount_volumes()

        self.volume_manager.toplevel_mount.mount.assert_called_once_with([])
        mock_path.assert_called_once_with(volume_mount.mountpoint)
        volume_mount.mount.assert_called_once_with(
            options=['subvol=@/var/tmp']
        )

    @patch('os.path.exists')
    @patch('kiwi.volume_manager.btrfs.Command.run')
    @patch('kiwi.volume_manager.btrfs.FileSystem')
    @patch('kiwi.volume_manager.btrfs.MappedDevice')
    @patch('kiwi.volume_manager.btrfs.MountManager')
    @patch('kiwi.volume_manager.base.mkdtemp')
    def test_remount_volumes(
        self, mock_mkdtemp, mock_mount, mock_mapped_device, mock_fs,
        mock_command, mock_os_exists
    ):
        mock_mkdtemp.return_value = '/tmp/kiwi_volumes.xx'
        toplevel_mount = Mock()
        toplevel_mount.is_mounted = Mock(
            return_value=False
        )
        mock_mount.return_value = toplevel_mount
        command_call = Mock()
        command_call.output = \
            'ID 258 gen 26 top level 257 path @/.snapshots/1/snapshot'
        mock_mapped_device.return_value = 'mapped_device'
        mock_os_exists.return_value = False
        mock_command.return_value = command_call
        self.volume_manager.custom_args['root_is_snapshot'] = True

        self.volume_manager.setup()

        mock_os_exists.return_value = True
        volume_mount = Mock()
        volume_mount.mountpoint = \
            '/tmp/kiwi_volumes.xx/@/.snapshots/1/snapshot/var/tmp'
        self.volume_manager.subvol_mount_list = [volume_mount]

        self.volume_manager.mount_volumes()
        self.volume_manager.umount_volumes()
        self.volume_manager.mount_volumes()

        assert volume_mount.mountpoint == '/tmp/kiwi_volumes.xx/var/tmp'

    def test_umount_volumes(self):
        self.volume_manager.toplevel_mount = Mock()
        volume_mount = Mock()
        volume_mount.is_mounted = Mock(
            return_value=True
        )
        self.volume_manager.toplevel_mount.is_mounted = Mock(
            return_value=True
        )
        self.volume_manager.subvol_mount_list = [volume_mount]
        self.volume_manager.umount_volumes()
        volume_mount.is_mounted.assert_called_once_with()
        volume_mount.umount.assert_called_once_with()
        self.volume_manager.toplevel_mount.is_mounted.assert_called_once_with()
        self.volume_manager.toplevel_mount.umount.assert_called_once_with()

    def test_umount_sub_volumes_busy(self):
        self.volume_manager.toplevel_mount = Mock()
        volume_mount = Mock()
        volume_mount.is_mounted = Mock(
            return_value=True
        )
        volume_mount.umount = Mock(
            return_value=False
        )
        self.volume_manager.subvol_mount_list = [volume_mount]
        assert self.volume_manager.umount_volumes() is False

    def test_umount_toplevel_busy(self):
        self.volume_manager.toplevel_mount = Mock()
        volume_mount = Mock()
        volume_mount.is_mounted = Mock(
            return_value=True
        )
        self.volume_manager.toplevel_mount.is_mounted = Mock(
            return_value=True
        )
        self.volume_manager.toplevel_mount.umount = Mock(
            return_value=False
        )
        assert self.volume_manager.umount_volumes() is False

    @patch('kiwi.volume_manager.btrfs.SysConfig')
    @patch('kiwi.volume_manager.btrfs.DataSync')
    @patch('kiwi.volume_manager.btrfs.Command.run')
    @patch('os.path.exists')
    @patch('shutil.copyfile')
    @patch.object(datetime, 'datetime', Mock(wraps=datetime.datetime))
    def test_sync_data(
        self, mock_copy, mock_exists, mock_command,
        mock_sync, mock_sysconf
    ):
        item = {'SNAPPER_CONFIGS': '""'}

        def getitem(key):
            return item[key]

        def setitem(key, value):
            item[key] = value

        def contains(key):
            return key in item

        def exists(name):
            if 'snapper/configs/root' in name:
                return False
            return True

        self.volume_manager.custom_args['quota_groups'] = True
        mock_exists.side_effect = exists

        sysconf = Mock()
        sysconf.__contains__ = Mock(side_effect=contains)
        sysconf.__getitem__ = Mock(side_effect=getitem)
        sysconf.__setitem__ = Mock(side_effect=setitem)
        mock_sysconf.return_value = sysconf

        xml_info = etree.tostring(etree.parse(
            '../data/info.xml', etree.XMLParser(remove_blank_text=True)
        ))
        datetime.datetime.now.return_value = datetime.datetime(2016, 1, 1)
        self.volume_manager.toplevel_mount = Mock()
        self.volume_manager.mountpoint = 'tmpdir'
        self.volume_manager.custom_args['root_is_snapshot'] = True
        sync = Mock()
        mock_sync.return_value = sync

        m_open = mock_open()
        with patch('builtins.open', m_open, create=True):
            self.volume_manager.sync_data(['exclude_me'])

        root_path = 'tmpdir/@/.snapshots/1/snapshot'
        mock_sync.assert_called_once_with('root_dir', root_path)
        mock_copy.assert_called_once_with(
            root_path + '/etc/snapper/config-templates/default',
            root_path + '/etc/snapper/configs/root'
        )
        sync.sync_data.assert_called_once_with(
            exclude=['exclude_me'],
            options=['-a', '-H', '-X', '-A', '--one-file-system']
        )
        assert m_open.call_args_list == [
            call('tmpdir/@/.snapshots/1/info.xml', 'w'),
        ]
        assert m_open.return_value.write.call_args_list == [
            call(minidom.parseString(xml_info).toprettyxml(indent="    "))
        ]
        assert mock_command.call_args_list == [
            call(['btrfs', 'qgroup', 'create', '1/0', 'tmpdir']),
            call([
                'chroot', 'tmpdir/@/.snapshots/1/snapshot',
                'snapper', '--no-dbus', 'set-config', 'QGROUP=1/0'
            ])
        ]

    @patch('kiwi.volume_manager.btrfs.SysConfig')
    @patch('kiwi.volume_manager.btrfs.DataSync')
    @patch('kiwi.volume_manager.btrfs.Command.run')
    @patch('os.path.exists')
    @patch.object(datetime, 'datetime', Mock(wraps=datetime.datetime))
    def test_sync_data_existing_bad_snapper_config(
        self, mock_exists, mock_command, mock_sync, mock_sysconf
    ):
        item = {'SNAPPER_CONFIGS': '"root foo"'}

        def getitem(key):
            return item[key]

        def contains(key):
            return key in item

        sysconf = Mock()
        sysconf.__contains__ = Mock(side_effect=contains)
        sysconf.__getitem__ = Mock(side_effect=getitem)
        mock_sysconf.return_value = sysconf

        self.volume_manager.custom_args['quota_groups'] = True
        mock_exists.return_value = True
        xml_info = etree.tostring(etree.parse(
            '../data/info.xml', etree.XMLParser(remove_blank_text=True)
        ))
        datetime.datetime.now.return_value = datetime.datetime(2016, 1, 1)
        self.volume_manager.toplevel_mount = Mock()
        self.volume_manager.mountpoint = 'tmpdir'
        self.volume_manager.custom_args['root_is_snapshot'] = True
        sync = Mock()
        mock_sync.return_value = sync

        m_open = mock_open()
        with patch('builtins.open', m_open, create=True):
            with raises(KiwiVolumeManagerSetupError):
                self.volume_manager.sync_data(['exclude_me'])

        mock_sync.assert_called_once_with(
            'root_dir', 'tmpdir/@/.snapshots/1/snapshot'
        )
        sync.sync_data.assert_called_once_with(
            exclude=['exclude_me'],
            options=['-a', '-H', '-X', '-A', '--one-file-system']
        )
        assert m_open.call_args_list == [
            call('tmpdir/@/.snapshots/1/info.xml', 'w'),
        ]
        assert m_open.return_value.write.call_args_list == [
            call(minidom.parseString(xml_info).toprettyxml(indent="    "))
        ]

    @patch('kiwi.volume_manager.btrfs.DataSync')
    @patch('kiwi.volume_manager.btrfs.Command.run')
    @patch('os.path.exists')
    @patch.object(datetime, 'datetime', Mock(wraps=datetime.datetime))
    def test_sync_data_no_root_snapshot(
        self, mock_exists, mock_command, mock_sync
    ):
        self.volume_manager.custom_args['quota_groups'] = True
        mock_exists.return_value = True
        datetime.datetime.now.return_value = datetime.datetime(2016, 1, 1)
        self.volume_manager.toplevel_mount = Mock()
        self.volume_manager.mountpoint = 'tmpdir'
        self.volume_manager.custom_args['root_is_snapshot'] = False
        sync = Mock()
        mock_sync.return_value = sync

        m_open = mock_open(read_data='')
        with patch('builtins.open', m_open, create=True):
            self.volume_manager.sync_data(['exclude_me'])

        mock_sync.assert_called_once_with(
            'root_dir', 'tmpdir/@'
        )
        sync.sync_data.assert_called_once_with(
            exclude=['exclude_me'],
            options=['-a', '-H', '-X', '-A', '--one-file-system']
        )

    @patch('kiwi.volume_manager.btrfs.Command.run')
    def test_set_property_readonly_root(self, mock_command):
        self.volume_manager.mountpoint = 'tmpdir'
        self.volume_manager.custom_args['root_is_snapshot'] = True
        self.volume_manager.custom_args['root_is_readonly_snapshot'] = True
        self.volume_manager.set_property_readonly_root()
        mock_command.assert_called_once_with(
            [
                'btrfs', 'property', 'set', 'tmpdir', 'ro', 'true'
            ]
        )

    @patch('kiwi.volume_manager.btrfs.VolumeManagerBtrfs.umount_volumes')
    @patch('kiwi.volume_manager.btrfs.Path.wipe')
    def test_destructor(self, mock_wipe, mock_umount_volumes):
        mock_umount_volumes.return_value = True
        self.volume_manager.toplevel_mount = Mock()
        self.volume_manager.__del__()
        mock_umount_volumes.assert_called_once_with()
        mock_wipe.assert_called_once_with(self.volume_manager.mountpoint)
Пример #9
0
class TestVolumeManagerBtrfs(object):
    @patch('os.path.exists')
    def setup(self, mock_path):
        self.volume_type = namedtuple(
            'volume_type', [
                'name',
                'size',
                'realpath',
                'mountpoint',
                'fullsize'
            ]
        )
        self.volumes = [
            self.volume_type(
                name='LVRoot', size='freespace:100', realpath='/',
                mountpoint=None, fullsize=False
            ),
            self.volume_type(
                name='LVetc', size='freespace:200', realpath='/etc',
                mountpoint='/etc', fullsize=False
            ),
            self.volume_type(
                name='myvol', size='size:500', realpath='/data',
                mountpoint='LVdata', fullsize=False
            ),
            self.volume_type(
                name='LVhome', size=None, realpath='/home',
                mountpoint='/home', fullsize=True
            ),
        ]
        mock_path.return_value = True
        self.device_provider = mock.Mock()
        self.device_provider.is_loop = mock.Mock(
            return_value=True
        )
        self.device_provider.get_device = mock.Mock(
            return_value='/dev/storage'
        )
        self.volume_manager = VolumeManagerBtrfs(
            self.device_provider, 'root_dir', self.volumes
        )

    def test_post_init(self):
        self.volume_manager.post_init({'some-arg': 'some-val'})
        assert self.volume_manager.custom_args['some-arg'] == 'some-val'

    @patch('os.path.exists')
    @patch('kiwi.volume_manager.btrfs.Command.run')
    @patch('kiwi.volume_manager.btrfs.FileSystem')
    @patch('kiwi.volume_manager.btrfs.MappedDevice')
    @patch('kiwi.volume_manager.btrfs.MountManager')
    @patch('kiwi.volume_manager.base.mkdtemp')
    def test_setup_no_snapshot(
        self, mock_mkdtemp, mock_mount, mock_mapped_device, mock_fs,
        mock_command, mock_os_exists
    ):
        mock_mkdtemp.return_value = 'tmpdir'
        toplevel_mount = mock.Mock()
        mock_mount.return_value = toplevel_mount
        command_call = mock.Mock()
        command_call.output = 'ID 256 gen 23 top level 5 path @'
        mock_mapped_device.return_value = 'mapped_device'
        mock_os_exists.return_value = False
        mock_command.return_value = command_call

        self.volume_manager.setup()

        mock_mount.assert_called_once_with(
            device='/dev/storage', mountpoint='tmpdir'
        )
        toplevel_mount.mount.assert_called_once_with([])
        assert mock_command.call_args_list == [
            call(['btrfs', 'subvolume', 'create', 'tmpdir/@']),
            call(['btrfs', 'subvolume', 'list', 'tmpdir']),
            call(['btrfs', 'subvolume', 'set-default', '256', 'tmpdir'])
        ]

    @patch('os.path.exists')
    @patch('kiwi.volume_manager.btrfs.Command.run')
    @patch('kiwi.volume_manager.btrfs.FileSystem')
    @patch('kiwi.volume_manager.btrfs.MappedDevice')
    @patch('kiwi.volume_manager.btrfs.MountManager')
    @patch('kiwi.volume_manager.base.mkdtemp')
    def test_setup_with_snapshot(
        self, mock_mkdtemp, mock_mount, mock_mapped_device, mock_fs,
        mock_command, mock_os_exists
    ):
        mock_mkdtemp.return_value = 'tmpdir'
        toplevel_mount = mock.Mock()
        mock_mount.return_value = toplevel_mount
        command_call = mock.Mock()
        command_call.output = \
            'ID 258 gen 26 top level 257 path @/.snapshots/1/snapshot'
        mock_mapped_device.return_value = 'mapped_device'
        mock_os_exists.return_value = False
        mock_command.return_value = command_call
        self.volume_manager.custom_args['root_is_snapshot'] = True

        self.volume_manager.setup()

        mock_mount.assert_called_once_with(
            device='/dev/storage', mountpoint='tmpdir'
        )
        toplevel_mount.mount.assert_called_once_with([])
        assert mock_command.call_args_list == [
            call(['btrfs', 'subvolume', 'create', 'tmpdir/@']),
            call(['btrfs', 'subvolume', 'create', 'tmpdir/@/.snapshots']),
            call(['mkdir', '-p', 'tmpdir/@/.snapshots/1']),
            call([
                'btrfs', 'subvolume', 'snapshot', 'tmpdir/@',
                'tmpdir/@/.snapshots/1/snapshot'
            ]),
            call(['btrfs', 'subvolume', 'list', 'tmpdir']),
            call(['btrfs', 'subvolume', 'set-default', '258', 'tmpdir'])
        ]

    @raises(KiwiVolumeRootIDError)
    @patch('os.path.exists')
    @patch('kiwi.volume_manager.btrfs.Command.run')
    @patch('kiwi.volume_manager.btrfs.FileSystem')
    @patch('kiwi.volume_manager.btrfs.MappedDevice')
    @patch('kiwi.volume_manager.btrfs.MountManager')
    def test_setup_volume_id_not_detected(
        self, mock_mount, mock_mapped_device, mock_fs,
        mock_command, mock_os_exists
    ):
        command_call = mock.Mock()
        command_call.output = 'id-string-invalid'
        mock_mapped_device.return_value = 'mapped_device'
        mock_os_exists.return_value = False
        mock_command.return_value = command_call
        self.volume_manager.setup()

    @patch('os.path.exists')
    @patch('kiwi.volume_manager.btrfs.Command.run')
    @patch('kiwi.volume_manager.btrfs.MountManager')
    @patch('kiwi.volume_manager.btrfs.Path.create')
    def test_create_volumes(
        self, mock_path, mock_mount, mock_command, mock_os_exists
    ):
        volume_mount = mock.Mock()
        mock_mount.return_value = volume_mount
        self.volume_manager.mountpoint = 'tmpdir'
        self.volume_manager.custom_args['root_is_snapshot'] = True
        mock_os_exists.return_value = False

        self.volume_manager.create_volumes('btrfs')

        assert mock_path.call_args_list == [
            call('root_dir/etc'),
            call('root_dir/data'),
            call('root_dir/home'),
            call('tmpdir/@'),
            call('tmpdir/@'),
            call('tmpdir/@')
        ]
        assert mock_command.call_args_list == [
            call(['btrfs', 'subvolume', 'create', 'tmpdir/@/data']),
            call(['btrfs', 'subvolume', 'create', 'tmpdir/@/etc']),
            call(['btrfs', 'subvolume', 'create', 'tmpdir/@/home'])
        ]
        assert mock_mount.call_args_list == [
            call(
               device='/dev/storage',
               mountpoint='tmpdir/@/.snapshots/1/snapshot/data'
            ),
            call(
                device='/dev/storage',
                mountpoint='tmpdir/@/.snapshots/1/snapshot/etc'
            ),
            call(
                device='/dev/storage',
                mountpoint='tmpdir/@/.snapshots/1/snapshot/home'
            )
        ]

    @patch('os.path.exists')
    @patch('kiwi.volume_manager.btrfs.Path.create')
    def test_mount_volumes(self, mock_path, mock_os_exists):
        mock_os_exists.return_value = False
        volume_mount = mock.Mock()
        volume_mount.mountpoint = 'tmpdir/@/.snapshots/1/snapshot/subvol'
        self.volume_manager.subvol_mount_list = [volume_mount]

        self.volume_manager.mount_volumes()

        mock_path.assert_called_once_with(volume_mount.mountpoint)
        volume_mount.mount.assert_called_once_with(
            options=['subvol=@/subvol']
        )

    def test_umount_volumes(self):
        self.volume_manager.toplevel_mount = mock.Mock()
        volume_mount = mock.Mock()
        volume_mount.is_mounted = mock.Mock(
            return_value=True
        )
        self.volume_manager.toplevel_mount.is_mounted = mock.Mock(
            return_value=True
        )
        self.volume_manager.subvol_mount_list = [volume_mount]
        self.volume_manager.umount_volumes()
        volume_mount.is_mounted.assert_called_once_with()
        volume_mount.umount.assert_called_once_with()
        self.volume_manager.toplevel_mount.is_mounted.assert_called_once_with()
        self.volume_manager.toplevel_mount.umount.assert_called_once_with()

    def test_umount_sub_volumes_busy(self):
        self.volume_manager.toplevel_mount = mock.Mock()
        volume_mount = mock.Mock()
        volume_mount.is_mounted = mock.Mock(
            return_value=True
        )
        volume_mount.umount = mock.Mock(
            return_value=False
        )
        self.volume_manager.subvol_mount_list = [volume_mount]
        assert self.volume_manager.umount_volumes() is False

    def test_umount_toplevel_busy(self):
        self.volume_manager.toplevel_mount = mock.Mock()
        volume_mount = mock.Mock()
        volume_mount.is_mounted = mock.Mock(
            return_value=True
        )
        self.volume_manager.toplevel_mount.is_mounted = mock.Mock(
            return_value=True
        )
        self.volume_manager.toplevel_mount.umount = mock.Mock(
            return_value=False
        )
        assert self.volume_manager.umount_volumes() is False

    @patch('kiwi.volume_manager.btrfs.DataSync')
    def test_sync_data(self, mock_sync):
        self.volume_manager.toplevel_mount = mock.Mock()
        self.volume_manager.mountpoint = 'tmpdir'
        self.volume_manager.custom_args['root_is_snapshot'] = True
        sync = mock.Mock()
        mock_sync.return_value = sync

        self.volume_manager.sync_data(['exclude_me'])

        mock_sync.assert_called_once_with(
            'root_dir', 'tmpdir/@/.snapshots/1/snapshot'
        )
        sync.sync_data.assert_called_once_with(
            exclude=['exclude_me'],
            options=['-a', '-H', '-X', '-A', '--one-file-system']
        )

    @patch('kiwi.volume_manager.btrfs.VolumeManagerBtrfs.umount_volumes')
    @patch('kiwi.volume_manager.btrfs.Path.wipe')
    def test_destructor(self, mock_wipe, mock_umount_volumes):
        mock_umount_volumes.return_value = True
        self.volume_manager.toplevel_mount = mock.Mock()
        self.volume_manager.__del__()
        mock_umount_volumes.assert_called_once_with()
        mock_wipe.assert_called_once_with(self.volume_manager.mountpoint)