© W. David Ashley 2019
W. David AshleyFoundations of Libvirt Development https://doi.org/10.1007/978-1-4842-4862-1_4

4. Guest Domains

W. David Ashley1 
(1)
Austin, TX, USA
 

A domain is a reference to a running or nonrunning virtual operating system under libvirt. As many domains as necessary can be created/installed under libvirt up to the limit imposed by either the system administrator or the number that can be supported without performance penalties on the host system. This number will vary depending on the performance abilities of the host system.

A nonrunning domain does not add any performance penalties on the host machine; only running domains impose penalties on the host system. Therefore, the system administrator can impose a limit on the number of running domains instead of the number of total domains.

Domain Overview

As mentioned in Chapter 2, a domain is an instance of an operating system running on a virtualized machine. The connection object provides methods to enumerate the guest domains, create new guest domains, and manage existing domains. A guest domain is represented with an instance of the virDomain class and has a number of unique identifiers.
  • ID: This is a positive integer, unique among the running guest domains on a single host. An inactive domain does not have an ID.

  • Name: This is a short string, unique among all the guest domains on a single host, both running and inactive. To ensure maximum portability between hypervisors, it is recommended that names include only alphanumeric (aZ, 09), hyphen (-), and underscore (_) characters.

  • UUID: This consists of 16 unsigned bytes, guaranteed to be unique among all guest domains on any host. RFC 4122 defines the format for UUIDs and provides a recommended algorithm for generating UUIDs with guaranteed uniqueness.

A guest domain can be transient or persistent. A transient guest domain can be managed only while it is running on the host. Once it is powered off, all traces of it will disappear. A persistent guest domain has its configuration maintained in a data store on the host by the hypervisor, in an implementation-defined format. Thus, when a persistent guest is powered off, it is still possible to manage its inactive configuration. A transient guest can be turned into a persistent guest while it is running by defining a configuration for it.

Once an application has a unique identifier for a domain, it will often want to obtain the corresponding virDomain object. There are three methods to look up existing domains: lookupByID, lookupByName, and lookupByUUID. Each of these takes the domain identifier as a parameter. They will return None if no matching domain exists. The error object can be queried to find specific details of the error if required. Listing 4-1 shows an example of how to query that error.
# Example-1.py
from __future__ import print_function
import sys
import libvirt
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
domainID = 6
dom = conn.lookupByID(domainID)
if dom == None:
    print('Failed to get the domain object', file=sys.stderr)
conn.close()
exit(0)
Listing 4-1

Fetching a Domain Object from an ID

Note

Listing 4-1 may abend if domainid is not found because it is possible for libvirt to abort the process rather than return an error to Python.

It should be noted that the lookupByID method will not work if the domain is not active. Inactive domains all have an ID of -1. Listing 4-2 shows how to fetch a domain using its libvirt name.
# Example-2.py
from __future__ import print_function
import sys
import libvirt
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
domainName = 'someguest'
dom = conn.lookupByName(domainName)
if dom == None:
    print('Failed to get the domain object', file=sys.stderr)
conn.close()
Listing 4-2

Fetching a Domain Object from a Name

Note

Listing 4-2 may abend if domainName is not found because it is possible for libvirt to abort the process rather than return an error to Python.

Listing 4-3 shows how to fetch a domain from the domain’s UUID.
# Example-3.py
from __future__ import print_function
import sys
import libvirt
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
domainUUID = '00311636-7767-71d2-e94a-26e7b8bad250'
dom = conn.lookupByUUID(domainUUID)
if dom == None:
    print('Failed to get the domain object', file=sys.stderr)
conn.close()
exit(0)
Listing 4-3

Fetching a Domain Object from a UUID

Note

Listing 4-3 may abend if domainUUID is not found because it is possible for libvirt to abort the process rather than return an error to Python.

The previous UUID example uses the printable format of UUID. Using the equivalent raw bytes is not supported by Python.

Listing Domains

The libvirt classes expose two lists of domains: the first contains running domains, while the second contains inactive, persistent domains. The lists are intended to be nonoverlapping, exclusive sets, though there is always a small possibility that a domain can stop or start in between the querying of each set. The events class described later in this section provides a way to track all lifecycle changes, avoiding this potential race condition.

The method for listing active domains returns a list of domain IDs. Every running domain has a positive integer ID, uniquely identifying it among all running domains on the host. The method for listing active domains, listDomainsID, requires no parameters. The return value will be None upon error or a Python list of the IDs expressed as ints. Listing 4-4 shows how to obtain a list of all running (active) domains.
# Example-4.py
from __future__ import print_function
import sys
import libvirt
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
domainIDs = conn.listDomainsID()
if domainIDs == None:
    print('Failed to get a list of domain IDs',
          file=sys.stderr)
print("Active domain IDs:")
if len(domainIDs) == 0:
    print('  None')
else:
    for domainID in domainIDs:
        print('  '+str(domainID))
conn.close()
exit(0)
Listing 4-4

Listing Active Domains

In addition to the running domains, there may be some persistent inactive domain configurations stored on the host. Since an inactive domain does not have any ID identifier, the listing of inactive domains is exposed as a list of name strings. The return value will be None upon error, or a Python list of elements filled with names (strings). Listing 4-5 shows how to obtain a list of all inactive domains.
# Example-5.py
from __future__ import print_function
import sys
import libvirt
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
domainNames = conn.listDefinedDomains()
if conn == None:
    print('Failed to get a list of domain names', file=sys.stderr)
domainIDs = conn.listDomainsID()
if domainIDs == None:
    print('Failed to get a list of domain IDs', file=sys.stderr)
if len(domainIDs) != 0:
    for domainID in domainIDs:
        domain = conn.lookupByID(domainID)
        domainNames.append(domain.name)
print("All (active and inactive domain names:")
if len(domainNames) == 0:
    print('  None')
else:
    for domainName in domainNames:
        print('  '+domainName)
conn.close()
exit(0)
Listing 4-5

Listing Inactive Domains

The methods for listing domains do not directly return the virDomain objects since this may incur an undue performance penalty for applications that want to query the list of domains on a frequent basis. However, the Python libvirt module does provide the method listAllDomains, which returns all the domains, active or inactive. It returns a Python list of the virDomain instances or None upon an error. The list can be empty when no persistent domains exist.

The listAllDomains method takes a single parameter that is a flag specifying a filter for the domains to be listed. If a value of 0 is specified, then all domains will be listed. Otherwise, any or all of the following constants can be added together to create a filter for the domains to be listed:

VIR_CONNECT_LIST_DOMAINS_ACTIVE

VIR_CONNECT_LIST_DOMAINS_INACTIVE

VIR_CONNECT_LIST_DOMAINS_PERSISTENT

VIR_CONNECT_LIST_DOMAINS_TRANSIENT

VIR_CONNECT_LIST_DOMAINS_RUNNING

VIR_CONNECT_LIST_DOMAINS_PAUSED

VIR_CONNECT_LIST_DOMAINS_SHUTOFF

VIR_CONNECT_LIST_DOMAINS_OTHER

VIR_CONNECT_LIST_DOMAINS_MANAGEDSAVE

VIR_CONNECT_LIST_DOMAINS_NO_MANAGEDSAVE

VIR_CONNECT_LIST_DOMAINS_AUTOSTART

VIR_CONNECT_LIST_DOMAINS_NO_AUTOSTART

VIR_CONNECT_LIST_DOMAINS_HAS_SNAPSHOT

VIR_CONNECT_LIST_DOMAINS_NO_SNAPSHOT

Listing 4-6 shows how to obtain a list of active and inactive domainslistAllDomains method .
# Example-6.py
from __future__ import print_function
import sys
import libvirt
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
print("All (active and inactive) domain names:")
domains = conn.listAllDomains(0)
if len(domains) != 0:
    for domain in domains:
        print('  '+domain.name())
else:
    print('  None')
conn.close()
exit(0)
Listing 4-6

Fetching All Domain Objects

Obtaining State Information About a Domain

Once a domain instance has been obtained, it is possible to fetch information about the state of the domain, such as the type of OS being hosted, running state, ID, UUID, and so on. The following methods will demonstrate how to fetch this information.

Fetching the ID of a Domain

The ID of a domain can be obtained by using the ID method . Only running domains have an ID; fetching the ID of a nonrunning domain always returns -1. Listing 4-7 shows how to obtain the ID of a domain from the libvirt domain name.
# Example-43.py
from __future__ import print_function
import sys
import libvirt
from xml.dom import minidom
domName = 'CentOS7'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByName(domName)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
id = dom.ID()
if id == -1:
    print('The domain is not running so has no ID.')
else:
    print('The ID of the domain is ' + str(id))
conn.close()
exit(0)
Listing 4-7

Fetching the ID of a Domain

Fetching the UUID of a Domain

The UUID of a domain can be obtained by using the UUID or UUIDString method . The UUID method is not all that useful for Python programs because it is a binary value. The UUIDString method is much more useful because it returns a formatted string value that can be easily parsed.

The UUID is not dependent on the running state of the domain and always returns a valid UUID. Listing 4-8 shows how to fetch the UUID of a domain using the domain name.
# Example-44.py
from __future__ import print_function
import sys
import libvirt
from xml.dom import minidom
domName = 'CentOS7'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByName(domName)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
uuid = dom.UUIDString()
print('The UUID of the domain is ' + uuid)
conn.close()
exit(0)
Listing 4-8

Fetching the UUID of a Domain

Fetching the OS Type of a Domain

The type of OS hosted by a domain is also available. Only running domains have an ID; fetching the ID of a nonrunning domain always returns -1. This same information can be retrieved via the info method . Listing 4-9 shows how to fetch the operating system type from a running domain.
# Example-45.py
from __future__ import print_function
import sys
import libvirt
from xml.dom import minidom
domName = 'CentOS7'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByName(domName)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
type = dom.OSType()
print('The OS type of the domain is "' + type + '"')
conn.close()
exit(0)
Listing 4-9

Fetching the ID of a Domain

Determining Whether the Domain Has a Current Snapshot

The hasCurrentSnapshot method returns a Boolean value indicating if a current snapshot is available. This method always returns a valid value and is not dependent on the running state of the domain. Listing 4-10 determines whether a domain has a current snapshot.
# Example-47.py
from __future__ import print_function
import sys
import libvirt
from xml.dom import minidom
domName = 'CentOS7'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByName(domName)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
flag = dom.hasCurrentSnapshot()
print('The value of the current snapshot flag is ' + str(flag))
conn.close()
exit(0)
Listing 4-10

Determining Whether the Domain Has a Current Snapshot

Determining Whether the Domain Has Managed Save Images

The hasManagedSaveImages method returns a Boolean value indicating if a domain has a managed save image. A saved image is created on an as-needed basis by the user.

Note that a running domain should never have a saved image because that image should have been removed when the domain was restarted. Listing 4-11 determines whether the domain has a managed save image.
# Example-48.py
from __future__ import print_function
import sys
import libvirt
from xml.dom import minidom
domName = 'CentOS7'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByName(domName)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
flag = dom.hasManagedSaveImage()
print('The value of the manaed save images flag is ' + str(flag))
conn.close()
exit(0)
Listing 4-11

Determining Whether the Domain Has a Managed Save Image

Fetching the Hostname of the Domain

The hostname method returns the hostname of the domain. The hostname method is highly dependent on the hypervisor and/or the qemu-guest-agent. It may throw an error if the method cannot complete successfully. Listing 4-12 will return the hostname of the domain (if available).
# Example-49.py
from __future__ import print_function
import sys
import libvirt
from xml.dom import minidom
domName = 'CentOS7'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByName(domName)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
name = dom.hostname()
print('The hostname of the domain  is ' + str(name))
conn.close()
exit(0)
Listing 4-12

Fetch the Hostname of the Domain

Getting the Domain Hardware Information

The info method returns some general information about the domain hardware. There should be five entries returned in a Python list: the state, max memory, memory, CPUs, and CPU time for the domain. Listing 4-13 obtains general information from a domain.
# Example-50.py
from __future__ import print_function
import sys
import libvirt
from xml.dom import minidom
domName = 'CentOS7'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByName(domName)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
state, maxmem, mem, cpus, cput = dom.info()
print('The state is ' + str(state))
print('The max memory is ' + str(maxmem))
print('The memory is ' + str(mem))
print('The number of cpus is ' + str(cpus))
print('The cpu time is ' + str(cput))
conn.close()
exit(0)
Listing 4-13

Get the Domain Info

Determining Whether the Domain Is Running

The isActive method returns a Boolean flag indicating whether the domain is active (running). Listing 4-14 returns a Boolean indicating the running state of the domain.
# Example-51.py
from __future__ import print_function
import sys
import libvirt
from xml.dom import minidom
domName = 'CentOS7'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByName(domName)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
flag = dom.isActive()
if flag == True:
    print('The domain is active.')
else:
    print('The domain is not active.')
conn.close()
exit(0)
Listing 4-14

Determine Whether the Domain Is Running

Determining Whether the Domain Is Persistent

The isPersistent method returns a Boolean flag indicating whether or not the domain is persistent (the domain will be persistent after a reboot). Listing 4-15 determines whether a running domain is persistent.
# Example-52.py
from __future__ import print_function
import sys
import libvirt
from xml.dom import minidom
domName = 'CentOS7'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByName(domName)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
flag = dom.isPersistent()
if flag == 1:
    print('The domain is persistent.')
elif flag == 0:
    print('The domain is not persistent.')
else:
    print('There was an error.')
conn.close()
exit(0)
Listing 4-15

Determine Whether the Domain Is Persistent

Determining Whether the Domain Is Updated

The isUpdated method returns a Boolean flag indicating whether the domain has been updated since it was created. An update to the domain can be just about any change to the domain’s configuration. Listing 4-16 returns a flag indicating the domain’s change state.
# Example-59.py
from __future__ import print_function
import sys
import libvirt
from xml.dom import minidom
domName = 'CentOS7'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByName(domName)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
flag = dom.isUpdated()
if flag == 1:
    print('The domain is updated.')
elif flag == 0:
    print('The domain is not updated.')
else:
    print('There was an error.')
conn.close()
exit(0)
Listing 4-16

Determine Whether the Domain Is Updated

Determining the Max Memory of the Domain

The maxMemory method returns the maximum memory allocated to the domain. This same information may be retrieved via the info method. Listing 4-17 returns the max memory allocated to the domain.
# Example-53.py
from __future__ import print_function
import sys
import libvirt
from xml.dom import minidom
domName = 'CentOS7'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByName(domName)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
mem = dom.maxMemory()
if mem > 0:
    print('The max memory for domain is ' + str(mem) + 'MB')
else:
    print('There was an error.')
conn.close()
exit(0)
Listing 4-17

Determine the Max Memory of the Domain

Determining the Max VCPUs of the Domain

The maxVcpus method returns the maximum number of virtual CPUs allocated to the domain. This same information can be retrieved via the info method. This works only on active domains. Listing 4-18 returns the max VCPUs allocated to the domain.
# Example-54.py
from __future__ import print_function
import sys
import libvirt
from xml.dom import minidom
domName = 'CentOS7'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByName(domName)
if dom == None:
    print('Failed to find the domain '+domName,
          file=sys.stderr)
    exit(1)
cpus = dom.maxVcpus()
if cpus != -1:
    print('The max Vcpus for domain is ' + str(cpus))
else:
    print('There was an error.')
conn.close()
exit(0)
Listing 4-18

Determine the Max VCPUs of the Domain

Fetching the Name of the Domain

The name method returns the name of the domain. Listing 4-19 returns the name of a domain.
# Example-55.py
from __future__ import print_function
import sys
import libvirt
from xml.dom import minidom
domName = 'CentOS7'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByName(domName)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
name = dom.name()
print('Thename of the domain is "' + name +'".')
conn.close()
exit(0)
Listing 4-19

Fetch the Name of the Domain

Fetching the State of the Domain

The state method returns the state of the domain. Listing 4-20 returns the state of the domain.
# Example-56.py
from __future__ import print_function
import sys
import libvirt
from xml.dom import minidom
domName = 'CentOS7'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByName(domName)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
state, reason = dom.state()
if state == libvirt.VIR_DOMAIN_NOSTATE:
    print('The state is VIR_DOMAIN_NOSTATE')
elif state == libvirt.VIR_DOMAIN_RUNNING:
    print('The state is VIR_DOMAIN_RUNNING')
elif state == libvirt.VIR_DOMAIN_BLOCKED:
    print('The state is VIR_DOMAIN_BLOCKED')
elif state == libvirt.VIR_DOMAIN_PAUSED:
    print('The state is VIR_DOMAIN_PAUSED')
elif state == libvirt.VIR_DOMAIN_SHUTDOWN:
    print('The state is VIR_DOMAIN_SHUTDOWN')
elif state == libvirt.VIR_DOMAIN_SHUTOFF:
    print('The state is VIR_DOMAIN_SHUTOFF')
elif state == libvirt.VIR_DOMAIN_CRASHED:
    print('The state is VIR_DOMAIN_CRASHED')
elif state == libvirt.VIR_DOMAIN_PMSUSPENDED:
    print('The state is VIR_DOMAIN_PMSUSPENDED')
else:
    print(' The state is unknown.')
print('The reason code is ' + str(reason))
conn.close()
exit(0)
Listing 4-20

Fetch the State of the Domain

Extracting the Time Information from the Domain

The getTime method extracts the current timestamp from the domain. The method returns the same value as the Python time.struct_time function. Listing 4-21 will fetch the current time as known by the domain.
# Example-57.py
from __future__ import print_function
import sys, time
import libvirt
from xml.dom import minidom
domName = 'CentOS7'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByName(domName)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
struct = dom.getTime()
timestamp = time.ctime(float(struct['seconds']))
print('The domain current time is ' + timestamp)
conn.close()
exit(0)
Listing 4-21

Fetch the Time Information of the Domain

Extracting the Network Interface Addresses from a Domain

The interfaceAddresses method extracts the current network interface address. This information is dynamic in nature as it can be modified by a guest domain system administrator. Thus, you should not depend on this information remaining static. The QEMU guest agent is queried by the libvirt library to obtain this information, and thus the agent must be running on the guest domain for any information to be returned.

Listing 4-22 will return the list of IP addresses assigned to an interface.
# Example-58.py
from __future__ import print_function
import libvirt
# setup
"""List network interface IP Addresses for a guest domain.
"""
domName = 'CentOS7'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
domain = conn.lookupByName(domName)
if domain == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
# make sure the domain is running
if domain.isActive() == False:
    print('Error: Domain is not active or never started.')
    exit(1)
# get and list the ip addresses, use QEMU as the source information
ifaces = domain.interfaceAddresses(libvirt.
                VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT, 0)
for (name, val) in ifaces.iteritems():
    if val['addrs']:
        for ipaddr in val['addrs']:
            print(name+' '+str(ipaddr))
conn.close()
exit(0)
Listing 4-22

Fetch the Network Interface IP Addresses of the Domain

Note

This program will fail if the QEMU guest agent does not support this function.

Lifecycle Control

libvirt can control the entire lifecycle of guest domains (see Figure 4-1). Guest domains transition through several states throughout their lifecycle.
  1. 1.

    Undefined: This is the baseline state. An undefined guest domain has not been defined or created in any way.

     
  2. 2.

    Defined: A defined guest domain has been defined but is not running. This state could also be described as Stopped.

     
  3. 3.

    Running: A running guest domain is defined and being executed on a hypervisor.

     
  4. 4.

    Paused: A paused guest domain is in a suspended state from the Running state. Its memory image has been temporarily stored, and it can be resumed to the Running state without the guest domain operating system being aware it was ever suspended.

     
  5. 5.

    Saved: A saved domain has had its memory image, as captured in the Paused state, saved to persistent storage. It can be restored to the Running state without the guest domain operating system being aware it was ever suspended.

     
The transitions between these states fall into several categories; see the following sections for more details.
../images/478111_1_En_4_Chapter/478111_1_En_4_Fig1_HTML.jpg
Figure 4-1.

Guest domain lifecycle

Provisioning and Starting

Provisioning refers to the task of creating new guest domains, typically using some form of operating system installation media. There are a wide variety of ways in which a guest can be provisioned, but the choices available will vary according to the hypervisor and type of guest domain being provisioned. It is not uncommon for an application to support several different provisioning methods. Starting refers to executing a provisioned guest domain on a hypervisor.

Methods for Provisioning

There are up to three methods involved in provisioning guests. The createXML method will create and immediately boot a new transient guest domain. When this guest domain shuts down, all traces of it will disappear. The defineXML method will store the configuration for a persistent guest domain. The create method will boot a previously defined guest domain from its persistent configuration. One important thing to note is that the defineXML command can be used to turn a previously booted transient guest domain into a persistent domain. This can be useful for some provisioning scenarios that will be illustrated later.

Booting a Transient Guest Domain

Booting a transient guest domain simply requires a connection to libvirt and a string containing the XML document describing the required guest configuration and a flag that controls the startup of the domain.

If the VIR_DOMAIN_START_PAUSED flag is set, the guest domain will be started, but its CPUs will remain paused. The CPUs can later be manually started using the resume method.

If the VIR_DOMAIN_START_AUTODESTROY flag is set, the guest domain will be automatically destroyed when the virConnect object is finally released. This will also happen if the client application crashes or loses its connection to the libvirtd daemon. Any domains marked for autodestroy will block attempts at migrating domains, saving to a file, or taking snapshots.

Listing 4-23 shows how to create a domain from an existing XML file. XML files will be covered later in the chapter.
# Example-7.py
from __future__ import print_function
import sys
import libvirt
xmlconfig = '<domain>........</domain>'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.createXML(xmlconfig, 0)
if dom == None:
    print('Failed to create a domain from an XML definition.',
          file=sys.stderr)
    exit(1)
print('Guest '+dom.name()+' has booted', file=sys.stderr)
conn.close()
exit(0)
Listing 4-23

Provisioning a Transient Guest Domain

Note

Listing 4-23 is just an example, and the XML does not exist in the provided code examples.

If the domain creation attempt succeeded, then the returned virDomain instance will be returned; otherwise, None will be returned. Although the domain was booted successfully, this does not guarantee that the domain is still running. It is entirely possible for the guest domain to crash, in which case attempts to use the returned virDomain object will generate an error since transient guests cease to exist when they are shut down (whether via a planned shutdown or a crash). Managing this scenario requires use of a persistent guest.

Defining and Booting a Persistent Guest Domain

Before a persistent domain can be booted, it must have its configuration defined. This again requires a connection to libvirt and a string containing the XML document describing the required guest configuration. The virDomain object obtained from defining the guest can then be used to boot it.

Currently the defineDomain method defines a flags parameter that is unused. A value of 0 should always be supplied for that parameter. This may be changed in later versions of the method. Listing 4-24 is a sample of how to create a domain. It is not a complete program as the XML for the program does not exist.
# Example-8.py
from __future__ import print_function
import sys
import libvirt
xmlconfig = '<domain>........</domain>'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.defineXML(xmlconfig, 0)
if dom == None:
    print('Failed to define a domain from an XML definition.',
          file=sys.stderr)
    exit(1)
if dom.create(dom) < 0:
    print('Can not boot guest domain.', file=sys.stderr)
    exit(1)
print('Guest '+dom.name()+' has booted', file=sys.stderr)
conn.close()
exit(0)
Listing 4-24

Defining and Booting a Persistent Guest Domain

New Guest Domain Provisioning Techniques

This section will first illustrate two configurations that allow for a provisioning approach that is comparable to those used for physical machines. It then outlines a third option that is specific to virtualized hardware but has some interesting benefits. For the purposes of illustration, the examples that follow will use an XML configuration that sets up a KVM fully virtualized guest, with a single disk and network interface and a video card using VNC for display.

The following is a sample XML configuration. Note that the demo.img file does not exist.
<domain type="kvm">
  <name>demo</name>
  <os>
    <type arch=’i686’ machine=’pc’>hvm</type>
    <boot> dec=’hd’/>
  </os>
  <uuid>c7a5fdbd-cdaf-9455-926a-d65c16db1809</uuid>
  <memory>500000</memory>
  <vcpu>1</vcpu>
  .... the <os> block will vary per approach ...
  <clock offset="utc"/>
  <on_poweroff>destroy</on_poweroff>
  <on_reboot>restart</on_reboot>
  <on_crash>destroy</on_crash>
  <devices>
    <emulator>/usr/bin/qemu-kvm</emulator>
    <disk type="file" device="disk">
      <source file='/var/lib/libvirt/images/demo.img'/>
      <driver name="qemu" type="raw"/>
      <target dev="hda"/>
    </disk>
    <interface type="bridge">
      <mac address='52:54:00:d8:65:c9'/>
      <source bridge="br0"/>
    </interface>
    <input type="mouse" bus="ps2"/>
    <graphics type="vnc" port='-1' listen='127.0.0.1'/>
  </devices>
</domain>

../images/478111_1_En_4_Chapter/478111_1_En_4_Figa_HTML.gifImportant

Be careful when choosing initial memory allocation. Too low of a value may cause mysterious crashes and installation failures. Some operating systems need as much as 600 MB of memory for initial installation, though this can often be reduced post-install.

CD-ROM/ISO Image Provisioning

All full virtualization technologies have support for emulating a CD-ROM device in a guest domain, making this an obvious choice for provisioning new guest domains. It is, however, fairly rare to find a hypervisor that provides CD-ROM devices for paravirtualized guests.

The first obvious change required to the XML configuration to support CD-ROM installation is to add a CD-ROM device. A guest domain’s CD-ROM device can be pointed either to a host CD-ROM device or to an ISO image file. The next change is to determine what the BIOS boot order should be, with there being two possible options. If the hard disk is listed ahead of the CD-ROM device, then the CD-ROM media won’t be booted unless the first boot sector on the hard disk is blank. If the CD-ROM device is listed ahead of the hard disk, then it will be necessary to alter the guest config after install to make it boot off the installed disk. While both can be made to work, the first option is easiest to implement.

The guest configuration shown earlier will have the following XML chunk inserted:
<os>
  <type arch="x86_64" machine="pc">hvm</type>
  <boot dev="hd"/>
  <boot dev="cdrom"/>
</os>
This assumes the hard disk boot sector is blank initially so that the first boot attempt falls through to the CD-ROM drive. It will also need a CD-ROM drive device added.
<disk type="file" device="cdrom">
  <source file='/var/lib/libvirt/images/rhel5-x86_64-dvd.iso'/>
  <target dev="hdc" bus="ide"/>
</disk>
With the configuration determined, it is now possible to provision the guest. This is an easy process, simply requiring a persistent guest to be defined and then booted. Listing 4-25 shows how to define and boot a domain. Note that the XML is missing, so this is just a sample program and will not run properly.
# Example-12.py
from __future__ import print_function
import sys
import libvirt
xmlconfig = '<domain>........</domain>'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.defineXML(xmlconfig, 0)
if dom == None:
    print('Failed to define a domain from an XML definition.',
          file=sys.stderr)
    exit(1)
if dom.create(dom) < 0:
    print('Can not boot guest domain.', file=sys.stderr)
    exit(1)
print('Guest '+dom.name()+' has booted', file=sys.stderr)
conn.close()
exit(0)
Listing 4-25

Defining and Booting a Persistent Guest Domain

If it had not been possible to guarantee that the boot sector of the hard disk was blank, then provisioning would have been a two-step process. First a transient guest would have been booted using a CD-ROM drive as the primary boot device. Once that was completed, then a persistent configuration for the guest would be defined to boot off the hard disk.

In addition to the defineXML method, the alternative method defineXMLFlags is available.

PXE Boot Provisioning

Some newer full virtualization technologies provide a BIOS that is able to use the PXE boot protocol to boot off the network. If an environment already has a PXE boot provisioning server deployed, this is a desirable method to use for guest domains.

PXE booting a guest obviously requires that the guest has a network device configured. The LAN that this network card is attached to also needs a PXE/TFTP server available. The next change is to determine what the BIOS boot order should be, with there being two possible options. If the hard disk is listed ahead of the network device, then the network card won’t PXE boot unless the first boot sector on the hard disk is blank. If the network device is listed ahead of the hard disk, then it will be necessary to alter the guest config after install to make it boot off the installed disk. While both can be made to work, the first option is easiest to implement.

The guest configuration shown earlier will have the following XML chunk inserted:
<os>
  <type arch="x86_64" machine="pc">hvm</type>
  <boot dev="hd"/>
  <boot dev="network"/>
</os>

This assumes the hard disk boot sector is blank initially, so the first boot attempt falls through to the NIC. With the configuration determined, it is now possible to provision the guest. This is an easy process, simply requiring a persistent guest to be defined and then booted.

Listing 4-26 shows how to define a PXE domain and boot it. Note that the XML is missing, so this is just a sample program and will not run properly.
# Example-14.py
from __future__ import print_function
import sys
import libvirt
xmlconfig = '<domain>........</domain>'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.defineXML(xmlconfig, 0)
if dom == None:
    print('Failed to define a domain from an XML definition.',
          file=sys.stderr)
    exit(1)
if dom.create(dom) < 0:
    print('Can not boot guest domain.', file=sys.stderr)
    exit(1)
print('Guest '+dom.name()+' has booted', file=sys.stderr)
conn.close()
exit(0)
Listing 4-26

PXE Boot Provisioning

If it had not been possible to guarantee that the boot sector of the hard disk was blank, then provisioning would have been a two-step process. First a transient guest would have been booted using a network as the primary boot device. Once that was completed, then a persistent configuration for the guest would be defined to boot off the hard disk.

Direct Kernel Boot Provisioning

Paravirtualization technologies emulate a fairly restrictive set of hardware, often making it impossible to use the provisioning options just outlined. For such scenarios, it is often possible to boot a new guest domain directly from a kernel and initrd image stored on the host file system. This has one interesting advantage, which is that it is possible to directly set kernel command-line boot arguments, making it easy to carry out a fully automated installation. This advantage can be compelling enough that this technique is used even for fully virtualized guest domains with CD-ROM drive/PXE support.

The one complication with direct kernel booting is that provisioning becomes a two-step process. For the first step, it is necessary to configure the guest XML configuration to point to a kernel/initrd. Listing 4-27 shows how to specify a kernel boot configuration.
<os>
  <type arch="x86_64" machine="pc">hvm</type>
  <kernel>/var/lib/libvirt/boot/f11-x86_64-vmlinuz</kernel>
  <initrd>/var/lib/libvirt/boot/f11-x86_64-initrd.img</initrd>
  <cmdline>method=http://download.fedoraproject.org/pub/fedora/linux/releases/11/x86_64/os console=ttyS0 console=tty</cmdline>
</os>
Listing 4-27

Kernel Boot Provisioning XML

Notice how the kernel command line provides the URL of the download site containing the distro install tree matching the kernel/initrd. This allows the installer to automatically download all its resources without prompting the user for an install URL. It could also be used to provide a kickstart file for a completely unattended installation. Finally, this command line also tells the kernel to activate both the first serial port and the VGA card as consoles, with the latter being the default. Having kernel messages duplicated on the serial port in this manner can be a useful debugging avenue. Of course, valid command-line arguments vary according to the particular kernel being booted. Consult the kernel vendor/distributor's documentation for valid options.

The last XML configuration detail before starting the guest is to change the on_reboot element action to destroy. This ensures that when the guest installer finishes and requests a reboot, the guest is instead powered off. This allows the management application to change the configuration to make it boot from the just installed hard disk again. The provisioning process can be started now by creating a transient guest with the first XML configuration.

Listing 4-28 shows how to create a kernel boot domain. Note that the XML is missing, so the program will not work until the XML is supplied.
# Listing-14.py
from __future__ import print_function
import sys
import libvirt
xmlconfig = '<domain>........</domain>'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.createXML(xmlconfig, 0)
if dom == None:
    print('Unable to boot transient guest configuration.',
          file=sys.stderr)
    exit(1)
print('Guest '+dom.name()+' has booted', file=sys.stderr)
conn.close()
exit(0)
Listing 4-28

Kernel Boot Provisioning

Once this guest shuts down, the second phase of the provisioning process can be started. For this phase, the OS element will have the kernel/initrd/cmdline elements removed and replaced by either a reference to a host-side bootloader or a BIOS boot setup. The former is used for Xen paravirtualized guests, while the latter is used for fully virtualized guests.

The phase 2 configuration for a Xen paravirtualized guest would thus look like this:
<bootloader>/usr/bin/pygrub</bootloader>
<os>
  <type arch="x86_64" machine="pc">xen</type>
</os>
A fully virtualized guest would use this:
<bootloader>/usr/bin/pygrub</bootloader>
<os>
  <type arch="x86_64" machine="pc">hvm</type>
  <boot dev="hd"/>
</os>
With the second phase configuration determined, the guest can be re-created, this time using a persistent configuration. Listing 4-29 shows how to create a persistent kernel boot domain.
# Example-18.py
from __future__ import print_function
import sys
import libvirt
xmlconfig = '<domain>........</domain>'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.createXML(xmlconfig, 0)
if dom == None:
    print('Unable to define persistent guest configuration.',
          file=sys.stderr)
    exit(1)
if dom.create(dom) < 0:
    print('Can not boot guest domain.', file=sys.stderr)
    exit(1)
print('Guest '+dom.name()+' has booted', file=sys.stderr)
conn.close()
exit(0)
Listing 4-29

Kernel Boot Provisioning for a Persistent Guest Domain

In addition to the createXML method , the alternative method createXMLFlags is available.

Stopping

Stopping refers to the process of halting a running guest. A guest can be stopped by two methods: shutdown and destroy.

The shutdown method is a clean stop process, which sends a signal to the guest domain operating system asking it to shut down immediately. The guest will be stopped only once the operating system has successfully shut down. The shutdown process is analogous to running a shutdown command on a physical machine. There is also a shutdownFlags method that can, depending on what the guest OS supports, shut down the domain and leave the object in a usable state.

The destroy and destroyFlags methods immediately terminate the guest domain. The destroy process is analogous to pulling the plug on a physical machine.

Suspending/Resuming and Saving/Restoring

The suspend and resume methods refer to the process of taking a running guest and temporarily saving its memory state. At a later time, it is possible to resume the guest to its original running state, continuingly executing where it left off. Suspend does not save a persistent image of the guest’s memory. For this, save is used.

The save and restore methods refer to the process of taking a running guest and saving its memory state to a file. At some time later, it is possible to restore the guest to its original running state, continuing execution where it left off.

It is important to note that the save/restore methods save only the memory state; no storage state is preserved. Thus, when the guest is restored, the underlying guest storage must be in the same state as it was when the guest was initially saved. For basic usage, this implies that a guest can be restored only once from any given saved state image. To allow a guest to be restored from the same saved state multiple times, the application must also have taken a snapshot of the guest storage at the time of saving and explicitly revert to this storage snapshot when restoring. A future enhancement in libvirt will allow for an automated snapshot capability that saves memory and storage state in one operation.

The save operation requires a fully qualified path to a file in which the guest domain's memory state will be saved. This path/file name must reside in the hypervisor’s file system, not the libvirt client application’s. There’s no difference between the two if managing a local hypervisor, but it is critically important if connecting remotely to a hypervisor across the network. The example in Listing 4-30 demonstrates saving a guest called demo-guest to a file. It checks to verify that the guest is running before saving, though this is technically redundant since the hypervisor driver will do such a check itself.

Listing 4-30 shows how to save a guest domain and then stop it in one step.
# Example-20.py
from __future__ import print_function
import sys
import libvirt
filename = '/var/lib/libvirt/save/demo-guest.img'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByName('demo-guest')
if dom == None:
    print('Cannot find guest to be saved.', file=sys.stderr)
    exit(1)
info = dom.info()
if info == None:
    print('Cannot check guest state', file=sys.stderr)
    exit(1)
if info.state == VIR_DOMAIN_SHUTOFF:
    print('Not saving guest that is not running', file=sys.stderr)
    exit(1)
if dom.save(filename) < 0:
    print('Unable to save guest to '+filename, file=sys.stderr)
print('Guest state saved to '+filename, file=sys.stderr)
conn.close()
exit(0)
Listing 4-30

Saving a Guest Domain

Some period of time later, the saved state file can then be used to restart the guest where it left off, using the restore method . The hypervisor driver will return an error if the guest is already running; however, it won’t prevent attempts to restore from the same state file multiple times. As noted earlier, it is the application’s responsibility to ensure the guest storage is in the same state as it was when the save image was created.

In addition, the saveFlags method allows the domain to be saved and at the same alter the configuration of the saved image. When the domain is restored, the new configuration will be applied to the running domain.

There is also another way to save a domain. The managedSave method can save a running domain state; however, in this case, the system selects the location for the saved image. In addition, the domain will be restored to the saved state when the domain is restarted.

Listing 4-31 shows how to restore a domain to a running state from a save image. Note that in this case the image file may not exist, and thus the program may fail if you try to run it.
# Example-21.py
from __future__ import print_function
import sys
import libvirt
filename = '/var/lib/libvirt/save/demo-guest.img'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
if id = conn.restore(filename) < 0:
    print('Unable to restore guest from '+filename,
          file=sys.stderr)
    exit(1)
dom = conn.lookupByID(id);
if dom == None:
    print('Cannot find guest that was restored', file=sys.stderr)
    exit(1)
print('Guest state restored from '+filename, file=sys.stderr)
conn.close()
exit(0)
Listing 4-31

Restoring a Guest Domain

../images/478111_1_En_4_Chapter/478111_1_En_4_Figb_HTML.gif Restoring a guest domain does not update the domain’s current date/time.

When a guest domain is restored, it is returned to the same state when it was saved. This will include the date and time when the domain was saved. The guest domain usually will not be able to determine that a time period has passed since it was saved. This means the current time will not be automatically updated either during or after the restore operation.

This warning is still valid even if NTP is configured.

In addition to the restore method, the alternative method restoreFlags is available.

Migrating

Migration is the process of taking the image of a guest domain and moving it somewhere, typically from a hypervisor on one node to a hypervisor on another node. There are two methods for migration. The migrate method takes an established hypervisor connection and instructs the domain to migrate to this connection. The migrateToUri method takes a URI specifying a hypervisor connection, opens the connection, and then instructs the domain to migrate to this connection. Both these methods can be passed a parameter to specify a live migration. For migration to complete successfully, storage needs to be shared between the source and target hypervisors.

The first parameter of the migrate method specifies the connection to be used to the target of the migration. This parameter is required.

The second parameter of the migrate method specifies a set of flags that control how the migration takes place over the connection. If no flags are needed, then the parameter should be set to zero.

Flags may be one of more of the following:

VIR_MIGRATE_LIVE

VIR_MIGRATE_PEER2PEER

VIR_MIGRATE_TUNNELLED

VIR_MIGRATE_PERSIST_DEST

VIR_MIGRATE_UNDEFINE_SOURCE

VIR_MIGRATE_PAUSED

VIR_MIGRATE_NON_SHARED_DISK

VIR_MIGRATE_NON_SHARED_INC

VIR_MIGRATE_CHANGE_PROTECTION

VIR_MIGRATE_UNSAFE

VIR_MIGRATE_OFFLINE

The third parameter of the migrate method specifies a new name for the domain on the target of the migration. Not all hypervisors support this operation. If no rename of the domain is required, then the parameter should be set to None.

The fourth parameter of the migrate method specifies the URI to be used as the target of the migration. A URI is required only when the target system supports multiple hypervisors. If there is only a single hypervisor on the target system, then the parameter can be set to None.

The fifth and last parameter of the migrate method specifies the bandwidth in MiB/s to be used. If this maximum is not needed, then set the parameter to zero.

Migration is tricky if the domain is running and in use. The user may experience delays, or even a dropped connection in the case of a remote migration. You should ensure that the domain is at least not being used if it is running to make sure the users are not impacted.

To migrate a guest domain to a connection that is already open, use the migrate method. Listing 4-32 shows how to migrate a domain.
# Example-22.py
from __future__ import print_function
import sys
import libvirt
domName = 'Fedora22-x86_64-1'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dest_conn = libvirt.open('qemu+ssh://desthost/system')
if dest_conn == None:
    print('Failed to open connection to qemu+ssh://desthost/system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByName(domName)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
new_dom = dom.migrate(dest_conn, 0, None, None, 0)
if new_dom == None:
    print('Could not migrate to the new domain', file=sys.stderr)
    exit(1)
print('Domain was migrated successfully.', file=sys.stderr)
destconn.close()
conn.close()
exit(0)
Listing 4-32

Migrate a Domain to an Open Connection

The migrateToURI method is similar except that the destination URI is the first parameter instead of an existing connection. To migrate a guest domain to a URI, use the migrateToURI method. Listing 4-33 shows how to migrate a domain to a URI.
# Example-23.py
from __future__ import print_function
import sys
import libvirt
domName = 'Fedora22-x86_64-1'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByName(domName)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
new_dom = dom.migrateToURI('qemu+ssh://desthost/system', 0, None, 0)
if new_dom == None:
    print('Could not migrate to the new domain', file=sys.stderr)
    exit(1)
print('Domain was migrated successfully.', file=sys.stderr)
conn.close()
exit(0)
Listing 4-33

Migrate a Domain to a URI

To migrate a live guest domain to a URI, use migrate or migrateToURI with the VIR_MIGRATE_LIVE flag set. Listing 4-34 shows how to migrate a live (running) domain to a URI.
# Example-24.py
from __future__ import print_function
import sys
import libvirt
domName = 'Fedora22-x86_64-1'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dest_conn = libvirt.open('qemu+ssh://desthost/system')
if conn == None:
    print('Failed to open connection to qemu+ssh://desthost/system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByID(6)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
new_dom = dom.migrate(dest_conn, libvirt.VIR_MIGRATE_LIVE, None,
                      None, 0)
if new_dom == None:
    print('Could not migrate to the new domain', file=sys.stderr)
    exit(1)
print('Domain was migrated successfully.', file=sys.stderr)
destconn.close()
conn.close()
exit(0)
Listing 4-34

Migrate a Live Domain to a URI

In addition to the migrate method , there are the alternative methods called migrate2, migrate3, migrateToURI, migrateToURI2, and migrateToURI3 to migrate over other types of connections.

Autostarting

A guest domain can be configured to autostart on a particular hypervisor, either by the hypervisor itself or by libvirt. In combination with a managed save, this allows the operating system on a guest domain to withstand host reboots without ever considering itself to have rebooted. When libvirt restarts, the guest domain will be automatically restored. This is handled by an API separate from regular save and restore operations because the paths must be known to libvirt without user input. Listing 4-35 shows how to set a domain to autostart when the main host is booted.
# Example-25.py
from __future__ import print_function
import sys
import libvirt
domName = 'Fedora22-x86_64-1'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByID(6)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
dom.setAutostart(1)  # turn on autostart
conn.close()
exit(0)
Listing 4-35

Set Autostart for a Domain

Domain Configuration

Domains are defined in libvirt using XML. Everything related only to the domain, such as memory and CPU, is defined in the domain XML. The domain XML format is specified at http://libvirt.org/formatdomain.html . This can be accessed locally in /usr/share/doc/libvirt-devel-version/ if your system has the libvirt-devel package installed.

Boot Modes

Booting via the BIOS is available for hypervisors supporting full virtualization. In this case, the BIOS has a boot order priority (floppy, hard disk, CD-ROM, network) that determines where to obtain/find the boot image (Listing 4-36).
 ...
  <os>
    <type>hvm</type>
    <loader readonly="yes" type="rom">
        /usr/lib/xen/boot/hvmloader
    </loader>
    <nvram template='/usr/share/OVMF/OVMF_VARS.fd'>
        /var/lib/libvirt/nvram/guest_VARS.fd
    </nvram>
    <boot dev="hd"/>
    <boot dev="cdrom"/>
    <bootmenu enable="yes" timeout="3000"/>
    <smbios mode="sysinfo"/>
    <bios useserial="yes" rebootTimeout="0"/>
  </os>
  ...
Listing 4-36

Setting the Boot Mode

Memory/CPU Resources

CPU and memory resources can be set at the time the domain is created or dynamically while the domain is either active or inactive.

CPU resources are set at domain creation time using tags in the XML definition of the domain. The hypervisor defines a limit on the number of virtual CPUs that may not be exceeded either at domain creation time or at a later time. This maximum can be dependent on a number of resource and hypervisor limits. An example of the CPU XML specification follows:
<domain>
  ...
  <vcpu placement="static" cpuset="1-4,^3,6" current="1">2</vcpu>
  ...
</domain>
Memory resources are also set at domain creation using tags in the XML definition of the domain. Both the maximum and current allocations of memory to the domain should be set. An example of the memory XML specification follows:
<domain>
  ...
  <maxMemory slots="16" unit="KiB">1524288</maxMemory>
  <memory unit="KiB">524288</memory>
  <currentMemory unit="KiB">524288</currentMemory>
  ...
</domain>
After the domain has been created, the number of virtual CPUs can be increased via the setVcpus or setVcpusFlags method. The number of CPUs may not exceed the hypervisor maximum discussed earlier. Listing 4-37 sets the maximum number of virtual CPUs for the domain. This can be done for both inactive and active domains. If the domain is active, then the new number will not be effective until the domain reboots.
# Example-29.py
from __future__ import print_function
import sys
import libvirt
domName = 'Fedora22-x86_64-1'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByID(6)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
dom.setVcpus(2)
conn.close()
exit(0)
Listing 4-37

Set the Number of Maximum Virtual CPUs for a Domain

Also, after the domain has been created, the amount of memory can be changed via the setMemory or setMemoryFlags method. The amount of memory should be expressed in kilobytes. Listing 4-38 sets the maximum amount of memory for a domain.
# Example-30.py
from __future__ import print_function
import sys
import libvirt
domName = 'Fedora22-x86_64-1'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByName(domName)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
dom.setMemory(4096) # 4 GigaBytes
conn.close()
exit(0)
Listing 4-38

Set the Amount of Memory for a Domain

In addition to the setMemory method, the alternative method setMemoryFlags is available.

Monitoring Performance

Statistical metrics are available for monitoring the utilization rates of domains, vCPUs, memory, block devices, and network interfaces.

Domain Block Device Performance

Disk usage statistics are provided by the blockStats method . Listing 4-39 shows how to fetch the statistics for a running domain. Note that the statistics can be returned for an inactive domain.
# Example-31.py
from __future__ import print_function
import sys
import libvirt
domName = 'Fedora22-x86_64-1'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByID(6)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
rd_req, rd_bytes, wr_req, wr_bytes, err =
dom.blockStats('/path/to/linux-0.2.img')
print('Read requests issued:  '+str(rd_req))
print('Bytes read:            '+str(rd_bytes))
print('Write requests issued: '+str(wr_req))
print('Bytes written:         '+str(wr_bytes))
print('Number of errors:      '+str(err))
conn.close()
exit(0)
Listing 4-39

Get the Disk Block I/O Statistics

The returned tuple contains the number of read (write) requests issued and the actual number of bytes transferred. A block device is specified by the image file path or the device bus name set by the devices/disk/target[@dev] element in the domain XML.

In addition to the blockStats method, the alternative method blockStatsFlags is available.

vCPU Performance

To obtain the individual VCPU statistics, use the getCPUStats method . Listing 4-40 shows how to display the CPU statistics.
# Example-33.py
from __future__ import print_function
import sys
import libvirt
domName = 'Fedora22-x86_64-1'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByID(5)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
cpu_stats = dom.getCPUStats(False)
for (i, cpu) in enumerate(cpu_stats):
   print('CPU '+str(i)+' Time: '+
         str(cpu['cpu_time'] / 1000000000.))
conn.close()
exit(0)
Listing 4-40

Get the Individual CPU Statistics

getCPUStats takes one parameter, a Boolean. When False is used, the statistics are reported as an aggregate of all the CPUs. When True is used, then each CPU reports its individual statistics. Either way, a list is returned. The statistics are reported in nanoseconds. If a host has four CPUs, there will be four entries in the cpu_stats list.

getCPUStats(True) aggregates the statistics for all CPUs on the host. Listing 4-41 displays the aggregate statistics for a domain.
# Example-34.py
from __future__ import print_function
import sys
import libvirt
domName = 'Fedora22-x86_64-1'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByID(5)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
stats = dom.getCPUStats(True)
print('cpu_time:    '+str(stats[0]['cpu_time']))
print('system_time: '+str(stats[0]['system_time']))
print('user_time:   '+str(stats[0]['user_time']))
conn.close()
exit(0)
Listing 4-41

Get the Aggregate CPU Statistics

Memory Statistics

To obtain the amount of memory currently used by the domain, you can use the memoryStats method . Listing 4-42 shows how to display the memory usage of a domain. Note that different kinds of memory are displayed including swap usage.
# Example-35.py
from __future__ import print_function
import sys
import libvirt
domName = 'Fedora22-x86_64-1'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByID(5)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
stats  = dom.memoryStats()
print('memory used:')
for name in stats:
    print('  '+str(stats[name])+' ('+name+')')
conn.close()
exit(0)
Listing 4-42

Get the Memory Statistics

Note that memoryStats returns a dictionary object. This object will contain a variable number of entries depending on the hypervisor and guest domain capabilities.

I/O Statistics

To get the network statistics, you’ll need the name of the host interface that the domain is connected to (usually vnetX). To find it, retrieve the domain XML description (libvirt modifies it at runtime). Then, look for the devices/interface/target[@dev] elements. Listing 4-43 shows how to display the I/O statistics for the domain.
# Example-32.py
from __future__ import print_function
import sys
import libvirt
from xml.etree import ElementTree
domName = 'Fedora22-x86_64-1'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByID(5)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
tree = ElementTree.fromstring(dom.XMLDesc())
iface = tree.find('devices/interface/target').get('dev')
stats = dom.interfaceStats(iface)
print('read bytes:    '+str(stats[0]))
print('read packets:  '+str(stats[1]))
print('read errors:   '+str(stats[2]))
print('read drops:    '+str(stats[3]))
print('write bytes:   '+str(stats[4]))
print('write packets: '+str(stats[5]))
print('write errors:  '+str(stats[6]))
print('write drops:   '+str(stats[7]))
conn.close()
exit(0)
Listing 4-43

Get the Network I/O Statistics

The interfaceStats method returns the number of bytes (packets) received (transmitted) and the number of reception/transmission errors.

Device Configuration

Configuration information for a guest domain can be obtained by using the XMLDesc method. This method returns the current description of a domain as an XML data stream. This stream can then be parsed to obtain detailed information about the domain and all the parts that make up the domain.

The flags parameter may contain any number of the following constants:

VIR_DOMAIN_XML_SECURE

VIR_DOMAIN_XML_INACTIVE

VIR_DOMAIN_XML_UPDATE_CPU

VIR_DOMAIN_XML_MIGRATABLE

Listing 4-44 shows how to display the domain’s XML information.
# Example-36.py
from __future__ import print_function
import sys
import libvirt
from xml.dom import minidom
domName = 'Fedora22-x86_64-1'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByID(5)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
raw_xml = dom.XMLDesc(0)
xml = minidom.parseString(raw_xml)
domainTypes = xml.getElementsByTagName('type')
for domainType in domainTypes:
    print(domainType.getAttribute('machine'))
    print(domainType.getAttribute('arch'))
conn.close()
exit(0)
Listing 4-44

Get Basic Domain Information from the Domain’s XML Description

Emulator

To discover the guest domain’s emulator , find and display the content of the emulator XML tag. Listing 4-45 shows how to display the emulator the domain is using. There are a number of possibilities for this, but they are too numerous to list here.
# Example-37.py
from __future__ import print_function
import sys
import libvirt
from xml.dom import minidom
domName = 'Fedora22-x86_64-1'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByID(5)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
raw_xml = dom.XMLDesc(0)
xml = minidom.parseString(raw_xml)
domainEmulator = xml.getElementsByTagName('emulator')
print('emulator: '+domainEmulator[0].firstChild.data)
conn.close()
exit(0)
Listing 4-45

Get the Domain’s Emulator Information

Listing 4-46 shows the XML configuration for the emulator.
<domain type="kvm">
    ...
    <emulator>/usr/libexec/qemu-kvm</emulator>
    ...
</domain>
Listing 4-46

Domain Emulator XML Information

Disks

To discover the guest domain’s disk (or disks), find and display the content of the disk XML tag (or tags). Listing 4-47 displays all the configured disks available to the domain.
# Example-39.py
from __future__ import print_function
import sys
import libvirt
from xml.dom import minidom
domName = 'Fedora22-x86_64-1'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByID(1)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
raw_xml = dom.XMLDesc(0)
xml = minidom.parseString(raw_xml)
diskTypes = xml.getElementsByTagName('disk')
for diskType in diskTypes:
    print('disk: type='+diskType.getAttribute('type')+' device='+
          diskType.getAttribute('device'))
    diskNodes = diskType.childNodes
    for diskNode in diskNodes:
        if diskNode.nodeName[0:1] != '#':
            print('  '+diskNode.nodeName)
            for attr in diskNode.attributes.keys():
                print('    '+diskNode.attributes[attr].name+' = '+
                 diskNode.attributes[attr].value)
conn.close()
exit(0)
Listing 4-47

Get the Domain’s Disk Information

Listing 4-48 shows the XML configuration for disks.
<domain type="kvm">
    ...
    <disk type="file" device="disk">
      <driver name="qemu" type="qcow2" cache="none"/>
      <source file='/var/lib/libvirt/images/RHEL7.1-x86_64-1.img'/>
      <target dev="vda" bus="virtio"/>
      <address type="pci" domain="0x0000" bus="0x00" slot="0x06"
               function='0x0'/>
    </disk>
    <disk type="file" device="cdrom">
      <driver name="qemu" type="raw"/>
      <target dev="hdc" bus="ide"/>
      <readonly/>
      <address type="drive" controller="0" bus="1" target="0"
               unit='0'/>
    </disk>
    ...
</domain>
Listing 4-48

Domain Disk XML Information

Networking

To discover the guest domain’s network interfaces, find and display the interface XML tag. Listing 4-49 lists all the network interfaces for a domain.
# Listing-38.py
from __future__ import print_function
import sys
import libvirt
from xml.dom import minidom
domName = 'Fedora22-x86_64-1'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByID(1)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
raw_xml = dom.XMLDesc(0)
xml = minidom.parseString(raw_xml)
interfaceTypes = xml.getElementsByTagName('interface')
for interfaceType in interfaceTypes:
    print('interface: type='+interfaceType.getAttribute('type'))
    interfaceNodes = interfaceType.childNodes
    for interfaceNode in interfaceNodes:
        if interfaceNode.nodeName[0:1] != '#':
            print('  '+interfaceNode.nodeName)
            for attr in interfaceNode.attributes.keys():
                print('    '+interfaceNode.attributes[attr].name+' = '+
                 interfaceNode.attributes[attr].value)
conn.close()
exit(0)
Listing 4-49

Get the Domain’s Network Interface Information

Listing 4-50 shows the XML configuration for network interfaces.
<domain type="kvm">
    ...
    <interface type="network">
      <mac address='52:54:00:94:f0:a4'/>
      <source network="default"/>
      <model type="virtio"/>
      <address type="pci" domain="0x0000" bus="0x00"
               slot='0x03' function="0x0"/>
    </interface>
    ...
</domain>
Listing 4-50

Domain Network Interface XML Information

Mice, Keyboard, and Tablets

To discover the guest domain’s input devices, find and display the input XML tags. Listing 4-51 shows how to list the XML information concerning mice, keyboards, and tablets.
# Example-40.py
from __future__ import print_function
import sys
import libvirt
from xml.dom import minidom
domName = 'Fedora22-x86_64-1'
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = conn.lookupByID(1)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)
raw_xml = dom.XMLDesc(0)
xml = minidom.parseString(raw_xml)
devicesTypes = xml.getElementsByTagName('input')
for inputType in devicesTypes:
    print('input: type='+inputType.getAttribute('type')+' bus='+
          inputType.getAttribute('bus'))
    inputNodes = inputType.childNodes
    for inputNode in inputNodes:
        if inputNode.nodeName[0:1] != '#':
            print('  '+inputNode.nodeName)
            for attr in inputNode.attributes.keys():
                print('    '+inputNode.attributes[attr].name+' = '+
                 inputNode.attributes[attr].value)
conn.close()
exit(0)
Listing 4-51

Get the Domain’s Input Device Information

Listing 4-52 shows the XML configuration for mouse, keyboard, and tablet.
<domain type="kvm">
    ...
    <input type="tablet" bus="usb"/>
    <input type="mouse" bus="ps2"/>
    ...
</domain>
Listing 4-52

Domain Mouse, Keyboard, and Tablet XML Information

USB Device Passthrough

The USB device passthrough capability allows a physical USB device from the host machine to be assigned directly to a guest machine. The guest OS drivers can use the device hardware directly without relying on any driver capabilities from the host OS.

../images/478111_1_En_4_Chapter/478111_1_En_4_Figc_HTML.gifImportant

USB devices are inherited by the guest domain only at boot time. Newly activated USB devices cannot be inherited from the host after the guest domain has booted.

Some caveats apply when using USB device passthrough. When a USB device is directly assigned to a guest, migration will not be possible without first hot-unplugging the device from the guest. In addition, libvirt does not guarantee that direct device assignment is secure, leaving security policy decisions to the underlying virtualization technology.

PCI Device Passthrough

The PCI device passthrough capability allows a physical PCI device from the host machine to be assigned directly to a guest machine. The guest OS drivers can use the device hardware directly without relying on any driver capabilities from the host OS.

Some caveats apply when using PCI device passthrough. When a PCI device is directly assigned to a guest, migration will not be possible without first hot-unplugging the device from the guest. In addition, libvirt does not guarantee that direct device assignment is secure, leaving security policy decisions to the underlying virtualization technology. Secure PCI device passthrough typically requires special hardware capabilities, such as the VT-d feature for Intel chipset, or the IOMMU for AMD chipsets.

There are two modes in which a PCI device can be attached, managed or unmanaged mode, although at time of writing only KVM supports managed mode attachment. In managed mode, the configured device will be automatically detached from the host OS drivers when the guest is started and then re-attached when the guest shuts down. In unmanaged mode, the device must be explicitly detached ahead of booting the guest. The guest will refuse to start if the device is still attached to the host OS. The libvirt Node Device APIs provide a means to detach/reattach PCI devices from/to host drivers. Alternatively, the host OS may be configured to blacklist the PCI devices used for guests so that they never get attached to host OS drivers.

In both modes, the virtualization technology will always perform a reset on the device before starting a guest and after the guest shuts down. This is critical to ensure isolation between the host and guest OSs. There are a variety of ways in which a PCI device can be reset. Some reset techniques are limited in scope to a single device/function, while others may affect multiple devices at once. In the latter case, it will be necessary to co-assign all affected devices to the same guest; otherwise, a reset will be impossible to do safely. The Node Device APIs can be used to determine whether a device needs to be co-assigned by manually detaching the device and then attempting to perform the reset operation. If this succeeds, then it will be possible to assign the device to a guest on its own. If it fails, then it will be necessary to co-assign the device with others on the same PCI bus.

A PCI device is attached to a guest using the hostdevice element. The mode attribute should always be set to subsystem, and the type attribute should be set to pci. The managed attribute can be either yes or no as required by the application. Within the hostdevice element there is a source element, and within that a further address element is used to specify the PCI device to be attached. The address element expects attributes for domain, bus, slot, and function. This is easiest to see with the short example in Listing 4-53.
<hostdev mode="subsystem" type="pci" managed="yes">
  <source>
    <address domain="0x0000"
             bus='0x06'
             slot='0x12'
             function='0x5'/>
  </source>
</hostdev>
Listing 4-53

Get the Domain’s Input Device Information

Block Device Jobs

libvirt provides generic block job methods that can be used to initiate and manage operations on disks that belong to a domain. Jobs are started by calling the function associated with the desired operation (e.g., blockPull). Once started, all block jobs are managed in the same manner. They can be aborted, throttled, and queried. Upon completion, an asynchronous event is issued to indicate the final status.

The following block jobs can be started:
  • blockPull starts a block pull operation for the specified disk. This operation is valid only for specially configured disks. blockPull will populate a disk image with data from its backing image. Once all data from its backing image has been pulled, the disk no longer depends on a backing image.

  • A disk can be queried for active block jobs by using blockJobInfo. If found, job information is reported in a structure that contains the job type, bandwidth throttling setting, and progress information.

  • Use virDomainBlockJobAbort() to cancel the active block job on the specified disk.

  • Use blockJobSetSpeed() to limit the amount of bandwidth that a block job may consume. Bandwidth is specified in units of MB/sec. Listing 4-54 displays the domain’s input device information.

# Example-40.py
from __future__ import print_function
import sys
import libvirt
domxml =
 """<domain type="kvm">
      <name>example</name>
      <memory>131072</memory>
      <vcpu>1</vcpu>
      <os>
        <type arch="x86_64" machine='pc-0.13'>hvm</type>
      </os>
      <devices>
        <disk type="file" device="disk">
          <driver name="qemu" type="qed"/>
          <source file='/var/lib/libvirt/images/example.qed' />
          <target dev="vda" bus="virtio"/>
        </disk>
      </devices>
    </domain>"""
def do_cmd (cmdline):
    status = os.system(cmdline)
    if status < 0:
        return -1
    return WEXITSTATUS(status)
def make_domain (conn):
    do_cmd("qemu-img create -f raw " +
           "/var/lib/libvirt/images/backing.qed 100M")
    do_cmd("qemu-img create -f qed -b " +
           "/var/lib/libvirt/images/backing.qed"+
           "/var/lib/libvirt/images/example.qed")
    dom = conn.createXML(domxml, 0)
    return dom
dom = None
disk = "/var/lib/libvirt/images/example.qed"
conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system',
          file=sys.stderr)
    exit(1)
dom = make_domain(conn)
if dom == None:
    print("Failed to create domain", file=sys.stderr)
    exit(1)
if dom.blockPull(disk, 0, 0) < 0:
    print("Failed to start block pull", file=sys.stderr)
    exit(1)
while (1):
    info = dom.blockJobInfo(disk, 0);
    if (info != None:
        print("BlockPull progress: %0.0f %%",
            float(100 * info.cur / info.end))
    elif info.cur == info.end):
        printf("BlockPull complete")
        break
    else:
        print("Failed to query block jobs", file=os.stderr)
        break
    time.sleep(1)
os.unlink("/var/lib/libvirt/images/backing.qed")
os.unlink("/var/lib/libvirt/images/example.qed")
if dom != NULL:
   conn.destroy(dom)
conn.close()
exit(0)
Listing 4-54

Get the Domain’s Input Device Information

Summary

In this chapter, you learned more about the concept of domains. This includes creating domains, configuring domains, setting information, returning information, and migrating domains. This chapter may require multiple reads to glean the information you require to work with domains. Therefore, this chapter has presented the information in such a way that it will be easy to refer to when you need more information or a reminder of how to perform a specific activity.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.5.239