Add possibility to override libvirt config
This is the equivalent of "custom config" from R3.x. fixes QubesOS/qubes-issues#1798
This commit is contained in:
parent
cedd822735
commit
9dc37c1ee7
@ -26,6 +26,7 @@ manpages and API documentation. For primary user documentation, see
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
libvirt
|
||||
autoxml
|
||||
manpages/index
|
||||
|
||||
|
114
doc/libvirt.rst
Normal file
114
doc/libvirt.rst
Normal file
@ -0,0 +1,114 @@
|
||||
Custom libvirt config
|
||||
=====================
|
||||
|
||||
Starting from Qubes OS R4.0, libvirt domain config is generated using jinja
|
||||
templates. Those templates can be overridden by the user in a couple of ways.
|
||||
A basic knowledge of jinja template language and libvirt xml spec is needed.
|
||||
|
||||
.. seealso::
|
||||
|
||||
https://libvirt.org/formatdomain.html
|
||||
Format of the domain XML in libvirt.
|
||||
|
||||
http://jinja.pocoo.org/docs/dev/templates/
|
||||
Template format documentation.
|
||||
|
||||
File paths
|
||||
----------
|
||||
|
||||
In order of increasing precedence: the main template, from which the config is
|
||||
generated is :file:`/usr/share/templates/libvirt/xen.xml`).
|
||||
The distributor may put a file at
|
||||
:file:`/usr/share/qubes/template/xen-dist.xml`) to override this file.
|
||||
User may put a file at either
|
||||
:file:`/etc/qubes/templates/libvirt/xen-user.xml` or
|
||||
:file:`/etc/qubes/templates/libvirt/by-name/<name>.xml`, where ``<name>`` is
|
||||
full name of the domain. Wildcards are not supported but symlinks are.
|
||||
|
||||
Jinja has a concept of template names, which basically is the path below some
|
||||
load point, which in Qubes' case is :file:`/etc/qubes/templates` and
|
||||
:file:`/usr/share/qubes/templates`. Thus names of those templates are
|
||||
respectively ``'libvirt/xen.xml'``, ``'libvirt/xen-dist.xml'``,
|
||||
``'libvirt/xen-user.xml'`` and ``'libvirt/by-name/<name>.xml'``.
|
||||
This will be important later.
|
||||
|
||||
.. note::
|
||||
|
||||
Those who know jinja python API will know that the abovementioned locations
|
||||
aren't the only possibilities. Yes, it's a lie, but a justified one.
|
||||
|
||||
What to put in the template
|
||||
---------------------------
|
||||
|
||||
In principle the user may put anything in the template and there is no attempt
|
||||
to constrain the user from doing stupid things. One obvious thing is to copy the
|
||||
original config file and make changes.
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
<domain type="xen">
|
||||
<name>{{ vm.name }}</name>
|
||||
...
|
||||
|
||||
The better way is to inherit from the original template and override any number
|
||||
of blocks. This is the point when we need the name of the original template.
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% extends 'libvirt/xen.xml' %}
|
||||
{% block devices %}
|
||||
{{ super() }}
|
||||
<serial type='pty'>
|
||||
<target port='0'/>
|
||||
</serial>
|
||||
{% endblock %}
|
||||
|
||||
``{% extends %}`` specifies which template we inherit from. Then you may put any
|
||||
block by putting new content inside ``{% block %}{% endblock %}``.
|
||||
``{{ super() }}`` is substituted with original content of the block as specified
|
||||
in the parent template. Untouched blocks remain as they were.
|
||||
|
||||
The example above adds serial device.
|
||||
|
||||
Template API
|
||||
------------
|
||||
|
||||
.. warning::
|
||||
|
||||
This API is provisional and subject to change at the minor releases until
|
||||
further notice. No backwards compatibility is promised.
|
||||
|
||||
Globals
|
||||
```````
|
||||
vm
|
||||
the domain object (instance of subclass of
|
||||
:py:class:`qubes.vm.qubesvm.QubesVM`)
|
||||
|
||||
Filters
|
||||
```````
|
||||
|
||||
No custom filters at the moment.
|
||||
|
||||
Blocks in the default template
|
||||
``````````````````````````````
|
||||
basic
|
||||
Contains ``<name>``, ``<uuid>``, ``<memory>``, ``<currentMemory>`` and
|
||||
``<vcpu>`` nodes.
|
||||
|
||||
os
|
||||
Contents of ``<os>`` node.
|
||||
|
||||
features
|
||||
Contents of ``<features>`` node.
|
||||
|
||||
clock
|
||||
Contains the ``<clock>`` node.
|
||||
|
||||
on
|
||||
Contains ``<on_*>`` nodes.
|
||||
|
||||
devices
|
||||
Contents of ``<devices>`` node.
|
||||
|
||||
|
||||
.. vim: ts=3 sts=3 sw=3 et
|
@ -625,7 +625,10 @@ class Qubes(qubes.PropertyHolder):
|
||||
|
||||
#: jinja2 environment for libvirt XML templates
|
||||
self.env = jinja2.Environment(
|
||||
loader=jinja2.FileSystemLoader('/usr/share/qubes/templates'),
|
||||
loader=jinja2.FileSystemLoader([
|
||||
'/etc/qubes/templates',
|
||||
'/usr/share/qubes/templates',
|
||||
]),
|
||||
undefined=jinja2.StrictUndefined)
|
||||
|
||||
if load:
|
||||
|
@ -269,8 +269,12 @@ class BaseVM(qubes.PropertyHolder):
|
||||
:param bool prepare_dvm: If we are in the process of preparing \
|
||||
DisposableVM
|
||||
'''
|
||||
domain_config = self.app.env.get_template('libvirt/xen.xml').render(
|
||||
vm=self, prepare_dvm=prepare_dvm)
|
||||
domain_config = self.app.env.select_template([
|
||||
'libvirt/xen/by-name/{}.xml'.format(self.name),
|
||||
'libvirt/xen-user.xml',
|
||||
'libvirt/xen-dist.xml',
|
||||
'libvirt/xen.xml',
|
||||
]).render(vm=self, prepare_dvm=prepare_dvm)
|
||||
return domain_config
|
||||
|
||||
|
||||
|
@ -1,122 +1,134 @@
|
||||
<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>
|
||||
{% block basic %}
|
||||
<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>
|
||||
{% endblock %}
|
||||
<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 %}
|
||||
{% block 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 %}
|
||||
{% endblock %}
|
||||
</os>
|
||||
|
||||
<features>
|
||||
{% if vm.hvm %}
|
||||
<pae/>
|
||||
<acpi/>
|
||||
<apic/>
|
||||
<viridian/>
|
||||
{% endif %}
|
||||
{% block features %}
|
||||
{% if vm.hvm %}
|
||||
<pae/>
|
||||
<acpi/>
|
||||
<apic/>
|
||||
<viridian/>
|
||||
{% endif %}
|
||||
|
||||
{% if vm.devices['pci'].attached(persistent=True) | list
|
||||
and vm.features.get('pci-e820-host', True) %}
|
||||
<xen>
|
||||
<e820_host state="on"/>
|
||||
</xen>
|
||||
{% endif %}
|
||||
{% if vm.devices['pci'].attached(persistent=True) | list
|
||||
and vm.features.get('pci-e820-host', True) %}
|
||||
<xen>
|
||||
<e820_host state="on"/>
|
||||
</xen>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</features>
|
||||
|
||||
{% if vm.hvm %}
|
||||
{% set timezone = vm.features.check_with_template('timezone', 'localtime').lower() %}
|
||||
{% if timezone == 'localtime' %}
|
||||
<clock offset="variable" adjustment="0" basis="localtime" />
|
||||
{% elif timezone.isdigit() %}
|
||||
<clock offset="variable" adjustment="{{ timezone }}" basis="UTC" />
|
||||
{% else %}
|
||||
<clock offset="variable" adjustment="0" basis="UTC" />
|
||||
{% endif %}
|
||||
{% 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>
|
||||
{% set i = 0 %}
|
||||
{# TODO Allow more volumes out of the box #}
|
||||
{% set dd = ['e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
|
||||
'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y']
|
||||
%}
|
||||
{% for device in vm.block_devices %}
|
||||
<disk type="block" device="{{ device.devtype }}">
|
||||
<driver name="phy" />
|
||||
<source dev="{{ device.path }}" />
|
||||
{% if device.name == 'root' %}
|
||||
<target dev="xvda" />
|
||||
{% elif device.name == 'private' %}
|
||||
<target dev="xvdb" />
|
||||
{% elif device.name == 'volatile' %}
|
||||
<target dev="xvdc" />
|
||||
{% elif device.name == 'kernel' %}
|
||||
<target dev="xvdd" />
|
||||
{% else %}
|
||||
<target dev="xvd{{dd[i]}}" />
|
||||
{% set i = i + 1 %}
|
||||
{% endif %}
|
||||
|
||||
{% if not device.rw %}
|
||||
<readonly />
|
||||
{% endif %}
|
||||
|
||||
{% if device.domain %}
|
||||
<backenddomain name="{{ domain }}" />
|
||||
{% endif %}
|
||||
|
||||
{% if device.script %}
|
||||
<script path="{{ device.script }}" />
|
||||
{% endif %}
|
||||
</disk>
|
||||
{% endfor %}
|
||||
|
||||
{% if vm.netvm %}
|
||||
{% include 'libvirt/devices/net.xml' with context %}
|
||||
{% endif %}
|
||||
|
||||
{% for device in vm.devices.pci.attached(persistent=True) %}
|
||||
{% include 'libvirt/devices/pci.xml' %}
|
||||
{% endfor %}
|
||||
|
||||
{% block clock %}
|
||||
{% 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"/>
|
||||
{% set timezone = vm.features.check_with_template('timezone', 'localtime').lower() %}
|
||||
{% if timezone == 'localtime' %}
|
||||
<clock offset="variable" adjustment="0" basis="localtime" />
|
||||
{% elif timezone.isdigit() %}
|
||||
<clock offset="variable" adjustment="{{ timezone }}" basis="UTC" />
|
||||
{% else %}
|
||||
<clock offset="variable" adjustment="0" basis="UTC" />
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<console type="pty">
|
||||
<target type="xen" port="0"/>
|
||||
</console>
|
||||
<clock offset='utc' adjustment='reset'>
|
||||
<timer name="tsc" mode="native"/>
|
||||
</clock>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block on %}
|
||||
<on_poweroff>destroy</on_poweroff>
|
||||
<on_reboot>destroy</on_reboot>
|
||||
<on_crash>destroy</on_crash>
|
||||
{% endblock %}
|
||||
|
||||
<devices>
|
||||
{% block devices %}
|
||||
{% set i = 0 %}
|
||||
{# TODO Allow more volumes out of the box #}
|
||||
{% set dd = ['e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
|
||||
'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y']
|
||||
%}
|
||||
{% for device in vm.block_devices %}
|
||||
<disk type="block" device="{{ device.devtype }}">
|
||||
<driver name="phy" />
|
||||
<source dev="{{ device.path }}" />
|
||||
{% if device.name == 'root' %}
|
||||
<target dev="xvda" />
|
||||
{% elif device.name == 'private' %}
|
||||
<target dev="xvdb" />
|
||||
{% elif device.name == 'volatile' %}
|
||||
<target dev="xvdc" />
|
||||
{% elif device.name == 'kernel' %}
|
||||
<target dev="xvdd" />
|
||||
{% else %}
|
||||
<target dev="xvd{{dd[i]}}" />
|
||||
{% set i = i + 1 %}
|
||||
{% endif %}
|
||||
|
||||
{% if not device.rw %}
|
||||
<readonly />
|
||||
{% endif %}
|
||||
|
||||
{% if device.domain %}
|
||||
<backenddomain name="{{ domain }}" />
|
||||
{% endif %}
|
||||
|
||||
{% if device.script %}
|
||||
<script path="{{ device.script }}" />
|
||||
{% endif %}
|
||||
</disk>
|
||||
{% endfor %}
|
||||
|
||||
{% if vm.netvm %}
|
||||
{% include 'libvirt/devices/net.xml' with context %}
|
||||
{% endif %}
|
||||
|
||||
{% for device in vm.devices.pci.attached(persistent=True) %}
|
||||
{% include 'libvirt/devices/pci.xml' %}
|
||||
{% 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 %}
|
||||
{% endblock %}
|
||||
</devices>
|
||||
</domain>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user