diff --git a/Makefile b/Makefile index 72136dac..38915e6c 100644 --- a/Makefile +++ b/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 diff --git a/qubes/__init__.py b/qubes/__init__.py index 1485c4e4..3c4c292e 100644 --- a/qubes/__init__.py +++ b/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() diff --git a/qubes/devices.py b/qubes/devices.py new file mode 100644 index 00000000..f384dc19 --- /dev/null +++ b/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 +# Copyright (C) 2015-2016 Wojtek Porczyk +# +# 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[0-9a-f]+):(?P[0-9a-f]+)\.(?P[0-9a-f]+)$') diff --git a/qubes/tools/qvm_ls.py b/qubes/tools/qvm_ls.py index 3749d800..f98cd7f1 100644 --- a/qubes/tools/qvm_ls.py +++ b/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 diff --git a/qubes/vm/__init__.py b/qubes/vm/__init__.py index 9b747da2..16bbe7d5 100644 --- a/qubes/vm/__init__.py +++ b/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) diff --git a/qubes/vm/hvm.py b/qubes/vm/hvm.py deleted file mode 100644 index d7954daf..00000000 --- a/qubes/vm/hvm.py +++ /dev/null @@ -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) diff --git a/qubes/vm/qubesvm.py b/qubes/vm/qubesvm.py index a38fc5bb..9362bc0a 100644 --- a/qubes/vm/qubesvm.py +++ b/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') diff --git a/qubes/vm/templatehvm.py b/qubes/vm/templatehvm.py deleted file mode 100644 index 069d02ea..00000000 --- a/qubes/vm/templatehvm.py +++ /dev/null @@ -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) diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index 1b05dca8..75bf4c1f 100644 --- a/rpm_spec/core-dom0.spec +++ b/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 diff --git a/templates/README b/templates/README new file mode 100644 index 00000000..a5d306c5 --- /dev/null +++ b/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 diff --git a/templates/libvirt/xen.xml b/templates/libvirt/xen.xml new file mode 100644 index 00000000..3d6ceeff --- /dev/null +++ b/templates/libvirt/xen.xml @@ -0,0 +1,121 @@ + +{% if prepare_dvm %}%NAME%{% else %}{{ vm.name }}{% endif %} + {{ vm.uuid }} + {{ vm.maxmem }} + {{ vm.memory }} + {{ vm.vcpus }} + + {% if vm.hvm %} + hvm + hvmloader + + + + {% else %} + linux + {{ vm.storage.kernels_dir }}/vmlinuz + {{ vm.storage.kernels_dir }}/initramfs + root=/dev/mapper/dmroot ro nomodeset console=hvc0 rd_NO_PLYMOUTH 3 {{ vm.kernelopts }} + {% endif %} + + + {% if vm.hvm %} + + + + + + + + {# TODO + + #} + {% else %} + + + + {% endif %} + + destroy + destroy + destroy + + {# + {% for device in vm.storage %} + + + + + + {% if not device.rw %} + + {% endif %} + + {% if device.domain %} + + {% endif %} + + {% if device.script %} + + {% endif %} + + {% 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 %} + + + + + + + {% endif %} + + {% for device in vm.devices.pci %} + + +
+ + + {% endfor %} + + {% if vm.hvm %} + + +