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

8. Error Handling

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

The libvirt error functions are designed to give more detailed information about what caused a failure in the case that a normal libvirt function or method returns an error. An important thing to note about Python libvirt error reporting is that errors are stored on a per-thread basis and not per connection.

The libvirtError Class

The libvirt Python module defines a standard exception class called libvirtError that can be subclassed to add additional functionality when raising a libvirt exception. Listing 8-1 shows a partial definition of the libvirtError class definition.
class libvirtError(exceptions.Exception)
    def __init__(self, defmsg, conn=None, dom=None,
                 net=None, pool=None, vol=None):
        # Never call virGetLastError().
        # virGetLastError() is now thread local
        err = virGetLastError()
        if err is None:
            msg = defmsg
        else:
            msg = err[2]
        Exception.__init__(self, msg)
        self.err = err
    def get_error_code(self):
        if self.err is None:
            return None
        return self.err[0]
    def get_error_domain(self):
        if self.err is None:
            return None
        return self.err[1]
    def get_error_message(self):
        if self.err is None:
            return None
        return self.err[2]
    def get_error_level(self):
        if self.err is None:
            return None
        return self.err[3]
    def get_str1(self):
        if self.err is None:
            return None
        return self.err[4]
    def get_str2(self):
        if self.err is None:
            return None
        return self.err[5]
    def get_str3(self):
        if self.err is None:
            return None
        return self.err[6]
    def get_int1(self):
        if self.err is None:
            return None
        return self.err[7]
    def get_int2(self):
        if self.err is None:
            return None
        return self.err[8]
Listing 8-1

libvirt Module’s libvirtError Class Definition

There are a few things to note about this definition. The first is that you can instantiate this class by using the Python raise statement. However, this is not much use unless you subclass the libvirtError class with your own class, which would contain the needed behavior. You should also make note of the methods that can obtain the error information. A description of these methods is provided here.

The method get_error_code returns the error code of the error. This is one of the data definitions from the Python libvirt module. Some of the higher-numbered entries from this list may not be available in your Python libvirt module.
VIR_ERR_OK = 0
VIR_ERR_INTERNAL_ERROR = 1    # internal error
VIR_ERR_NO_MEMORY = 2         # memory allocation failure
VIR_ERR_NO_SUPPORT = 3        # no support for this function
VIR_ERR_UNKNOWN_HOST = 4      # could not resolve hostname
VIR_ERR_NO_CONNECT = 5        # can not connect to hypervisor
VIR_ERR_INVALID_CONN = 6      # invalid connection object
VIR_ERR_INVALID_DOMAIN = 7    # invalid domain object
VIR_ERR_INVALID_ARG = 8       # invalid function argument
VIR_ERR_OPERATION_FAILED = 9  # a command to hypervisor failed
VIR_ERR_GET_FAILED = 10       # a HTTP GET command to failed
VIR_ERR_POST_FAILED = 11      # a HTTP POST command to failed
VIR_ERR_HTTP_ERROR = 12       # unexpected HTTP error code
VIR_ERR_SEXPR_SERIAL = 13     # failure to serialize an S-Expr
VIR_ERR_NO_XEN = 14           # could not open Xen hypervisor control
VIR_ERR_XEN_CALL = 15         # failure doing an hypervisor call
VIR_ERR_OS_TYPE = 16          # unknown OS type
VIR_ERR_NO_KERNEL = 17        # missing kernel information
VIR_ERR_NO_ROOT = 18          # missing root device information
VIR_ERR_NO_SOURCE = 19        # missing source device information
VIR_ERR_NO_TARGET = 20        # missing target device information
VIR_ERR_NO_NAME = 21          # missing domain name information
VIR_ERR_NO_OS = 22            # missing domain OS information
VIR_ERR_NO_DEVICE = 23        # missing domain devices information
VIR_ERR_NO_XENSTORE = 24      # could not open Xen Store control
VIR_ERR_DRIVER_FULL = 25      # too many drivers registered
VIR_ERR_CALL_FAILED = 26      # not supported by the drivers (DEPRECATED)
VIR_ERR_XML_ERROR = 27        # an XML description is not well formed or broken
VIR_ERR_DOM_EXIST = 28        # the domain already exist
VIR_ERR_OPERATION_DENIED = 29 # operation forbidden on read-only connections
VIR_ERR_OPEN_FAILED = 30      # failed to open a conf file
VIR_ERR_READ_FAILED = 31      # failed to read a conf file
VIR_ERR_PARSE_FAILED = 32        # failed to parse a conf file
VIR_ERR_CONF_SYNTAX = 33         # failed to parse the syntax of a conf file
VIR_ERR_WRITE_FAILED = 34        # failed to write a conf file
VIR_ERR_XML_DETAIL = 35          # detail of an XML error
VIR_ERR_INVALID_NETWORK = 36     # invalid network object
VIR_ERR_NETWORK_EXIST = 37       # the network already exist
VIR_ERR_SYSTEM_ERROR = 38        # general system call failure
VIR_ERR_RPC = 39                 # some sort of RPC error
VIR_ERR_GNUTLS_ERROR = 40        # error from a GNUTLS call
VIR_WAR_NO_NETWORK = 41          # failed to start network
VIR_ERR_NO_DOMAIN = 42           # domain not found or unexpectedly disappeared
VIR_ERR_NO_NETWORK = 43          # network not found
VIR_ERR_INVALID_MAC = 44         # invalid MAC address
VIR_ERR_AUTH_FAILED = 45         # authentication failed
VIR_ERR_INVALID_STORAGE_POOL = 46 # invalid storage pool object
VIR_ERR_INVALID_STORAGE_VOL = 47 # invalid storage vol object
VIR_WAR_NO_STORAGE = 48          # failed to start storage
VIR_ERR_NO_STORAGE_POOL = 49     # storage pool not found
VIR_ERR_NO_STORAGE_VOL = 50      # storage pool not found
VIR_WAR_NO_NODE = 51             # failed to start node driver
VIR_ERR_INVALID_NODE_DEVICE = 52  # invalid node device object
VIR_ERR_NO_NODE_DEVICE = 53      # node device not found
VIR_ERR_NO_SECURITY_MODEL = 54   # security model not found
VIR_ERR_OPERATION_INVALID = 55   # operation is not applicable at this time
VIR_WAR_NO_INTERFACE = 56        # failed to start interface driver
VIR_ERR_NO_INTERFACE = 57        # interface driver not running
VIR_ERR_INVALID_INTERFACE = 58   # invalid interface object
VIR_ERR_MULTIPLE_INTERFACES = 59        # more than one matching interface found
VIR_WAR_NO_NWFILTER = 60            # failed to start nwfilter driver
VIR_ERR_INVALID_NWFILTER = 61       # invalid nwfilter object
VIR_ERR_NO_NWFILTER = 62            # nw filter pool not found
VIR_ERR_BUILD_FIREWALL = 63         # nw filter pool not found
VIR_WAR_NO_SECRET = 64              # failed to start secret storage
VIR_ERR_INVALID_SECRET = 65         # invalid secret
VIR_ERR_NO_SECRET = 66              # secret not found
VIR_ERR_CONFIG_UNSUPPORTED = 67       # unsupported configuration construct
VIR_ERR_OPERATION_TIMEOUT = 68       # timeout occurred during operation
VIR_ERR_MIGRATE_PERSIST_FAILED = 69 # a migration worked, but making the VM persist on the dest host failed
VIR_ERR_HOOK_SCRIPT_FAILED = 70     # a synchronous hook script failed
VIR_ERR_INVALID_DOMAIN_SNAPSHOT = 71 # invalid domain snapshot
VIR_ERR_NO_DOMAIN_SNAPSHOT = 72     # domain snapshot was not found
VIR_ERR_INVALID_STREAM = 73         # invalid i/o stream
VIR_ERR_ARGUMENT_UNSUPPORTED = 74   # an argument was unsupported
VIR_ERR_STORAGE_PROBE_FAILED = 75   # a storage probe failed
VIR_ERR_STORAGE_POOL_BUILT = 76
VIR_ERR_SNAPSHOT_REVERT_RISKY = 77
VIR_ERR_OPERATION_ABORTED = 78      # the operation was aborted
VIR_ERR_AUTH_CANCELLED = 79
VIR_ERR_NO_DOMAIN_METADATA = 80     # no domain metadata was found
VIR_ERR_MIGRATE_UNSAFE = 81
VIR_ERR_OVERFLOW = 82              # an overflow situation was detected
VIR_ERR_BLOCK_COPY_ACTIVE = 83
VIR_ERR_OPERATION_UNSUPPORTED = 84 # the operation was unsupported
VIR_ERR_SSH = 85                 # an ssh error was detected
VIR_ERR_AGENT_UNRESPONSIVE = 86  # an agent timeout was detected
VIR_ERR_RESOURCE_BUSY = 87
VIR_ERR_ACCESS_DENIED = 88
VIR_ERR_DBUS_SERVICE = 89
VIR_ERR_STORAGE_VOL_EXIST = 90
VIR_ERR_CPU_INCOMPATIBLE = 91
VIR_ERR_XML_INVALID_SCHEMA = 92
The method get_error_domain is named as such for legacy reasons, but it really represents which part of libvirt generated the error. This is one of the data definitions from the Python libvirt module. Some of the higher-numbered entries from this list may not be available in your Python libvirt module. The full list is as follows:
VIR_FROM_NONE = 0
VIR_FROM_XEN = 1              # Error at Xen hypervisor layer
VIR_FROM_XEND = 2             # Error at connection with xend daemon
VIR_FROM_XENSTORE = 3         # Error at connection with xen store
VIR_FROM_SEXPR = 4            # Error in the S-Expression code
VIR_FROM_XML = 5              # Error in the XML code
VIR_FROM_DOM = 6              # Error when operating on a domain
VIR_FROM_RPC = 7              # Error in the XML-RPC code
VIR_FROM_PROXY = 8            # Error in the proxy code
VIR_FROM_CONF = 9             # Error in the configuration file handling
VIR_FROM_QEMU = 10            # Error at the QEMU daemon
VIR_FROM_NET = 11             # Error when operating on a network
VIR_FROM_TEST = 12            # Error from test driver
VIR_FROM_REMOTE = 13          # Error from remote driver
VIR_FROM_OPENVZ = 14          # Error from OpenVZ driver
VIR_FROM_XENXM = 15           # Error at Xen XM layer
VIR_FROM_STATS_LINUX = 16     # Error in the Linux Stats code
VIR_FROM_LXC = 17             # Error from Linux Container driver
VIR_FROM_STORAGE = 18         # Error from storage driver
VIR_FROM_NETWORK = 19         # Error from network config
VIR_FROM_DOMAIN = 20          # Error from domain config
VIR_FROM_UML = 21             # Error at the UML driver
VIR_FROM_NODEDEV = 22         # Error from node device monitor
VIR_FROM_XEN_INOTIFY = 23     # Error from xen inotify layer
VIR_FROM_SECURITY = 24        # Error from security framework
VIR_FROM_VBOX = 25            # Error from VirtualBox driver
VIR_FROM_INTERFACE = 26       # Error when operating on an interface
VIR_FROM_ONE = 27             # Error from OpenNebula driver
VIR_FROM_ESX = 28             # Error from ESX driver
VIR_FROM_PHYP = 29            # Error from IBM power hypervisor
VIR_FROM_SECRET = 30          # Error from secret storage
VIR_FROM_CPU = 31             # Error from CPU driver
VIR_FROM_XENAPI = 32          # Error from XenAPI
VIR_FROM_NWFILTER = 33        # Error from network filter driver
VIR_FROM_HOOK = 34            # Error from Synchronous hooks
VIR_FROM_DOMAIN_SNAPSHOT = 35 # Error from domain snapshot
VIR_FROM_AUDIT = 36
VIR_FROM_SYSINFO = 37
VIR_FROM_STREAMS = 38
VIR_FROM_VMWARE = 39
VIR_FROM_EVENT = 40
VIR_FROM_LIBXL = 41
VIR_FROM_LOCKING = 42
VIR_FROM_HYPERV = 43
VIR_FROM_CAPABILITIES = 44
VIR_FROM_URI = 45
VIR_FROM_AUTH = 46
VIR_FROM_DBUS = 47
VIR_FROM_PARALLELS = 48
VIR_FROM_DEVICE = 49
VIR_FROM_SSH = 50
VIR_FROM_LOCKSPACE = 51
VIR_FROM_INITCTL = 52
VIR_FROM_IDENTITY = 53
VIR_FROM_CGROUP = 54
VIR_FROM_ACCESS = 55
VIR_FROM_SYSTEMD = 56
VIR_FROM_BHYVE = 57
VIR_FROM_CRYPTO = 58
VIR_FROM_FIREWALL = 59
VIR_FROM_POLKIT = 60

The method get_error_message is a human-readable string describing the error.

The method get_error_level describes the severity of the error. This is one of the data definitions from the Python libvirt module. The full list of levels is as follows:
VIR_ERR_NONE = 0
VIR_ERR_WARNING = 1  # A simple warning
VIR_ERR_ERROR = 2    # An error

The method get_error_str1 gives extra human-readable information.

The method get_error_str2 gives extra human-readable information.

The method get_error_str3 gives extra human-readable information.

The method get_error_int1 gives extra numeric information that may be useful for further classifying the error.

The method get_error_int2 gives extra numeric information that may be useful for further classifying the error.

Example code that uses various parts of this structure will be presented in subsequent subsections.

Using virGetLastError

The virGetLastError function can be used to obtain a Python list that contains all the information from the error reported from libvirt. This information is kept in thread-local storage, so separate threads can safely use this function concurrently. Note that it does not make a copy, so error information can be lost if the current thread calls this function subsequently. Listing 8-2 demonstrates the use of virGetLastError.
# Example-24.py
from __future__ import print_function
import sys
import libvirt
def report_libvirt_error():
    """Call virGetLastError function to get the last error information."""
    err = libvirt.virGetLastError()
    print('Error code:    '+str(err[0]), file=sys.stderr)
    print('Error domain:  '+str(err[1]), file=sys.stderr)
    print('Error message: '+str(err[2]), file=sys.stderr)
    print('Error level:   '+str(err[3]), file=sys.stderr)
    if err[4] != None:
        print('Error string1: '+str(err[4]), file=sys.stderr)
    else:
        print('Error string1:', file=sys.stderr)
    if err[5] != None:
        print('Error string2: '+str(err[5]), file=sys.stderr)
    else:
        print('Error string2:', file=sys.stderr)
    if err[6] != None:
        print('Error string3: '+str(err[6]), file=sys.stderr)
    else:
        print('Error string3:', file=sys.stderr)
    print('Error int1:    '+str(err[7]), file=sys.stderr)
    print('Error int2:    '+str(err[8]), file=sys.stderr)
    exit(1)
try:
    conn = libvirt.open('qemu:///system') # invalidate the parameter to force an error
except:
    report_libvirt_error()
conn.close()
exit(0)
Listing 8-2

Using virGetLastError

Subclassing libvirtError

It is possible to subclass the libvirtError class to add functionality. The default libvirtError does not provide any ability to either save the error information or present the information to the user. Subclassing libvirtError gives the programmer the flexibility to add any functionality needed. Listing 8-3 shows an example of this.
# Example-25.py
from __future__ import print_function
import sys
import libvirt
class report_libvirt_error(libvirt.libvirtError):
    """Subclass virError to get the last error information."""
    def __init__(self, defmsg, conn=None, dom=None, net=None,
                 pool=None, vol=None):
        libvirt.libvirtError.__init__(self, defmsg, conn=None,
                                      dom=None, net=None, pool=None,
                                      vol=None)
        print('Default msg:   '+str(defmsg), file=sys.stderr)
        print('Error code:    '+str(self.get_error_code()), file=sys.stderr)
        print('Error domain:  '+str(self.get_error_domain()), file=sys.stderr)
        print('Error message: '+self.get_error_message(), file=sys.stderr)
        print('Error level:   '+str(self.get_error_level()), file=sys.stderr)
        if self.err[4] != None:
            print('Error string1: '+self.get_str1(), file=sys.stderr)
        else:
            print('Error string1:', file=sys.stderr)
        if self.err[5] != None:
            print('Error string2: '+self.get_str2(), file=sys.stderr)
        else:
            print('Error string2:', file=sys.stderr)
        if self.err[6] != None:
            print('Error string3: '+self.get_str3(), file=sys.stderr)
        else:
            print('Error string3:', file=sys.stderr)
        print('Error int1:    '+str(self.get_int1()), file=sys.stderr)
        print('Error int2:    '+str(self.get_int2()), file=sys.stderr)
        exit(1)
try:
    conn = libvirt.open('qemu:///system') # invalidate the parameter
                                       # to force an error
except libvirt.libvirtError:
    raise report_libvirt_error('Connection error')
conn.close()
exit(0)
Listing 8-3

Subclassing libvirtError

Registering an Error Handler Function

libvirt also supports setting up an error handler Python function. This is done using the libvirt function registerErrorHandler , which returns 1 in the case of success.

The registered function is called as f(ctx, error), with error being a list of information about the error being raised.

Listing 8-4 shows an example of this function.
# Example-26.py
from __future__ import print_function
import sys
import libvirt
def libvirt_error_handler(ctx, err):
    print('Error code:    '+str(err[0]), file=sys.stderr)
    print('Error domain:  '+str(err[1]), file=sys.stderr)
    print('Error message: '+err[2], file=sys.stderr)
    print('Error level:   '+str(err[3]), file=sys.stderr)
    if err[4] != None:
        print('Error string1: '+err[4], file=sys.stderr)
    else:
        print('Error string1:', file=sys.stderr)
    if err[5] != None:
        print('Error string2: '+err[5], file=sys.stderr)
    else:
        print('Error string2:', file=sys.stderr)
    if err[6] != None:
        print('Error string3: '+err[6], file=sys.stderr)
    else:
        print('Error string3:', file=sys.stderr)
    print('Error int1:    '+str(err[7]), file=sys.stderr)
    print('Error int2:    '+str(err[8]), file=sys.stderr)
    exit(1)
ctx = 'just some information'
libvirt.registerErrorHandler(libvirt_error_handler, ctx)
conn = libvirt.open('qemu:///system') # invalidate the parameter
                                     # to force an error
conn.close()
exit(0)
Listing 8-4

Registering an Error Handler

Summary

This chapter concentrated on the errors that can be raised by libvirt. You examined the default error class and saw how this class can be extended to include other information. Understanding how to use the error class is key to building a program that can withstand errors that may occur during execution and possibly include automatic error correction.

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

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