Clone VM's volume into the same pool, unless overridden specifically
When cloning VM, create it in the same pool as the source one. Previously it always used default pool, which means for example renaming a VM in non-default pool moved it back to the default one. Fixes QubesOS/qubes-issues#4145 Fixes QubesOS/qubes-issues#4523
This commit is contained in:
		
							parent
							
								
									1c82441781
								
							
						
					
					
						commit
						e827e47926
					
				@ -329,6 +329,21 @@ class QubesBase(qubesadmin.base.PropertyHolder):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        label = src_vm.label
 | 
					        label = src_vm.label
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if pool is None and pools is None:
 | 
				
			||||||
 | 
					            # use the same pools as the source - check if non default is used
 | 
				
			||||||
 | 
					            for volume in sorted(src_vm.volumes.values()):
 | 
				
			||||||
 | 
					                if not volume.save_on_stop:
 | 
				
			||||||
 | 
					                    # clone only persistent volumes
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					                if ignore_volumes and volume.name in ignore_volumes:
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					                default_pool = getattr(self.app, 'default_pool_' + volume.name,
 | 
				
			||||||
 | 
					                    volume.pool)
 | 
				
			||||||
 | 
					                if default_pool != volume.pool:
 | 
				
			||||||
 | 
					                    if pools is None:
 | 
				
			||||||
 | 
					                        pools = {}
 | 
				
			||||||
 | 
					                    pools[volume.name] = volume.pool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        method_prefix = 'admin.vm.Create.'
 | 
					        method_prefix = 'admin.vm.Create.'
 | 
				
			||||||
        payload = 'name={} label={}'.format(new_name, label)
 | 
					        payload = 'name={} label={}'.format(new_name, label)
 | 
				
			||||||
        if pool:
 | 
					        if pool:
 | 
				
			||||||
 | 
				
			|||||||
@ -298,61 +298,62 @@ class TC_10_QubesBase(qubesadmin.tests.QubesTestCase):
 | 
				
			|||||||
            b'0\x00'
 | 
					            b'0\x00'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # storage
 | 
					        # storage
 | 
				
			||||||
        self.app.expected_calls[
 | 
					        for vm in (src, dst):
 | 
				
			||||||
            (dst, 'admin.vm.volume.List', None, None)] = \
 | 
					            self.app.expected_calls[
 | 
				
			||||||
            b'0\x00root\nprivate\nvolatile\nkernel\n'
 | 
					                (vm, 'admin.vm.volume.Info', 'root', None)] = \
 | 
				
			||||||
        self.app.expected_calls[
 | 
					                b'0\x00pool=lvm\n' \
 | 
				
			||||||
            (dst, 'admin.vm.volume.Info', 'root', None)] = \
 | 
					                b'vid=vm-' + vm.encode() + b'/root\n' \
 | 
				
			||||||
            b'0\x00pool=lvm\n' \
 | 
					                b'size=10737418240\n' \
 | 
				
			||||||
            b'vid=vm-test-vm/root\n' \
 | 
					                b'usage=2147483648\n' \
 | 
				
			||||||
            b'size=10737418240\n' \
 | 
					                b'rw=False\n' \
 | 
				
			||||||
            b'usage=2147483648\n' \
 | 
					                b'internal=True\n' \
 | 
				
			||||||
            b'rw=False\n' \
 | 
					                b'source=vm-test-template/root\n' \
 | 
				
			||||||
            b'internal=True\n' \
 | 
					                b'save_on_stop=False\n' \
 | 
				
			||||||
            b'source=vm-test-template/root\n' \
 | 
					                b'snap_on_start=True\n'
 | 
				
			||||||
            b'save_on_stop=False\n' \
 | 
					            self.app.expected_calls[
 | 
				
			||||||
            b'snap_on_start=True\n'
 | 
					                (vm, 'admin.vm.volume.Info', 'private', None)] = \
 | 
				
			||||||
        self.app.expected_calls[
 | 
					                b'0\x00pool=lvm\n' \
 | 
				
			||||||
            (dst, 'admin.vm.volume.Info', 'private', None)] = \
 | 
					                b'vid=vm-' + vm.encode() + b'/private\n' \
 | 
				
			||||||
            b'0\x00pool=lvm\n' \
 | 
					                b'size=2147483648\n' \
 | 
				
			||||||
            b'vid=vm-test-vm/private\n' \
 | 
					                b'usage=214748364\n' \
 | 
				
			||||||
            b'size=2147483648\n' \
 | 
					                b'rw=True\n' \
 | 
				
			||||||
            b'usage=214748364\n' \
 | 
					                b'internal=True\n' \
 | 
				
			||||||
            b'rw=True\n' \
 | 
					                b'save_on_stop=True\n' \
 | 
				
			||||||
            b'internal=True\n' \
 | 
					                b'snap_on_start=False\n'
 | 
				
			||||||
            b'save_on_stop=True\n' \
 | 
					            self.app.expected_calls[
 | 
				
			||||||
            b'snap_on_start=False\n'
 | 
					                (vm, 'admin.vm.volume.Info', 'volatile', None)] = \
 | 
				
			||||||
        self.app.expected_calls[
 | 
					                b'0\x00pool=lvm\n' \
 | 
				
			||||||
            (dst, 'admin.vm.volume.Info', 'volatile', None)] = \
 | 
					                b'vid=vm-' + vm.encode() + b'/volatile\n' \
 | 
				
			||||||
            b'0\x00pool=lvm\n' \
 | 
					                b'size=10737418240\n' \
 | 
				
			||||||
            b'vid=vm-test-vm/volatile\n' \
 | 
					                b'usage=0\n' \
 | 
				
			||||||
            b'size=10737418240\n' \
 | 
					                b'rw=True\n' \
 | 
				
			||||||
            b'usage=0\n' \
 | 
					                b'internal=True\n' \
 | 
				
			||||||
            b'rw=True\n' \
 | 
					                b'source=None\n' \
 | 
				
			||||||
            b'internal=True\n' \
 | 
					                b'save_on_stop=False\n' \
 | 
				
			||||||
            b'source=None\n' \
 | 
					                b'snap_on_start=False\n'
 | 
				
			||||||
            b'save_on_stop=False\n' \
 | 
					            self.app.expected_calls[
 | 
				
			||||||
            b'snap_on_start=False\n'
 | 
					                (vm, 'admin.vm.volume.Info', 'kernel', None)] = \
 | 
				
			||||||
        self.app.expected_calls[
 | 
					                b'0\x00pool=linux-kernel\n' \
 | 
				
			||||||
            (dst, 'admin.vm.volume.Info', 'kernel', None)] = \
 | 
					                b'vid=\n' \
 | 
				
			||||||
            b'0\x00pool=linux-kernel\n' \
 | 
					                b'size=0\n' \
 | 
				
			||||||
            b'vid=\n' \
 | 
					                b'usage=0\n' \
 | 
				
			||||||
            b'size=0\n' \
 | 
					                b'rw=False\n' \
 | 
				
			||||||
            b'usage=0\n' \
 | 
					                b'internal=True\n' \
 | 
				
			||||||
            b'rw=False\n' \
 | 
					                b'source=None\n' \
 | 
				
			||||||
            b'internal=True\n' \
 | 
					                b'save_on_stop=False\n' \
 | 
				
			||||||
            b'source=None\n' \
 | 
					                b'snap_on_start=False\n'
 | 
				
			||||||
            b'save_on_stop=False\n' \
 | 
					            self.app.expected_calls[
 | 
				
			||||||
            b'snap_on_start=False\n'
 | 
					                (vm, 'admin.vm.volume.List', None, None)] = \
 | 
				
			||||||
        self.app.expected_calls[
 | 
					                b'0\x00root\nprivate\nvolatile\nkernel\n'
 | 
				
			||||||
            (src, 'admin.vm.volume.List', None, None)] = \
 | 
					 | 
				
			||||||
            b'0\x00root\nprivate\nvolatile\nkernel\n'
 | 
					 | 
				
			||||||
        self.app.expected_calls[
 | 
					        self.app.expected_calls[
 | 
				
			||||||
            (src, 'admin.vm.volume.CloneFrom', 'private', None)] = \
 | 
					            (src, 'admin.vm.volume.CloneFrom', 'private', None)] = \
 | 
				
			||||||
            b'0\x00token-private'
 | 
					            b'0\x00token-private'
 | 
				
			||||||
        self.app.expected_calls[
 | 
					        self.app.expected_calls[
 | 
				
			||||||
            (dst, 'admin.vm.volume.CloneTo', 'private', b'token-private')] = \
 | 
					            (dst, 'admin.vm.volume.CloneTo', 'private', b'token-private')] = \
 | 
				
			||||||
            b'0\x00'
 | 
					            b'0\x00'
 | 
				
			||||||
 | 
					        self.app.expected_calls[
 | 
				
			||||||
 | 
					            ('dom0', 'admin.property.Get', 'default_pool_private', None)] = \
 | 
				
			||||||
 | 
					            b'0\0default=True type=str lvm'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_030_clone(self):
 | 
					    def test_030_clone(self):
 | 
				
			||||||
        self.clone_setup_common_calls('test-vm', 'new-name')
 | 
					        self.clone_setup_common_calls('test-vm', 'new-name')
 | 
				
			||||||
@ -387,6 +388,11 @@ class TC_10_QubesBase(qubesadmin.tests.QubesTestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def test_032_clone_pool(self):
 | 
					    def test_032_clone_pool(self):
 | 
				
			||||||
        self.clone_setup_common_calls('test-vm', 'new-name')
 | 
					        self.clone_setup_common_calls('test-vm', 'new-name')
 | 
				
			||||||
 | 
					        for volume in ('root', 'private', 'volatile', 'kernel'):
 | 
				
			||||||
 | 
					            del self.app.expected_calls[
 | 
				
			||||||
 | 
					                'test-vm', 'admin.vm.volume.Info', volume, None]
 | 
				
			||||||
 | 
					        del self.app.expected_calls[
 | 
				
			||||||
 | 
					            'dom0', 'admin.property.Get', 'default_pool_private', None]
 | 
				
			||||||
        self.app.expected_calls[('dom0', 'admin.vm.CreateInPool.AppVM',
 | 
					        self.app.expected_calls[('dom0', 'admin.vm.CreateInPool.AppVM',
 | 
				
			||||||
            'test-template',
 | 
					            'test-template',
 | 
				
			||||||
            b'name=new-name label=red pool=some-pool')] = b'0\x00'
 | 
					            b'name=new-name label=red pool=some-pool')] = b'0\x00'
 | 
				
			||||||
@ -401,6 +407,11 @@ class TC_10_QubesBase(qubesadmin.tests.QubesTestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def test_033_clone_pools(self):
 | 
					    def test_033_clone_pools(self):
 | 
				
			||||||
        self.clone_setup_common_calls('test-vm', 'new-name')
 | 
					        self.clone_setup_common_calls('test-vm', 'new-name')
 | 
				
			||||||
 | 
					        for volume in ('root', 'private', 'volatile', 'kernel'):
 | 
				
			||||||
 | 
					            del self.app.expected_calls[
 | 
				
			||||||
 | 
					                'test-vm', 'admin.vm.volume.Info', volume, None]
 | 
				
			||||||
 | 
					        del self.app.expected_calls[
 | 
				
			||||||
 | 
					            'dom0', 'admin.property.Get', 'default_pool_private', None]
 | 
				
			||||||
        self.app.expected_calls[('dom0', 'admin.vm.CreateInPool.AppVM',
 | 
					        self.app.expected_calls[('dom0', 'admin.vm.CreateInPool.AppVM',
 | 
				
			||||||
            'test-template',
 | 
					            'test-template',
 | 
				
			||||||
            b'name=new-name label=red pool:private=some-pool '
 | 
					            b'name=new-name label=red pool:private=some-pool '
 | 
				
			||||||
@ -451,6 +462,10 @@ class TC_10_QubesBase(qubesadmin.tests.QubesTestCase):
 | 
				
			|||||||
        self.app.expected_calls[
 | 
					        self.app.expected_calls[
 | 
				
			||||||
            ('test-vm', 'admin.vm.property.List', None, None)] = \
 | 
					            ('test-vm', 'admin.vm.property.List', None, None)] = \
 | 
				
			||||||
            b'0\0qid\nname\ntemplate\nlabel\nmemory\n'
 | 
					            b'0\0qid\nname\ntemplate\nlabel\nmemory\n'
 | 
				
			||||||
 | 
					        # simplify it a little, shouldn't get this far anyway
 | 
				
			||||||
 | 
					        self.app.expected_calls[
 | 
				
			||||||
 | 
					            ('test-vm', 'admin.vm.volume.List', None, None)] = \
 | 
				
			||||||
 | 
					            b'0\x00'
 | 
				
			||||||
        self.app.expected_calls[
 | 
					        self.app.expected_calls[
 | 
				
			||||||
            ('test-vm', 'admin.vm.property.Get', 'label', None)] = \
 | 
					            ('test-vm', 'admin.vm.property.Get', 'label', None)] = \
 | 
				
			||||||
            b'0\0default=False type=label red'
 | 
					            b'0\0default=False type=label red'
 | 
				
			||||||
@ -587,6 +602,34 @@ class TC_10_QubesBase(qubesadmin.tests.QubesTestCase):
 | 
				
			|||||||
            self.app.clone_vm('test-vm', 'new-name')
 | 
					            self.app.clone_vm('test-vm', 'new-name')
 | 
				
			||||||
        self.assertAllCalled()
 | 
					        self.assertAllCalled()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_042_clone_nondefault_pool(self):
 | 
				
			||||||
 | 
					        self.clone_setup_common_calls('test-vm', 'new-name')
 | 
				
			||||||
 | 
					        self.app.expected_calls[
 | 
				
			||||||
 | 
					            ('test-vm', 'admin.vm.volume.Info', 'private', None)] = \
 | 
				
			||||||
 | 
					            b'0\x00pool=another\n' \
 | 
				
			||||||
 | 
					            b'vid=vm-test-vm/private\n' \
 | 
				
			||||||
 | 
					            b'size=2147483648\n' \
 | 
				
			||||||
 | 
					            b'usage=214748364\n' \
 | 
				
			||||||
 | 
					            b'rw=True\n' \
 | 
				
			||||||
 | 
					            b'internal=True\n' \
 | 
				
			||||||
 | 
					            b'save_on_stop=True\n' \
 | 
				
			||||||
 | 
					            b'snap_on_start=False\n'
 | 
				
			||||||
 | 
					        self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
 | 
				
			||||||
 | 
					            b'0\x00new-name class=AppVM state=Halted\n' \
 | 
				
			||||||
 | 
					            b'test-vm class=AppVM state=Halted\n' \
 | 
				
			||||||
 | 
					            b'test-template class=TemplateVM state=Halted\n' \
 | 
				
			||||||
 | 
					            b'test-net class=AppVM state=Halted\n'
 | 
				
			||||||
 | 
					        self.app.expected_calls[('dom0', 'admin.vm.CreateInPool.AppVM',
 | 
				
			||||||
 | 
					            'test-template', b'name=new-name label=red pool:private=another')]\
 | 
				
			||||||
 | 
					            = b'0\x00'
 | 
				
			||||||
 | 
					        new_vm = self.app.clone_vm('test-vm', 'new-name')
 | 
				
			||||||
 | 
					        self.assertEqual(new_vm.name, 'new-name')
 | 
				
			||||||
 | 
					        self.check_output_mock.assert_called_once_with(
 | 
				
			||||||
 | 
					            ['qvm-appmenus', '--init', '--update',
 | 
				
			||||||
 | 
					                '--source', 'test-vm', 'new-name'],
 | 
				
			||||||
 | 
					            stderr=subprocess.STDOUT
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertAllCalled()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TC_20_QubesLocal(unittest.TestCase):
 | 
					class TC_20_QubesLocal(unittest.TestCase):
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user