Product SiteDocumentation Site

Chapter 9. Event and Timer Handling

9.1. Event Handling
9.2. Timer Handling
The Python libvirt module provides a complete interface for handling both events and timers. Both event and timer handling are invoked through a function interface as opposed to a class/method interface. This makes it easier to integrate the interface into either a graphical or console program.

9.1. Event Handling

The Python libvirt module supplies a framework for event handling. While this is most useful for graphical programs, it can also be used for console programs to provide a consistent user interface and control the processing of console events.
Event handling is done through the functions virEventAddHandle, virEventRegisterDefaultImpl, virEventRegisterImpl, virEventRemoveHandle, virEventRunDefaultImpl, and virEventUpdateHandle.
Creating an event requires that an event loop has previously been registered with virEventRegisterImpl or virEventRegisterDefaultImpl.
An example program that uses most of these functions follows. Note that in order to interact with the guest through the terminal, a serial console needs to be enabled in the guest system. For linux hosts, this can be done like so:
sudo systemctl enable serial-getty@ttyS0.service
$ sudo systemctl start serial-getty@ttyS0.service

Example 9.1. Provide a persistent console that survives guest reboots

# consolecallback - provide a persistent console that survives guest reboots
#!/usr/bin/env python3

import sys, os, logging, libvirt, tty, termios, atexit

def reset_term():
    termios.tcsetattr(0, termios.TCSADRAIN, attrs)

def error_handler(unused, error):
    # The console stream errors on VM shutdown; we don't care
    if (error[0] == libvirt.VIR_ERR_RPC and
        error[1] == libvirt.VIR_FROM_STREAMS):

class Console(object):
    def __init__(self, uri, uuid):
        self.uri = uri
        self.uuid = uuid
        self.connection =
        self.domain = self.connection.lookupByUUIDString(uuid)
        self.state = self.domain.state(0)
        self.connection.domainEventRegister(lifecycle_callback, self) = None
        self.run_console = True"%s initial state %d, reason %d",
                     self.uuid, self.state[0], self.state[1])

def check_console(console):
    if (console.state[0] == libvirt.VIR_DOMAIN_RUNNING or
        console.state[0] == libvirt.VIR_DOMAIN_PAUSED):
        if is None:
   = console.connection.newStream(libvirt.VIR_STREAM_NONBLOCK)
            console.domain.openConsole(None,, 0)
  , stream_callback, console)
   = None

    return console.run_console

def stdin_callback(watch, fd, events, console):
    readbuf =, 1024)
    if readbuf.startswith(""):
        console.run_console = False

def stream_callback(stream, events, console):
        received_data =
    os.write(0, received_data)

def lifecycle_callback (connection, domain, event, detail, console):
    console.state = console.domain.state(0)"%s transitioned to state %d, reason %d",
                 console.uuid, console.state[0], console.state[1])

# main
if len(sys.argv) != 3:
    print("Usage:", sys.argv[0], "URI UUID")
    print("for example:", sys.argv[0], "'qemu:///system' '32ad945f-7e78-c33a-e96d-39f25e025d81'")

uri = sys.argv[1]
uuid = sys.argv[2]

print("Escape character is ^]")
logging.basicConfig(filename='msg.log', level=logging.DEBUG)"URI: %s", uri)"UUID: %s", uuid)

libvirt.registerErrorHandler(error_handler, None)

attrs = termios.tcgetattr(0)

console = Console(uri, uuid)
console.stdin_watch = libvirt.virEventAddHandle(0, libvirt.VIR_EVENT_HANDLE_READABLE, stdin_callback, console)

while check_console(console):