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:: | .. toctree:: | ||||||
|    :maxdepth: 1 |    :maxdepth: 1 | ||||||
| 
 | 
 | ||||||
|  |    libvirt | ||||||
|    autoxml |    autoxml | ||||||
|    manpages/index |    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 |         #: jinja2 environment for libvirt XML templates | ||||||
|         self.env = jinja2.Environment( |         self.env = jinja2.Environment( | ||||||
|             loader=jinja2.FileSystemLoader('/usr/share/qubes/templates'), |             loader=jinja2.FileSystemLoader([ | ||||||
|  |                 '/etc/qubes/templates', | ||||||
|  |                 '/usr/share/qubes/templates', | ||||||
|  |             ]), | ||||||
|             undefined=jinja2.StrictUndefined) |             undefined=jinja2.StrictUndefined) | ||||||
| 
 | 
 | ||||||
|         if load: |         if load: | ||||||
|  | |||||||
| @ -269,8 +269,12 @@ class BaseVM(qubes.PropertyHolder): | |||||||
|         :param bool prepare_dvm: If we are in the process of preparing \ |         :param bool prepare_dvm: If we are in the process of preparing \ | ||||||
|             DisposableVM |             DisposableVM | ||||||
|         ''' |         ''' | ||||||
|         domain_config = self.app.env.get_template('libvirt/xen.xml').render( |         domain_config = self.app.env.select_template([ | ||||||
|             vm=self, prepare_dvm=prepare_dvm) |                 '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 |         return domain_config | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,122 +1,134 @@ | |||||||
| <domain type="xen"> | <domain type="xen"> | ||||||
| <name>{% if prepare_dvm %}%NAME%{% else %}{{ vm.name }}{% endif %}</name> |     {% block basic %} | ||||||
|     <uuid>{{ vm.uuid }}</uuid> |         <name>{% if prepare_dvm %}%NAME%{% else %}{{ vm.name }}{% endif %}</name> | ||||||
|     <memory unit="MiB">{{ vm.maxmem }}</memory> |         <uuid>{{ vm.uuid }}</uuid> | ||||||
|     <currentMemory unit="MiB">{{ vm.memory }}</currentMemory> |         <memory unit="MiB">{{ vm.maxmem }}</memory> | ||||||
|     <vcpu placement="static">{{ vm.vcpus }}</vcpu> |         <currentMemory unit="MiB">{{ vm.memory }}</currentMemory> | ||||||
|  |         <vcpu placement="static">{{ vm.vcpus }}</vcpu> | ||||||
|  |     {% endblock %} | ||||||
|     <os> |     <os> | ||||||
|     {% if vm.hvm %} |         {% block os %} | ||||||
|         <type arch="x86_64" machine="xenfv">hvm</type> |             {% if vm.hvm %} | ||||||
|         <loader>hvmloader</loader> |                 <type arch="x86_64" machine="xenfv">hvm</type> | ||||||
|         <boot dev="cdrom" /> |                 <loader>hvmloader</loader> | ||||||
|         <boot dev="hd" /> |                 <boot dev="cdrom" /> | ||||||
| <!-- server_ip is the address of stubdomain. It hosts it's own DNS server. --> |                 <boot dev="hd" /> | ||||||
|     {% else %} |             <!-- server_ip is the address of stubdomain. It hosts it's own DNS server. --> | ||||||
|         <type arch="x86_64" machine="xenpv">linux</type> |             {% else %} | ||||||
|         <kernel>{{ vm.storage.kernels_dir }}/vmlinuz</kernel> |                 <type arch="x86_64" machine="xenpv">linux</type> | ||||||
|         <initrd>{{ vm.storage.kernels_dir }}/initramfs</initrd> |                 <kernel>{{ vm.storage.kernels_dir }}/vmlinuz</kernel> | ||||||
|         <cmdline>root=/dev/mapper/dmroot ro nomodeset console=hvc0 rd_NO_PLYMOUTH 3 {{ vm.kernelopts }}</cmdline> |                 <initrd>{{ vm.storage.kernels_dir }}/initramfs</initrd> | ||||||
|     {% endif %} |                 <cmdline>root=/dev/mapper/dmroot ro nomodeset console=hvc0 rd_NO_PLYMOUTH 3 {{ vm.kernelopts }}</cmdline> | ||||||
|  |             {% endif %} | ||||||
|  |         {% endblock %} | ||||||
|     </os> |     </os> | ||||||
| 
 | 
 | ||||||
|     <features> |     <features> | ||||||
|         {% if vm.hvm %} |         {% block features %} | ||||||
|             <pae/> |             {% if vm.hvm %} | ||||||
|             <acpi/> |                 <pae/> | ||||||
|             <apic/> |                 <acpi/> | ||||||
|             <viridian/> |                 <apic/> | ||||||
|         {% endif %} |                 <viridian/> | ||||||
|  |             {% endif %} | ||||||
| 
 | 
 | ||||||
|         {% if vm.devices['pci'].attached(persistent=True) | list |             {% if vm.devices['pci'].attached(persistent=True) | list | ||||||
|                 and vm.features.get('pci-e820-host', True) %} |                     and vm.features.get('pci-e820-host', True) %} | ||||||
|             <xen> |                 <xen> | ||||||
|                 <e820_host state="on"/> |                     <e820_host state="on"/> | ||||||
|             </xen> |                 </xen> | ||||||
|         {% endif %} |             {% endif %} | ||||||
|  |         {% endblock %} | ||||||
|     </features> |     </features> | ||||||
| 
 | 
 | ||||||
|     {% if vm.hvm %} |     {% block clock %} | ||||||
|         {% 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 %} |  | ||||||
| 
 |  | ||||||
|         {% if vm.hvm %} |         {% if vm.hvm %} | ||||||
|             <emulator |             {% set timezone = vm.features.check_with_template('timezone', 'localtime').lower() %} | ||||||
|                 type="stubdom" |             {% if timezone == 'localtime' %} | ||||||
|                 {% if vm.netvm %} |                 <clock offset="variable" adjustment="0" basis="localtime" /> | ||||||
|                     cmdline="-net lwip,client_ip={{ vm.ip -}} |             {% elif timezone.isdigit() %} | ||||||
|                         ,server_ip={{ vm.secondary_dns -}} |                 <clock offset="variable" adjustment="{{ timezone }}" basis="UTC" /> | ||||||
|                         ,dns={{ vm.netvm.gateway -}} |             {% else %} | ||||||
|                         ,gw={{ self.netvm.gateway -}} |                 <clock offset="variable" adjustment="0" basis="UTC" /> | ||||||
|                         ,netmask={{ vm.netmask }}" |             {% endif %} | ||||||
|                 {% endif %} |  | ||||||
|                 /> |  | ||||||
|             <input type="tablet" bus="usb"/> |  | ||||||
|             <video type="vga"/> |  | ||||||
|         {% else %} |         {% else %} | ||||||
|             <console type="pty"> |             <clock offset='utc' adjustment='reset'> | ||||||
|                 <target type="xen" port="0"/> |                 <timer name="tsc" mode="native"/> | ||||||
|             </console> |             </clock> | ||||||
|         {% endif %} |         {% 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> |     </devices> | ||||||
| </domain> | </domain> | ||||||
|   |   | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Wojtek Porczyk
						Wojtek Porczyk