Product SiteDocumentation Site

Chapter 12. Usage Examples

12.1. Zero to Virtual Appliance
12.2. Taking a Screenshot
This section goes through the fundamentals of the library, aiming to be a quick tutorial to get developers up and running as quickly as possible.

12.1. Zero to Virtual Appliance

This section describes the steps necessary to create a virtual machine (or a container). The driver that will be used is QEMU, but these instructions can be adapted for other drivers as well. The commands given here are for python3, and you are encouraged to run them as you go through this section in an interactive prompt such as ipython3.
We begin by defining a new storage pool for out virtual machine. Note that containers do not need any form of storage.

Example 12.1. Defining a new storage pool

import libvirt, sys

conn = None
try:
    conn = libvirt.open("qemu:///system")
except libvirt.libvirtError as e:
    print(repr(e), file=sys.stderr)
    exit(1)

# We do not need to define a capacity as we want it to be unlimited.
poolXML = """<pool type='dir'>
  <name>ExamplePool</name>
  <uuid/>
  <source>
  </source>
  <target>
    <path>/var/lib/libvirt/images</path>
    <permissions>
      <mode>0755</mode>
      <owner>-1</owner>
      <group>-1</group>
    </permissions>
  </target>
</pool>"""

pool = conn.storagePoolDefineXML(poolXML, 0)
pool.setAutostart(1)
pool.create()
Then, we create a new volume for our virtual machine (again, skip this if creating a container). We use qcow2 as the disk format since it has more support than others for operations like taking snapshots. Note that the path we use is inside the path we defined for our Storage Pool. We assign 16 GiB to this volume, feel free to change this if you wish.

Example 12.2. Defining a new storage volume

volumeXML = """
<volume>
  <name>Volume.qcow2</name>
  <allocation>0</allocation>
  <capacity unit="G">16</capacity>
  <target>
    <path>/var/lib/libvirt/images/Volume.qcow2</path>
    <format type='qcow2'/>
    <permissions>
      <owner>107</owner>
      <group>107</group>
      <mode>0744</mode>
     <label>ExampleVolume</label>
    </permissions>
  </target>
</volume>"""

pool.createXML(volumeXML, 0)
Now, we define our domain. You do not have to define it in the exact same way:

Example 12.3. Defining the domain

domainXML = """
<domain type='kvm'>
  <name>ExampleDomain</name>
  <memory unit='MiB'>2048</memory>
  <currentMemory unit='MiB'>2048</currentMemory>
  <vcpu placement='static'>2</vcpu>
  <os>
    <type arch='x86_64'>hvm</type>
    <boot dev='hd'/>
    <boot dev='cdrom'/>
  </os>
  <features>
    <acpi/>
    <apic/>
    <vmport state='off'/>
  </features>
  <clock offset='utc'>
    <timer name='rtc' tickpolicy='catchup'/>
    <timer name='pit' tickpolicy='delay'/>
    <timer name='hpet' present='no'/>
  </clock>
  <pm>
    <suspend-to-mem enabled='yes'/>
    <suspend-to-disk enabled='yes'/>
  </pm>
  <devices>
    <emulator>/usr/bin/kvm-spice</emulator>
    <disk type='file' device='disk'>
      <driver name='qemu' type='qcow2'/>
      <source file='/var/lib/libvirt/images/Volume.qcow2'/>
      <target dev='hda' bus='ide'/>
      <address type='drive' controller='0' bus='0' target='0' unit='0'/>
    </disk>
    <disk type='file' device='cdrom'>
      <driver name='qemu' type='raw'/>
      <source file=''/>
      <target dev='hdb' bus='ide'/>
      <address type='drive' controller='0' bus='0' target='0' unit='1'/>
    </disk>
    <controller type='usb' index='0' model='ich9-ehci1'>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x7'/>
    </controller>
    <controller type='usb' index='0' model='ich9-uhci1'>
      <master startport='0'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0' multifunction='on'/>
    </controller>
    <controller type='usb' index='0' model='ich9-uhci2'>
      <master startport='2'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x1'/>
    </controller>
    <controller type='usb' index='0' model='ich9-uhci3'>
      <master startport='4'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x2'/>
    </controller>
    <controller type='pci' index='0' model='pci-root'/>
    <controller type='ide' index='0'>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
    </controller>
    <controller type='virtio-serial' index='0'>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/>
    </controller>
    <serial type='pty'>
      <target type='isa-serial' port='0'>
        <model name='isa-serial'/>
      </target>
    </serial>
    <console type='pty'>
      <target type='serial' port='0'/>
    </console>
    <channel type='spicevmc'>
      <target type='virtio' name='com.redhat.spice.0'/>
      <address type='virtio-serial' controller='0' bus='0' port='1'/>
    </channel>
    <input type='mouse' bus='ps2'/>
    <input type='keyboard' bus='ps2'/>
    <graphics type='spice' autoport='yes'>
      <listen type='address'/>
      <image compression='off'/>
    </graphics>
    <sound model='ich6'>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
    </sound>
    <video>
      <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
    </video>
    <redirdev bus='usb' type='spicevmc'>
      <address type='usb' bus='0' port='1'/>
    </redirdev>
    <redirdev bus='usb' type='spicevmc'>
      <address type='usb' bus='0' port='2'/>
    </redirdev>
    <memballoon model='virtio'>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/>
    </memballoon>
  </devices>
</domain>"""

dom = conn.defineXML(domainXML)
We have successfully defined a virtual machine. You’ll notice however if you try to start this domain, not much happens. We now need to install an operating system. And to do that, we need to attach a DVD drive with the installation disk:

Example 12.4. Attaching a new disk to the domain

# Path to desired '.iso' file
diskFile = "/tmp/debian.iso"
diskXML = """    <disk type='file' device='cdrom'>
      <driver name='qemu' type='raw'/>
      <source file='""" + diskFile + """'/>
      <target dev='hdb' bus='ide'/>
      <address type='drive' controller='0' bus='0' target='0' unit='1'/>
    </disk>"""
dom.updateDeviceFlags(diskXML, 0)
Because we updated the device instead of creating a new one, we made sure to indent the XML appropriately for legibility.
Now, we can go ahead and start our domain:

Example 12.5. Starting the domain

dom.create()
We can interact with our domain using virt-viewer. Simply running it from the terminal gives us a list of virtual machines we can connect to, so that we can start a graphics session:
$ virt-viewer
Once we are done with the installation and configuration of our new domain, we should take a snapshot so we can preserve its state. We can do this like so:

Example 12.6. Taking a snapshot

snapXML = """
<domainsnapshot>
  <name>FirstSnapshot</name>
  <description>Just created this virtual machine</description>
</domainsnapshot>"""
dom.snapshotCreateXML(snapXML, 0)
Note that if you have applications such as 'Virtual Machine Manager' running to check what you are doing, you may need to refresh the snapshot list to have your new snapshot show up.
And with that, we are done! We have created a virtual machine from scratch, and even taken a snapshot to back it up! If we ever mess up the state of the virtual machine, we can always revert back to a snapshot of a known good state like so:

Example 12.7. Revert to snapshot

# List all the snapshots
snapshotList = dom.listAllSnapshots()
# Find the snapshot with the name we want:
revertTo = None
for snap in snapshotList:
    if snap.getName() == 'FirstSnapshot':
        revertTo = snap
if revertTo is not None:
    dom.revertToSnapshot(revertTo)