Merge branch 'maxmem'
This commit is contained in:
		
						commit
						3728230e3c
					
				@ -39,10 +39,28 @@ class ServicesExtension(qubes.ext.Extension):
 | 
				
			|||||||
            vm.untrusted_qdb.write('/qubes-service/{}'.format(service),
 | 
					            vm.untrusted_qdb.write('/qubes-service/{}'.format(service),
 | 
				
			||||||
                str(int(bool(value))))
 | 
					                str(int(bool(value))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # always set meminfo-writer according to maxmem
 | 
				
			||||||
 | 
					        vm.untrusted_qdb.write('/qubes-service/meminfo-writer',
 | 
				
			||||||
 | 
					            '1' if vm.maxmem > 0 else '0')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @qubes.ext.handler('domain-feature-set:*')
 | 
					    @qubes.ext.handler('domain-feature-set:*')
 | 
				
			||||||
    def on_domain_feature_set(self, vm, event, feature, value, oldvalue=None):
 | 
					    def on_domain_feature_set(self, vm, event, feature, value, oldvalue=None):
 | 
				
			||||||
        '''Update /qubes-service/ QubesDB tree in runtime'''
 | 
					        '''Update /qubes-service/ QubesDB tree in runtime'''
 | 
				
			||||||
        # pylint: disable=unused-argument
 | 
					        # pylint: disable=unused-argument
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # TODO: remove this compatibility hack in Qubes 4.1
 | 
				
			||||||
 | 
					        if feature == 'service.meminfo-writer':
 | 
				
			||||||
 | 
					            # if someone try to enable meminfo-writer ...
 | 
				
			||||||
 | 
					            if value:
 | 
				
			||||||
 | 
					                # ... reset maxmem to default
 | 
				
			||||||
 | 
					                vm.maxmem = qubes.property.DEFAULT
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                # otherwise, set to 0
 | 
				
			||||||
 | 
					                vm.maxmem = 0
 | 
				
			||||||
 | 
					            # in any case, remove the entry, as it does not indicate memory
 | 
				
			||||||
 | 
					            # balancing state anymore
 | 
				
			||||||
 | 
					            del vm.features['service.meminfo-writer']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not vm.is_running():
 | 
					        if not vm.is_running():
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        if not feature.startswith('service.'):
 | 
					        if not feature.startswith('service.'):
 | 
				
			||||||
@ -61,8 +79,22 @@ class ServicesExtension(qubes.ext.Extension):
 | 
				
			|||||||
        if not feature.startswith('service.'):
 | 
					        if not feature.startswith('service.'):
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        service = feature[len('service.'):]
 | 
					        service = feature[len('service.'):]
 | 
				
			||||||
 | 
					        # this one is excluded from user control
 | 
				
			||||||
 | 
					        if service == 'meminfo-writer':
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
        vm.untrusted_qdb.rm('/qubes-service/{}'.format(service))
 | 
					        vm.untrusted_qdb.rm('/qubes-service/{}'.format(service))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @qubes.ext.handler('domain-load')
 | 
				
			||||||
 | 
					    def on_domain_load(self, vm, event):
 | 
				
			||||||
 | 
					        '''Migrate meminfo-writer service into maxmem'''
 | 
				
			||||||
 | 
					        # pylint: disable=no-self-use,unused-argument
 | 
				
			||||||
 | 
					        if 'service.meminfo-writer' in vm.features:
 | 
				
			||||||
 | 
					            # if was set to false, force maxmem=0
 | 
				
			||||||
 | 
					            # otherwise, simply ignore as the default is fine
 | 
				
			||||||
 | 
					            if not vm.features['service.meminfo-writer']:
 | 
				
			||||||
 | 
					                vm.maxmem = 0
 | 
				
			||||||
 | 
					            del vm.features['service.meminfo-writer']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @qubes.ext.handler('features-request')
 | 
					    @qubes.ext.handler('features-request')
 | 
				
			||||||
    def supported_services(self, vm, event, untrusted_features):
 | 
					    def supported_services(self, vm, event, untrusted_features):
 | 
				
			||||||
        '''Handle advertisement of supported services'''
 | 
					        '''Handle advertisement of supported services'''
 | 
				
			||||||
 | 
				
			|||||||
@ -235,6 +235,7 @@ class TC_20_Services(qubes.tests.QubesTestCase):
 | 
				
			|||||||
        self.features = {}
 | 
					        self.features = {}
 | 
				
			||||||
        self.vm.configure_mock(**{
 | 
					        self.vm.configure_mock(**{
 | 
				
			||||||
            'template': None,
 | 
					            'template': None,
 | 
				
			||||||
 | 
					            'maxmem': 1024,
 | 
				
			||||||
            'is_running.return_value': True,
 | 
					            'is_running.return_value': True,
 | 
				
			||||||
            'features.get.side_effect': self.features.get,
 | 
					            'features.get.side_effect': self.features.get,
 | 
				
			||||||
            'features.items.side_effect': self.features.items,
 | 
					            'features.items.side_effect': self.features.items,
 | 
				
			||||||
@ -250,6 +251,7 @@ class TC_20_Services(qubes.tests.QubesTestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        self.ext.on_domain_qdb_create(self.vm, 'domain-qdb-create')
 | 
					        self.ext.on_domain_qdb_create(self.vm, 'domain-qdb-create')
 | 
				
			||||||
        self.assertEqual(sorted(self.vm.untrusted_qdb.mock_calls), [
 | 
					        self.assertEqual(sorted(self.vm.untrusted_qdb.mock_calls), [
 | 
				
			||||||
 | 
					            ('write', ('/qubes-service/meminfo-writer', '1'), {}),
 | 
				
			||||||
            ('write', ('/qubes-service/test1', '1'), {}),
 | 
					            ('write', ('/qubes-service/test1', '1'), {}),
 | 
				
			||||||
            ('write', ('/qubes-service/test2', '0'), {}),
 | 
					            ('write', ('/qubes-service/test2', '0'), {}),
 | 
				
			||||||
        ])
 | 
					        ])
 | 
				
			||||||
 | 
				
			|||||||
@ -44,6 +44,22 @@ class TestApp(object):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def __init__(self):
 | 
					    def __init__(self):
 | 
				
			||||||
        self.domains = {}
 | 
					        self.domains = {}
 | 
				
			||||||
 | 
					        self.host = unittest.mock.Mock()
 | 
				
			||||||
 | 
					        self.host.memory_total = 4096 * 1024
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestFeatures(dict):
 | 
				
			||||||
 | 
					    def __init__(self, vm, **kwargs) -> None:
 | 
				
			||||||
 | 
					        self.vm = vm
 | 
				
			||||||
 | 
					        super().__init__(**kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def check_with_template(self, feature, default):
 | 
				
			||||||
 | 
					        vm = self.vm
 | 
				
			||||||
 | 
					        while vm is not None:
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                return vm.features[feature]
 | 
				
			||||||
 | 
					            except KeyError:
 | 
				
			||||||
 | 
					                vm = getattr(vm, 'template', None)
 | 
				
			||||||
 | 
					        return default
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestProp(object):
 | 
					class TestProp(object):
 | 
				
			||||||
    # pylint: disable=too-few-public-methods
 | 
					    # pylint: disable=too-few-public-methods
 | 
				
			||||||
@ -80,6 +96,7 @@ class TestVM(object):
 | 
				
			|||||||
        for k, v in kwargs.items():
 | 
					        for k, v in kwargs.items():
 | 
				
			||||||
            setattr(self, k, v)
 | 
					            setattr(self, k, v)
 | 
				
			||||||
        self.devices = {'pci': TestDeviceCollection()}
 | 
					        self.devices = {'pci': TestDeviceCollection()}
 | 
				
			||||||
 | 
					        self.features = TestFeatures(self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def is_running(self):
 | 
					    def is_running(self):
 | 
				
			||||||
        return self.running
 | 
					        return self.running
 | 
				
			||||||
@ -170,8 +187,6 @@ class TC_10_default(qubes.tests.QubesTestCase):
 | 
				
			|||||||
        self.assertEqual(default_getter(self.vm), 'template-kernel')
 | 
					        self.assertEqual(default_getter(self.vm), 'template-kernel')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_010_default_virt_mode(self):
 | 
					    def test_010_default_virt_mode(self):
 | 
				
			||||||
        default_getter = qubes.vm.qubesvm._default_with_template('kernel',
 | 
					 | 
				
			||||||
            lambda x: x.app.default_kernel)
 | 
					 | 
				
			||||||
        self.assertEqual(qubes.vm.qubesvm._default_virt_mode(self.vm),
 | 
					        self.assertEqual(qubes.vm.qubesvm._default_virt_mode(self.vm),
 | 
				
			||||||
            'pvh')
 | 
					            'pvh')
 | 
				
			||||||
        self.vm.template = unittest.mock.Mock()
 | 
					        self.vm.template = unittest.mock.Mock()
 | 
				
			||||||
@ -185,6 +200,48 @@ class TC_10_default(qubes.tests.QubesTestCase):
 | 
				
			|||||||
        self.assertEqual(qubes.vm.qubesvm._default_virt_mode(self.vm),
 | 
					        self.assertEqual(qubes.vm.qubesvm._default_virt_mode(self.vm),
 | 
				
			||||||
            'hvm')
 | 
					            'hvm')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_020_default_maxmem(self):
 | 
				
			||||||
 | 
					        default_maxmem = 2048
 | 
				
			||||||
 | 
					        self.vm.is_memory_balancing_possible = \
 | 
				
			||||||
 | 
					            lambda: qubes.vm.qubesvm.QubesVM.is_memory_balancing_possible(
 | 
				
			||||||
 | 
					                self.vm)
 | 
				
			||||||
 | 
					        self.vm.virt_mode = 'pvh'
 | 
				
			||||||
 | 
					        self.assertEqual(qubes.vm.qubesvm._default_maxmem(self.vm),
 | 
				
			||||||
 | 
					            default_maxmem)
 | 
				
			||||||
 | 
					        self.vm.virt_mode = 'hvm'
 | 
				
			||||||
 | 
					        # HVM without qubes tools
 | 
				
			||||||
 | 
					        self.assertEqual(qubes.vm.qubesvm._default_maxmem(self.vm), 0)
 | 
				
			||||||
 | 
					        # just 'qrexec' feature
 | 
				
			||||||
 | 
					        self.vm.features['qrexec'] = True
 | 
				
			||||||
 | 
					        print(self.vm.features.check_with_template('qrexec', False))
 | 
				
			||||||
 | 
					        self.assertEqual(qubes.vm.qubesvm._default_maxmem(self.vm),
 | 
				
			||||||
 | 
					            default_maxmem)
 | 
				
			||||||
 | 
					        # some supported-service.*, but not meminfo-writer
 | 
				
			||||||
 | 
					        self.vm.features['supported-service.qubes-firewall'] = True
 | 
				
			||||||
 | 
					        self.assertEqual(qubes.vm.qubesvm._default_maxmem(self.vm), 0)
 | 
				
			||||||
 | 
					        # then add meminfo-writer
 | 
				
			||||||
 | 
					        self.vm.features['supported-service.meminfo-writer'] = True
 | 
				
			||||||
 | 
					        self.assertEqual(qubes.vm.qubesvm._default_maxmem(self.vm),
 | 
				
			||||||
 | 
					            default_maxmem)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_021_default_maxmem_with_pcidevs(self):
 | 
				
			||||||
 | 
					        self.vm.is_memory_balancing_possible = \
 | 
				
			||||||
 | 
					            lambda: qubes.vm.qubesvm.QubesVM.is_memory_balancing_possible(
 | 
				
			||||||
 | 
					                self.vm)
 | 
				
			||||||
 | 
					        self.vm.devices['pci'].persistent().append('00_00.0')
 | 
				
			||||||
 | 
					        self.assertEqual(qubes.vm.qubesvm._default_maxmem(self.vm), 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_022_default_maxmem_linux(self):
 | 
				
			||||||
 | 
					        self.vm.is_memory_balancing_possible = \
 | 
				
			||||||
 | 
					            lambda: qubes.vm.qubesvm.QubesVM.is_memory_balancing_possible(
 | 
				
			||||||
 | 
					                self.vm)
 | 
				
			||||||
 | 
					        self.vm.virt_mode = 'pvh'
 | 
				
			||||||
 | 
					        self.vm.memory = 400
 | 
				
			||||||
 | 
					        self.vm.features['os'] = 'Linux'
 | 
				
			||||||
 | 
					        self.assertEqual(qubes.vm.qubesvm._default_maxmem(self.vm), 2048)
 | 
				
			||||||
 | 
					        self.vm.memory = 100
 | 
				
			||||||
 | 
					        self.assertEqual(qubes.vm.qubesvm._default_maxmem(self.vm), 1000)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class QubesVMTestsMixin(object):
 | 
					class QubesVMTestsMixin(object):
 | 
				
			||||||
    property_no_default = object()
 | 
					    property_no_default = object()
 | 
				
			||||||
@ -696,7 +753,7 @@ class TC_90_QubesVM(QubesVMTestsMixin, qubes.tests.QubesTestCase):
 | 
				
			|||||||
        expected = '''<domain type="xen">
 | 
					        expected = '''<domain type="xen">
 | 
				
			||||||
        <name>test-inst-test</name>
 | 
					        <name>test-inst-test</name>
 | 
				
			||||||
        <uuid>7db78950-c467-4863-94d1-af59806384ea</uuid>
 | 
					        <uuid>7db78950-c467-4863-94d1-af59806384ea</uuid>
 | 
				
			||||||
        <memory unit="MiB">500</memory>
 | 
					        <memory unit="MiB">400</memory>
 | 
				
			||||||
        <currentMemory unit="MiB">400</currentMemory>
 | 
					        <currentMemory unit="MiB">400</currentMemory>
 | 
				
			||||||
        <vcpu placement="static">2</vcpu>
 | 
					        <vcpu placement="static">2</vcpu>
 | 
				
			||||||
        <cpu mode='host-passthrough'>
 | 
					        <cpu mode='host-passthrough'>
 | 
				
			||||||
@ -797,6 +854,7 @@ class TC_90_QubesVM(QubesVMTestsMixin, qubes.tests.QubesTestCase):
 | 
				
			|||||||
        vm = self.get_vm(uuid=my_uuid)
 | 
					        vm = self.get_vm(uuid=my_uuid)
 | 
				
			||||||
        vm.netvm = None
 | 
					        vm.netvm = None
 | 
				
			||||||
        vm.virt_mode = 'hvm'
 | 
					        vm.virt_mode = 'hvm'
 | 
				
			||||||
 | 
					        vm.features['qrexec'] = True
 | 
				
			||||||
        with unittest.mock.patch('qubes.config.qubes_base_dir',
 | 
					        with unittest.mock.patch('qubes.config.qubes_base_dir',
 | 
				
			||||||
                '/tmp/qubes-test'):
 | 
					                '/tmp/qubes-test'):
 | 
				
			||||||
            kernel_dir = '/tmp/qubes-test/vm-kernels/dummy'
 | 
					            kernel_dir = '/tmp/qubes-test/vm-kernels/dummy'
 | 
				
			||||||
@ -879,6 +937,155 @@ class TC_90_QubesVM(QubesVMTestsMixin, qubes.tests.QubesTestCase):
 | 
				
			|||||||
        self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
 | 
					        self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
 | 
				
			||||||
            lxml.etree.XML(expected))
 | 
					            lxml.etree.XML(expected))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_600_libvirt_xml_pvh_no_membalance(self):
 | 
				
			||||||
 | 
					        expected = '''<domain type="xen">
 | 
				
			||||||
 | 
					        <name>test-inst-test</name>
 | 
				
			||||||
 | 
					        <uuid>7db78950-c467-4863-94d1-af59806384ea</uuid>
 | 
				
			||||||
 | 
					        <memory unit="MiB">400</memory>
 | 
				
			||||||
 | 
					        <currentMemory unit="MiB">400</currentMemory>
 | 
				
			||||||
 | 
					        <vcpu placement="static">2</vcpu>
 | 
				
			||||||
 | 
					        <cpu mode='host-passthrough'>
 | 
				
			||||||
 | 
					            <!-- disable nested HVM -->
 | 
				
			||||||
 | 
					            <feature name='vmx' policy='disable'/>
 | 
				
			||||||
 | 
					            <feature name='svm' policy='disable'/>
 | 
				
			||||||
 | 
					            <!-- disable SMAP inside VM, because of Linux bug -->
 | 
				
			||||||
 | 
					            <feature name='smap' policy='disable'/>
 | 
				
			||||||
 | 
					        </cpu>
 | 
				
			||||||
 | 
					        <os>
 | 
				
			||||||
 | 
					            <type arch="x86_64" machine="xenfv">pvh</type>
 | 
				
			||||||
 | 
					            <kernel>/tmp/kernel/vmlinuz</kernel>
 | 
				
			||||||
 | 
					            <initrd>/tmp/kernel/initramfs</initrd>
 | 
				
			||||||
 | 
					            <cmdline>root=/dev/mapper/dmroot ro nomodeset console=hvc0 rd_NO_PLYMOUTH rd.plymouth.enable=0 plymouth.enable=0 nopat</cmdline>
 | 
				
			||||||
 | 
					        </os>
 | 
				
			||||||
 | 
					        <features>
 | 
				
			||||||
 | 
					            <pae/>
 | 
				
			||||||
 | 
					            <acpi/>
 | 
				
			||||||
 | 
					            <apic/>
 | 
				
			||||||
 | 
					            <viridian/>
 | 
				
			||||||
 | 
					        </features>
 | 
				
			||||||
 | 
					        <clock offset='utc' adjustment='reset'>
 | 
				
			||||||
 | 
					            <timer name="tsc" mode="native"/>
 | 
				
			||||||
 | 
					        </clock>
 | 
				
			||||||
 | 
					        <on_poweroff>destroy</on_poweroff>
 | 
				
			||||||
 | 
					        <on_reboot>destroy</on_reboot>
 | 
				
			||||||
 | 
					        <on_crash>destroy</on_crash>
 | 
				
			||||||
 | 
					        <devices>
 | 
				
			||||||
 | 
					            <disk type="block" device="disk">
 | 
				
			||||||
 | 
					                <driver name="phy" />
 | 
				
			||||||
 | 
					                <source dev="/tmp/kernel/modules.img" />
 | 
				
			||||||
 | 
					                <target dev="xvdd" />
 | 
				
			||||||
 | 
					                <backenddomain name="dom0" />
 | 
				
			||||||
 | 
					            </disk>
 | 
				
			||||||
 | 
					            <console type="pty">
 | 
				
			||||||
 | 
					                <target type="xen" port="0"/>
 | 
				
			||||||
 | 
					            </console>
 | 
				
			||||||
 | 
					        </devices>
 | 
				
			||||||
 | 
					        </domain>
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        my_uuid = '7db78950-c467-4863-94d1-af59806384ea'
 | 
				
			||||||
 | 
					        vm = self.get_vm(uuid=my_uuid)
 | 
				
			||||||
 | 
					        vm.netvm = None
 | 
				
			||||||
 | 
					        vm.virt_mode = 'pvh'
 | 
				
			||||||
 | 
					        vm.maxmem = 0
 | 
				
			||||||
 | 
					        with unittest.mock.patch('qubes.config.qubes_base_dir',
 | 
				
			||||||
 | 
					                '/tmp/qubes-test'):
 | 
				
			||||||
 | 
					            kernel_dir = '/tmp/qubes-test/vm-kernels/dummy'
 | 
				
			||||||
 | 
					            os.makedirs(kernel_dir, exist_ok=True)
 | 
				
			||||||
 | 
					            open(os.path.join(kernel_dir, 'vmlinuz'), 'w').close()
 | 
				
			||||||
 | 
					            open(os.path.join(kernel_dir, 'initramfs'), 'w').close()
 | 
				
			||||||
 | 
					            self.addCleanup(shutil.rmtree, '/tmp/qubes-test')
 | 
				
			||||||
 | 
					            vm.kernel = 'dummy'
 | 
				
			||||||
 | 
					        # tests for storage are later
 | 
				
			||||||
 | 
					        vm.volumes['kernel'] = unittest.mock.Mock(**{
 | 
				
			||||||
 | 
					            'kernels_dir': '/tmp/kernel',
 | 
				
			||||||
 | 
					            'block_device.return_value.domain': 'dom0',
 | 
				
			||||||
 | 
					            'block_device.return_value.script': None,
 | 
				
			||||||
 | 
					            'block_device.return_value.path': '/tmp/kernel/modules.img',
 | 
				
			||||||
 | 
					            'block_device.return_value.devtype': 'disk',
 | 
				
			||||||
 | 
					            'block_device.return_value.name': 'kernel',
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        libvirt_xml = vm.create_config_file()
 | 
				
			||||||
 | 
					        self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
 | 
				
			||||||
 | 
					            lxml.etree.XML(expected))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_600_libvirt_xml_hvm_pcidev(self):
 | 
				
			||||||
 | 
					        expected = '''<domain type="xen">
 | 
				
			||||||
 | 
					        <name>test-inst-test</name>
 | 
				
			||||||
 | 
					        <uuid>7db78950-c467-4863-94d1-af59806384ea</uuid>
 | 
				
			||||||
 | 
					        <memory unit="MiB">400</memory>
 | 
				
			||||||
 | 
					        <currentMemory unit="MiB">400</currentMemory>
 | 
				
			||||||
 | 
					        <vcpu placement="static">2</vcpu>
 | 
				
			||||||
 | 
					        <cpu mode='host-passthrough'>
 | 
				
			||||||
 | 
					            <!-- disable nested HVM -->
 | 
				
			||||||
 | 
					            <feature name='vmx' policy='disable'/>
 | 
				
			||||||
 | 
					            <feature name='svm' policy='disable'/>
 | 
				
			||||||
 | 
					            <!-- disable SMAP inside VM, because of Linux bug -->
 | 
				
			||||||
 | 
					            <feature name='smap' policy='disable'/>
 | 
				
			||||||
 | 
					        </cpu>
 | 
				
			||||||
 | 
					        <os>
 | 
				
			||||||
 | 
					            <type arch="x86_64" machine="xenfv">hvm</type>
 | 
				
			||||||
 | 
					                <!--
 | 
				
			||||||
 | 
					                     For the libxl backend libvirt switches between OVMF (UEFI)
 | 
				
			||||||
 | 
					                     and SeaBIOS based on the loader type. This has nothing to
 | 
				
			||||||
 | 
					                     do with the hvmloader binary.
 | 
				
			||||||
 | 
					                -->
 | 
				
			||||||
 | 
					            <loader type="rom">hvmloader</loader>
 | 
				
			||||||
 | 
					            <boot dev="cdrom" />
 | 
				
			||||||
 | 
					            <boot dev="hd" />
 | 
				
			||||||
 | 
					        </os>
 | 
				
			||||||
 | 
					        <features>
 | 
				
			||||||
 | 
					            <pae/>
 | 
				
			||||||
 | 
					            <acpi/>
 | 
				
			||||||
 | 
					            <apic/>
 | 
				
			||||||
 | 
					            <viridian/>
 | 
				
			||||||
 | 
					            <xen>
 | 
				
			||||||
 | 
					                <e820_host state="on"/>
 | 
				
			||||||
 | 
					            </xen>
 | 
				
			||||||
 | 
					        </features>
 | 
				
			||||||
 | 
					        <clock offset="variable" adjustment="0" basis="localtime" />
 | 
				
			||||||
 | 
					        <on_poweroff>destroy</on_poweroff>
 | 
				
			||||||
 | 
					        <on_reboot>destroy</on_reboot>
 | 
				
			||||||
 | 
					        <on_crash>destroy</on_crash>
 | 
				
			||||||
 | 
					        <devices>
 | 
				
			||||||
 | 
					            <hostdev type="pci" managed="yes">
 | 
				
			||||||
 | 
					                <source>
 | 
				
			||||||
 | 
					                    <address
 | 
				
			||||||
 | 
					                        bus="0x00"
 | 
				
			||||||
 | 
					                        slot="0x00"
 | 
				
			||||||
 | 
					                        function="0x0" />
 | 
				
			||||||
 | 
					                </source>
 | 
				
			||||||
 | 
					            </hostdev>
 | 
				
			||||||
 | 
					            <!-- server_ip is the address of stubdomain. It hosts it's own DNS server. -->
 | 
				
			||||||
 | 
					            <emulator type="stubdom-linux" />
 | 
				
			||||||
 | 
					            <input type="tablet" bus="usb"/>
 | 
				
			||||||
 | 
					            <video>
 | 
				
			||||||
 | 
					                <model type="vga"/>
 | 
				
			||||||
 | 
					            </video>
 | 
				
			||||||
 | 
					            <graphics type="qubes"/>
 | 
				
			||||||
 | 
					        </devices>
 | 
				
			||||||
 | 
					        </domain>
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        my_uuid = '7db78950-c467-4863-94d1-af59806384ea'
 | 
				
			||||||
 | 
					        # required for PCI devices listing
 | 
				
			||||||
 | 
					        self.app.vmm.offline_mode = False
 | 
				
			||||||
 | 
					        vm = self.get_vm(uuid=my_uuid)
 | 
				
			||||||
 | 
					        vm.netvm = None
 | 
				
			||||||
 | 
					        vm.virt_mode = 'hvm'
 | 
				
			||||||
 | 
					        vm.kernel = None
 | 
				
			||||||
 | 
					        # even with meminfo-writer enabled, should have memory==maxmem
 | 
				
			||||||
 | 
					        vm.features['service.meminfo-writer'] = True
 | 
				
			||||||
 | 
					        assignment = qubes.devices.DeviceAssignment(
 | 
				
			||||||
 | 
					            vm,  # this is violation of API, but for PCI the argument
 | 
				
			||||||
 | 
					            #  is unused
 | 
				
			||||||
 | 
					            '00_00.0',
 | 
				
			||||||
 | 
					            bus='pci',
 | 
				
			||||||
 | 
					            persistent=True)
 | 
				
			||||||
 | 
					        vm.devices['pci']._set.add(
 | 
				
			||||||
 | 
					            assignment)
 | 
				
			||||||
 | 
					        libvirt_xml = vm.create_config_file()
 | 
				
			||||||
 | 
					        self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
 | 
				
			||||||
 | 
					            lxml.etree.XML(expected))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_610_libvirt_xml_network(self):
 | 
					    def test_610_libvirt_xml_network(self):
 | 
				
			||||||
        expected = '''<domain type="xen">
 | 
					        expected = '''<domain type="xen">
 | 
				
			||||||
        <name>test-inst-test</name>
 | 
					        <name>test-inst-test</name>
 | 
				
			||||||
@ -937,6 +1144,7 @@ class TC_90_QubesVM(QubesVMTestsMixin, qubes.tests.QubesTestCase):
 | 
				
			|||||||
        vm = self.get_vm(uuid=my_uuid)
 | 
					        vm = self.get_vm(uuid=my_uuid)
 | 
				
			||||||
        vm.netvm = netvm
 | 
					        vm.netvm = netvm
 | 
				
			||||||
        vm.virt_mode = 'hvm'
 | 
					        vm.virt_mode = 'hvm'
 | 
				
			||||||
 | 
					        vm.features['qrexec'] = True
 | 
				
			||||||
        with self.subTest('ipv4_only'):
 | 
					        with self.subTest('ipv4_only'):
 | 
				
			||||||
            libvirt_xml = vm.create_config_file()
 | 
					            libvirt_xml = vm.create_config_file()
 | 
				
			||||||
            self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
 | 
					            self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
 | 
				
			||||||
@ -1000,6 +1208,7 @@ class TC_90_QubesVM(QubesVMTestsMixin, qubes.tests.QubesTestCase):
 | 
				
			|||||||
            '/qubes-iptables-error': '',
 | 
					            '/qubes-iptables-error': '',
 | 
				
			||||||
            '/qubes-iptables-header': iptables_header,
 | 
					            '/qubes-iptables-header': iptables_header,
 | 
				
			||||||
            '/qubes-service/qubes-update-check': '0',
 | 
					            '/qubes-service/qubes-update-check': '0',
 | 
				
			||||||
 | 
					            '/qubes-service/meminfo-writer': '1',
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @unittest.mock.patch('qubes.utils.get_timezone')
 | 
					    @unittest.mock.patch('qubes.utils.get_timezone')
 | 
				
			||||||
@ -1060,6 +1269,7 @@ class TC_90_QubesVM(QubesVMTestsMixin, qubes.tests.QubesTestCase):
 | 
				
			|||||||
            '/qubes-iptables-error': '',
 | 
					            '/qubes-iptables-error': '',
 | 
				
			||||||
            '/qubes-iptables-header': iptables_header,
 | 
					            '/qubes-iptables-header': iptables_header,
 | 
				
			||||||
            '/qubes-service/qubes-update-check': '0',
 | 
					            '/qubes-service/qubes-update-check': '0',
 | 
				
			||||||
 | 
					            '/qubes-service/meminfo-writer': '1',
 | 
				
			||||||
            '/qubes-ip': '10.137.0.3',
 | 
					            '/qubes-ip': '10.137.0.3',
 | 
				
			||||||
            '/qubes-netmask': '255.255.255.255',
 | 
					            '/qubes-netmask': '255.255.255.255',
 | 
				
			||||||
            '/qubes-gateway': '10.137.0.2',
 | 
					            '/qubes-gateway': '10.137.0.2',
 | 
				
			||||||
 | 
				
			|||||||
@ -69,13 +69,21 @@ def _setter_kernel(self, prop, value):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _setter_positive_int(self, prop, value):
 | 
					def _setter_positive_int(self, prop, value):
 | 
				
			||||||
    ''' Helper for setting a positive int. Checks that the int is >= 0 '''
 | 
					    ''' Helper for setting a positive int. Checks that the int is > 0 '''
 | 
				
			||||||
    # pylint: disable=unused-argument
 | 
					    # pylint: disable=unused-argument
 | 
				
			||||||
    value = int(value)
 | 
					    value = int(value)
 | 
				
			||||||
    if value <= 0:
 | 
					    if value <= 0:
 | 
				
			||||||
        raise ValueError('Value must be positive')
 | 
					        raise ValueError('Value must be positive')
 | 
				
			||||||
    return value
 | 
					    return value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _setter_non_negative_int(self, prop, value):
 | 
				
			||||||
 | 
					    ''' Helper for setting a positive int. Checks that the int is >= 0 '''
 | 
				
			||||||
 | 
					    # pylint: disable=unused-argument
 | 
				
			||||||
 | 
					    value = int(value)
 | 
				
			||||||
 | 
					    if value < 0:
 | 
				
			||||||
 | 
					        raise ValueError('Value must be positive or zero')
 | 
				
			||||||
 | 
					    return value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _setter_default_user(self, prop, value):
 | 
					def _setter_default_user(self, prop, value):
 | 
				
			||||||
    ''' Helper for setting default user '''
 | 
					    ''' Helper for setting default user '''
 | 
				
			||||||
@ -122,6 +130,25 @@ def _default_with_template(prop, default):
 | 
				
			|||||||
    return _func
 | 
					    return _func
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _default_maxmem(self):
 | 
				
			||||||
 | 
					    # first check for any reason to _not_ enable qmemman
 | 
				
			||||||
 | 
					    if not self.is_memory_balancing_possible():
 | 
				
			||||||
 | 
					        return 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Linux specific cap: max memory can't scale beyond 10.79*init_mem
 | 
				
			||||||
 | 
					    # see https://groups.google.com/forum/#!topic/qubes-devel/VRqkFj1IOtA
 | 
				
			||||||
 | 
					    if self.features.get('os', None) == 'Linux':
 | 
				
			||||||
 | 
					        default_maxmem = self.memory * 10
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        default_maxmem = 4000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # don't use default larger than half of physical ram
 | 
				
			||||||
 | 
					    default_maxmem = min(default_maxmem,
 | 
				
			||||||
 | 
					        int(self.app.host.memory_total / 1024 / 2))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return _default_with_template('maxmem', default_maxmem)(self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
 | 
					class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
 | 
				
			||||||
    '''Base functionality of Qubes VM shared between all VMs.
 | 
					    '''Base functionality of Qubes VM shared between all VMs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -457,12 +484,12 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
 | 
				
			|||||||
            'template\'s value by default.')
 | 
					            'template\'s value by default.')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    maxmem = qubes.property('maxmem', type=int,
 | 
					    maxmem = qubes.property('maxmem', type=int,
 | 
				
			||||||
        setter=_setter_positive_int,
 | 
					        setter=_setter_non_negative_int,
 | 
				
			||||||
        default=_default_with_template('maxmem', (lambda self:
 | 
					        default=_default_maxmem,
 | 
				
			||||||
            int(min(self.app.host.memory_total / 1024 / 2, 4000)))),
 | 
					 | 
				
			||||||
        doc='''Maximum amount of memory available for this VM (for the purpose
 | 
					        doc='''Maximum amount of memory available for this VM (for the purpose
 | 
				
			||||||
            of the memory balancer). TemplateBasedVMs use its '
 | 
					            of the memory balancer). Set to 0 to disable memory balancing for
 | 
				
			||||||
            'template\'s value by default.''')
 | 
					            this qube. TemplateBasedVMs use its template\'s value by default
 | 
				
			||||||
 | 
					            (unless memory balancing not supported for this qube).''')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    stubdom_mem = qubes.property('stubdom_mem', type=int,
 | 
					    stubdom_mem = qubes.property('stubdom_mem', type=int,
 | 
				
			||||||
        setter=_setter_positive_int,
 | 
					        setter=_setter_positive_int,
 | 
				
			||||||
@ -759,11 +786,6 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
 | 
				
			|||||||
            assert hasattr(self, 'qid')
 | 
					            assert hasattr(self, 'qid')
 | 
				
			||||||
            assert hasattr(self, 'name')
 | 
					            assert hasattr(self, 'name')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Linux specific cap: max memory can't scale beyond 10.79*init_mem
 | 
					 | 
				
			||||||
        # see https://groups.google.com/forum/#!topic/qubes-devel/VRqkFj1IOtA
 | 
					 | 
				
			||||||
        if self.maxmem > self.memory * 10:
 | 
					 | 
				
			||||||
            self.maxmem = self.memory * 10
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if xml is None:
 | 
					        if xml is None:
 | 
				
			||||||
            # new qube, disable updates check if requested for new qubes
 | 
					            # new qube, disable updates check if requested for new qubes
 | 
				
			||||||
            # SEE: 1637 when features are done, migrate to plugin
 | 
					            # SEE: 1637 when features are done, migrate to plugin
 | 
				
			||||||
@ -1367,6 +1389,34 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return stdouterr
 | 
					        return stdouterr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def is_memory_balancing_possible(self):
 | 
				
			||||||
 | 
					        '''Check if memory balancing can be enabled.
 | 
				
			||||||
 | 
					        Reasons to not enable it:
 | 
				
			||||||
 | 
					         - have PCI devices
 | 
				
			||||||
 | 
					         - balloon driver not present
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        We don't have reliable way to detect the second point, but good
 | 
				
			||||||
 | 
					        heuristic is HVM virt_mode (PV and PVH require OS support and it does
 | 
				
			||||||
 | 
					        include balloon driver) and lack of qrexec/meminfo-writer service
 | 
				
			||||||
 | 
					        support (no qubes tools installed).
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        if list(self.devices['pci'].persistent()):
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					        if self.virt_mode == 'hvm':
 | 
				
			||||||
 | 
					            # if VM announce any supported service
 | 
				
			||||||
 | 
					            features_set = set(self.features)
 | 
				
			||||||
 | 
					            template = getattr(self, 'template', None)
 | 
				
			||||||
 | 
					            while template is not None:
 | 
				
			||||||
 | 
					                features_set.update(template.features)
 | 
				
			||||||
 | 
					                template = getattr(template, 'template', None)
 | 
				
			||||||
 | 
					            supported_services = any(f.startswith('supported-service.')
 | 
				
			||||||
 | 
					                for f in features_set)
 | 
				
			||||||
 | 
					            if (not self.features.check_with_template('qrexec', False) or
 | 
				
			||||||
 | 
					                (supported_services and not self.features.check_with_template(
 | 
				
			||||||
 | 
					                    'supported-service.meminfo-writer', False))):
 | 
				
			||||||
 | 
					                return False
 | 
				
			||||||
 | 
					        return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def request_memory(self, mem_required=None):
 | 
					    def request_memory(self, mem_required=None):
 | 
				
			||||||
        # overhead of per-qube/per-vcpu Xen structures,
 | 
					        # overhead of per-qube/per-vcpu Xen structures,
 | 
				
			||||||
        # taken from OpenStack nova/virt/xenapi/driver.py
 | 
					        # taken from OpenStack nova/virt/xenapi/driver.py
 | 
				
			||||||
 | 
				
			|||||||
@ -2,11 +2,12 @@
 | 
				
			|||||||
    {% block basic %}
 | 
					    {% block basic %}
 | 
				
			||||||
        <name>{{ vm.name }}</name>
 | 
					        <name>{{ vm.name }}</name>
 | 
				
			||||||
        <uuid>{{ vm.uuid }}</uuid>
 | 
					        <uuid>{{ vm.uuid }}</uuid>
 | 
				
			||||||
        {% if vm.virt_mode == 'hvm' and vm.devices['pci'].persistent() | list %}
 | 
					        {% if ((vm.virt_mode == 'hvm' and vm.devices['pci'].persistent() | list)
 | 
				
			||||||
 | 
					            or vm.maxmem == 0) -%}
 | 
				
			||||||
            <memory unit="MiB">{{ vm.memory }}</memory>
 | 
					            <memory unit="MiB">{{ vm.memory }}</memory>
 | 
				
			||||||
        {% else %}
 | 
					        {% else -%}
 | 
				
			||||||
            <memory unit="MiB">{{ vm.maxmem }}</memory>
 | 
					            <memory unit="MiB">{{ vm.maxmem }}</memory>
 | 
				
			||||||
        {% endif %}
 | 
					        {% endif -%}
 | 
				
			||||||
        <currentMemory unit="MiB">{{ vm.memory }}</currentMemory>
 | 
					        <currentMemory unit="MiB">{{ vm.memory }}</currentMemory>
 | 
				
			||||||
        <vcpu placement="static">{{ vm.vcpus }}</vcpu>
 | 
					        <vcpu placement="static">{{ vm.vcpus }}</vcpu>
 | 
				
			||||||
    {% endblock %}
 | 
					    {% endblock %}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user