Browse Source

HVM part 1

Wojtek Porczyk 8 years ago
parent
commit
5eaf03c4a2

+ 2 - 2
Makefile

@@ -80,8 +80,8 @@ endif
 	cp qubes-rpc/qubes-notify-tools $(DESTDIR)/usr/libexec/qubes/
 
 	mkdir -p "$(DESTDIR)$(FILESDIR)"
-	cp vm-config/$(BACKEND_VMM)-vm-template.xml "$(DESTDIR)$(FILESDIR)/vm-template.xml"
-	cp vm-config/$(BACKEND_VMM)-vm-template-hvm.xml "$(DESTDIR)$(FILESDIR)/vm-template-hvm.xml"
+	cp -r templates "$(DESTDIR)$(FILESDIR)/templates"
+	rm -f "$(DESTDIR)$(FILESDIR)/templates/README"
 
 	mkdir -p $(DESTDIR)$(DATADIR)
 	mkdir -p $(DESTDIR)$(DATADIR)/vm-templates

+ 6 - 0
qubes/__init__.py

@@ -53,6 +53,7 @@ import __builtin__
 
 import docutils.core
 import docutils.io
+import jinja2
 import lxml.etree
 import pkg_resources
 
@@ -1199,6 +1200,11 @@ class Qubes(PropertyHolder):
 
         self.__load_timestamp = None
 
+        #: jinja2 environment for libvirt XML templates
+        self.env = jinja2.Environment(
+            loader=jinja2.FileSystemLoader('/usr/share/qubes/templates'),
+            undefined=jinja2.StrictUndefined)
+
         if load:
             self.load()
 

+ 115 - 0
qubes/devices.py

@@ -0,0 +1,115 @@
+#!/usr/bin/python2 -O
+# vim: fileencoding=utf-8
+
+#
+# The Qubes OS Project, https://www.qubes-os.org/
+#
+# Copyright (C) 2010-2016  Joanna Rutkowska <joanna@invisiblethingslab.com>
+# Copyright (C) 2015-2016  Wojtek Porczyk <woju@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+import re
+
+class DeviceCollection(object):
+    '''Bag for devices.
+
+    Used as default value for :py:meth:`DeviceManager.__missing__` factory.
+
+    :param vm: VM for which we manage devices
+    :param class_: device class
+    '''
+
+    def __init__(self, vm, class_):
+        self._vm = vm
+        self._class = class_
+        self._set = set()
+
+
+    def attach(self, device):
+        '''Attach (add) device to domain.
+
+        :param str device: device identifier (format is class-dependent)
+        '''
+
+        if device in self:
+            raise KeyError(
+                'device {!r} of class {} already attached to {!r}'.format(
+                    device, self._class, self._vm))
+        self._vm.fire_event_pre('device-pre-attach:{}'.format(self._class),
+            device)
+        self._set.add(device)
+        self._vm.fire_event('device-attach:{}'.format(self._class), device)
+
+
+    def detach(self, device):
+        '''Detach (remove) device from domain.
+
+        :param str device: device identifier (format is class-dependent)
+        '''
+
+        if device not in self:
+            raise KeyError(
+                'device {!r} of class {} not attached to {!r}'.format(
+                    device, self._class, self._vm))
+        self._vm.fire_event_pre('device-pre-detach:{}'.format(self._class),
+            device)
+        self._set.remove(device)
+        self._vm.fire_event('device-detach:{}'.format(self._class), device)
+
+
+    def __iter__(self):
+        return iter(self._set)
+
+
+    def __contains__(self, item):
+        return item in self._set
+
+
+    def __len__(self):
+        return len(self._set)
+
+
+class DeviceManager(dict):
+    '''Device manager that hold all devices by their classess.
+
+    :param vm: VM for which we manage devices
+    '''
+
+    def __init__(self, vm):
+        super(DeviceManager, self).__init__()
+        self._vm = vm
+
+    def __missing__(self, key):
+        self[key] = DeviceCollection(self._vm, key)
+        return self[key]
+
+
+class RegexDevice(str):
+    def __init__(self, *args, **kwargs):
+        super(RegexDevice, self).__init__(*args, **kwargs)
+
+        dev_match = self.regex.match(self)
+        if not dev_match:
+            raise ValueError('Invalid device identifier: {!r}'.format(self))
+
+        for group in self.regex.groupindex:
+            setattr(self, group, dev_match.group(group))
+
+
+class PCIDevice(RegexDevice):
+    regex = re.compile(
+        r'^(?P<bus>[0-9a-f]+):(?P<device>[0-9a-f]+)\.(?P<function>[0-9a-f]+)$')

+ 1 - 2
qubes/tools/qvm_ls.py

@@ -291,7 +291,6 @@ class StatusColumn(Column):
         import qubes.vm.adminvm
         import qubes.vm.appvm
         import qubes.vm.dispvm
-        import qubes.vm.hvm
         import qubes.vm.qubesvm
         import qubes.vm.templatevm
 
@@ -310,7 +309,7 @@ class StatusColumn(Column):
             ret = 'd'
 
         if ret is not None:
-            if isinstance(vm, qubes.vm.hvm.HVM):
+            if getattr(vm, 'hvm', False):
                 return ret.upper()
             else:
                 return ret

+ 4 - 82
qubes/vm/__init__.py

@@ -43,6 +43,7 @@ import lxml.etree
 
 import qubes
 import qubes.log
+import qubes.devices
 import qubes.events
 import qubes.tools.qvm_ls
 
@@ -145,80 +146,6 @@ class BaseVMMeta(qubes.events.EmitterMeta):
         qubes.tools.qvm_ls.process_class(cls)
 
 
-class DeviceCollection(object):
-    '''Bag for devices.
-
-    Used as default value for :py:meth:`DeviceManager.__missing__` factory.
-
-    :param vm: VM for which we manage devices
-    :param class_: device class
-    '''
-
-    def __init__(self, vm, class_):
-        self._vm = vm
-        self._class = class_
-        self._set = set()
-
-
-    def attach(self, device):
-        '''Attach (add) device to domain.
-
-        :param str device: device identifier (format is class-dependent)
-        '''
-
-        if device in self:
-            raise KeyError(
-                'device {!r} of class {} already attached to {!r}'.format(
-                    device, self._class, self._vm))
-        self._vm.fire_event_pre('device-pre-attached:{}'.format(self._class),
-            device)
-        self._set.add(device)
-        self._vm.fire_event('device-attached:{}'.format(self._class), device)
-
-
-    def detach(self, device):
-        '''Detach (remove) device from domain.
-
-        :param str device: device identifier (format is class-dependent)
-        '''
-
-        if device not in self:
-            raise KeyError(
-                'device {!r} of class {} not attached to {!r}'.format(
-                    device, self._class, self._vm))
-        self._vm.fire_event_pre('device-pre-detached:{}'.format(self._class),
-            device)
-        self._set.remove(device)
-        self._vm.fire_event('device-detached:{}'.format(self._class), device)
-
-
-    def __iter__(self):
-        return iter(self._set)
-
-
-    def __contains__(self, item):
-        return item in self._set
-
-
-    def __len__(self):
-        return len(self._set)
-
-
-class DeviceManager(dict):
-    '''Device manager that hold all devices by their classess.
-
-    :param vm: VM for which we manage devices
-    '''
-
-    def __init__(self, vm):
-        super(DeviceManager, self).__init__()
-        self._vm = vm
-
-    def __missing__(self, key):
-        self[key] = DeviceCollection(self._vm, key)
-        return self[key]
-
-
 class BaseVM(qubes.PropertyHolder):
     '''Base class for all VMs
 
@@ -251,7 +178,7 @@ class BaseVM(qubes.PropertyHolder):
 
         #: :py:class:`DeviceManager` object keeping devices that are attached to
         #: this domain
-        self.devices = DeviceManager(self) if devices is None else devices
+        self.devices = devices or qubes.devices.DeviceManager(self)
 
         #: user-specified tags
         self.tags = tags or {}
@@ -480,13 +407,8 @@ class BaseVM(qubes.PropertyHolder):
         conf_template = f_conf_template.read()
         f_conf_template.close()
 
-        template_params = self.get_config_params()
-        if prepare_dvm:
-            template_params['name'] = '%NAME%'
-            template_params['privatedev'] = ''
-            template_params['netdev'] = re.sub(r"address='[0-9.]*'",
-                "address='%IP%'", template_params['netdev'])
-        domain_config = conf_template.format(**template_params)
+        domain_config = self.app.env.get_template('libvirt/xen.xml').render(
+            vm=self, prepare_dvm=prepare_dvm)
 
         # FIXME: This is only for debugging purposes
         old_umask = os.umask(002)

+ 0 - 9
qubes/vm/hvm.py

@@ -1,9 +0,0 @@
-#!/usr/bin/python2 -O
-# vim: fileencoding=utf-8
-
-import qubes.vm.qubesvm
-
-class HVM(qubes.vm.qubesvm.QubesVM):
-    '''HVM'''
-    def __init__(self, D):
-        super(HVM, self).__init__(D)

+ 6 - 0
qubes/vm/qubesvm.py

@@ -166,6 +166,12 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
         ls_width=36,
         doc='UUID from libvirt.')
 
+    hvm = qubes.property('hvm',
+        type=bool, setter=qubes.property.bool,
+        default=False,
+        doc='''Use full virtualisation (HVM) for this qube,
+            instead of paravirtualisation (PV)''')
+
     # XXX this should be part of qubes.xml
     firewall_conf = qubes.property('firewall_conf', type=str,
         default='firewall.xml')

+ 0 - 9
qubes/vm/templatehvm.py

@@ -1,9 +0,0 @@
-#!/usr/bin/python2 -O
-# vim: fileencoding=utf-8
-
-import qubes.vm.hvm
-
-class TemplateHVM(qubes.vm.hvm.HVM):
-    '''Template for HVM'''
-    def __init__(self, D):
-        super(TemplateHVM, self).__init__(D)

+ 2 - 4
rpm_spec/core-dom0.spec

@@ -202,6 +202,7 @@ fi
 %dir %{python_sitelib}/qubes
 %{python_sitelib}/qubes/__init__.py*
 %{python_sitelib}/qubes/config.py*
+%{python_sitelib}/qubes/devices.py*
 %{python_sitelib}/qubes/dochelpers.py*
 %{python_sitelib}/qubes/events.py*
 %{python_sitelib}/qubes/exc.py*
@@ -214,9 +215,7 @@ fi
 %{python_sitelib}/qubes/vm/adminvm.py*
 %{python_sitelib}/qubes/vm/appvm.py*
 %{python_sitelib}/qubes/vm/dispvm.py*
-%{python_sitelib}/qubes/vm/hvm.py*
 %{python_sitelib}/qubes/vm/qubesvm.py*
-%{python_sitelib}/qubes/vm/templatehvm.py*
 %{python_sitelib}/qubes/vm/templatevm.py*
 
 %dir %{python_sitelib}/qubes/vm/mix
@@ -314,8 +313,7 @@ fi
 %attr(2770,root,qubes) %dir /var/lib/qubes/backup
 %attr(2770,root,qubes) %dir /var/lib/qubes/dvmdata
 %attr(2770,root,qubes) %dir /var/lib/qubes/vm-kernels
-/usr/share/qubes/vm-template.xml
-/usr/share/qubes/vm-template-hvm.xml
+/usr/share/qubes/templates/libvirt/xen.xml
 /usr/lib/tmpfiles.d/qubes.conf
 /usr/lib/qubes/qubes-prepare-saved-domain.sh
 /usr/lib/qubes/qubes-update-dispvm-savefile-with-progress.sh

+ 4 - 0
templates/README

@@ -0,0 +1,4 @@
+Here were files with config for WNI, available in repo's history.
+
+vm-config/wni-vm-template-hvm.xml
+vm-config/wni-vm-template.xml

+ 121 - 0
templates/libvirt/xen.xml

@@ -0,0 +1,121 @@
+<domain type="xen">
+<name>{% if prepare_dvm %}%NAME%{% else %}{{ vm.name }}{% endif %}</name>
+	<uuid>{{ vm.uuid }}</uuid>
+	<memory unit="MiB">{{ vm.maxmem }}</memory>
+	<currentMemory unit="MiB">{{ vm.memory }}</currentMemory>
+	<vcpu placement="static">{{ vm.vcpus }}</vcpu>
+	<os>
+	{% if vm.hvm %}
+		<type arch="x86_64" machine="xenfv">hvm</type>
+		<loader>hvmloader</loader>
+		<boot dev="cdrom" />
+		<boot dev="hd" />
+<!-- server_ip is the address of stubdomain. It hosts it's own DNS server. -->
+	{% else %}
+		<type arch="x86_64" machine="xenpv">linux</type>
+		<kernel>{{ vm.storage.kernels_dir }}/vmlinuz</kernel>
+		<initrd>{{ vm.storage.kernels_dir }}/initramfs</initrd>
+		<cmdline>root=/dev/mapper/dmroot ro nomodeset console=hvc0 rd_NO_PLYMOUTH 3 {{ vm.kernelopts }}</cmdline>
+	{% endif %}
+	</os>
+
+	{% if vm.hvm %}
+		<features>
+			<pae/>
+			<acpi/>
+			<apic/>
+			<viridian/>
+		</features>
+
+        {# TODO
+        <clock offset="variable"
+            adjustment='{timeoffset}' basis='{time_basis}'/>
+        #}
+	{% else %}
+		<clock offset='utc' adjustment='reset'>
+			<timer name="tsc" mode="native"/>
+		</clock>
+	{% endif %}
+
+	<on_poweroff>destroy</on_poweroff>
+	<on_reboot>destroy</on_reboot>
+	<on_crash>destroy</on_crash>
+	<devices>
+        {#
+		{% for device in vm.storage %}
+            <disk type="block" device="{{ device.type }}">
+                <driver name="phy" />
+                <source dev="{{ device.path }}" />
+                <target dev="{{ device.vdev }}" />
+
+                {% if not device.rw %}
+                    <readonly />
+                {% endif %}
+
+                {% if device.domain %}
+                    <domain name="{{ domain }}" />
+                {% endif %}
+
+                {% if device.script %}
+                    <script path="{{ device.script }}"></script>
+                {% endif %}
+            </disk>
+		{% endfor %}
+        #}
+
+        {{ vm.storage.root_dev_config() }}
+        {% if not prepare_dvm %}{{ vm.storage.private_dev_config() }}{% endif %}
+        {{ vm.storage.other_dev_config() }}
+
+		{% if not vm.hvm %}
+            {{ vm.storage.volatile_dev_config() }}
+		{% endif %}
+
+		{% if vm.netvm %}
+			<interface type="ethernet">
+				<mac address="{{ vm.mac }}" />
+                <ip address="
+                    {%- if prepare_dvm -%}
+                        %IP%
+                    {%- else -%}
+                        {{ vm.ip }}
+                    {%- endif %}" />
+				<backenddomain name="{{ vm.netvm.name }}" />
+                <script path="vif-route-qubes"></script>
+			</interface>
+		{% endif %}
+
+		{% for device in vm.devices.pci %}
+            <hostdev type="pci" managed="yes">
+                <source>
+                    <address
+                        bus="0x{{ device.bus }}"
+                        slot="0x{{ device.device }}"
+                        function="0x{{ device.function }}" />
+                </source>
+            </hostdev>
+		{% endfor %}
+
+		{% if vm.hvm %}
+            <emulator
+                type="stubdom"
+                {% if vm.netvm %}
+                    cmdline="-net lwip,client_ip={{ vm.ip -}}
+                        ,server_ip={{ vm.secondary_dns -}}
+                        ,dns={{ vm.netvm.gateway -}}
+                        ,gw={{ self.netvm.gateway -}}
+                        ,netmask={{ vm.netmask }}"
+                {% endif %}
+                />
+			<input type="tablet" bus="usb"/>
+			<video type="vga"/>
+		{% else %}
+			<console type="pty">
+				<target type="xen" port="0"/>
+			</console>
+		{% endif %}
+
+	</devices>
+</domain>
+ 
+<!-- vim: set ft=jinja ts=4 sts=4 sw=4 et tw=80 : -->

+ 0 - 9
vm-config/wni-vm-template-hvm.xml

@@ -1,9 +0,0 @@
-<domain type='test'>
-  <name>{name}</name>
-  <uuid>{uuid}</uuid>
-  <memory unit='MiB'>{maxmem}</memory>
-  <os>
-    <type>hvm</type>
-  </os>
-</domain>
-

+ 0 - 9
vm-config/wni-vm-template.xml

@@ -1,9 +0,0 @@
-<domain type='test'>
-  <name>{name}</name>
-  <uuid>{uuid}</uuid>
-  <memory unit='MiB'>{maxmem}</memory>
-  <os>
-    <type>pv</type>
-  </os>
-</domain>
-

+ 0 - 38
vm-config/xen-vm-template-hvm.xml

@@ -1,38 +0,0 @@
-<domain type='xen'>
-  <name>{name}</name>
-  <uuid>{uuid}</uuid>
-  <memory unit='MiB'>{maxmem}</memory>
-  <currentMemory unit='MiB'>{mem}</currentMemory>
-  <vcpu placement='static'>{vcpus}</vcpu>
-  <os>
-    <type arch='x86_64' machine='xenfv'>hvm</type>
-    <loader>hvmloader</loader>
-    <boot dev='cdrom'/>
-    <boot dev='hd'/>
-  </os>
-  <features>
-    <pae/>
-    <acpi/>
-    <apic/>
-    <viridian/>
-  </features>
-  <clock offset='variable' adjustment='{timeoffset}' basis='{time_basis}'/>
-  <on_poweroff>destroy</on_poweroff>
-  <on_reboot>destroy</on_reboot>
-  <on_crash>destroy</on_crash>
-  <devices>
-    {no_network_begin}<emulator type='stubdom'/>{no_network_end}
-<!-- server_ip is the address of stubdomain. It hosts it's own DNS server. -->
-    {network_begin}<emulator type='stubdom' cmdline='-net lwip,client_ip={ip},server_ip={dns2},dns={dns1},gw={gateway},netmask={netmask}'/>{network_end}
-{rootdev}
-{privatedev}
-{otherdevs}
-{netdev}
-{pcidevs}
-    <input type='tablet' bus='usb'/>
-    <video type='vga'>
-      <model type='xen' vram='16384'/>
-    </video>
-  </devices>
-</domain>
-

+ 0 - 31
vm-config/xen-vm-template.xml

@@ -1,31 +0,0 @@
-<domain type='xen'>
-  <name>{name}</name>
-  <uuid>{uuid}</uuid>
-  <memory unit='MiB'>{maxmem}</memory>
-  <currentMemory unit='MiB'>{mem}</currentMemory>
-  <vcpu placement='static'>{vcpus}</vcpu>
-  <os>
-    <type arch='x86_64' machine='xenpv'>linux</type>
-    <kernel>{kerneldir}/vmlinuz</kernel>
-    <initrd>{kerneldir}/initramfs</initrd>
-    <cmdline>root=/dev/mapper/dmroot ro nomodeset console=hvc0 rd_NO_PLYMOUTH 3 {kernelopts}</cmdline>
-  </os>
-  <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>
-{rootdev}
-{privatedev}
-{volatiledev}
-{otherdevs}
-{netdev}
-{pcidevs}
-    <console type='pty'>
-      <target type='xen' port='0'/>
-    </console>
-  </devices>
-</domain>
-