[libvirt] Help needed: simple python api to obtain events

Dan Kenigsberg danken at redhat.com
Tue Oct 6 17:04:29 UTC 2009


Would someone help me have a shrink-wrapped solution for obtaining libvirt
events in python?

I'm looking for a python module with no API calls (or maybe just a few).

I'd like to register a callback functions with
virConnect.domainEventRegister(), and want them to be called in the
context of a specialized (automatically-created?) thread. I don't mind
if the module has two API calls instead: register() and
startPollThread().

I learned that it takes me far too long to do it on my own, and that just
modifying events-python.py (attached) reaches miserable outcomes
("libvir: Domain error : invalid domain pointer in virDomainFree", and
deadlocked python:
#0  0x00000037fe40d174 in __lll_lock_wait () from /lib64/libpthread.so.0
#1  0x00000037fe408aca in _L_lock_1034 () from /lib64/libpthread.so.0
#2  0x00000037fe40898c in pthread_mutex_lock () from /lib64/libpthread.so.0
#3  0x0000003cce04db84 in remoteDomainEventFired ()
#4  0x00002b4cc96e1612 in libvirt_virEventInvokeHandleCallback ()
)

Help most welcome,

Dan.
-------------- next part --------------
#!/usr/bin/python -u
# taken from libvirt's events-python
#
# seems a bit ugly with all these globals.. let us hide it in a module and
# hope for the best.

import sys,getopt,os
import libvirt
import select
import errno

mypoll = select.poll()
TIMEOUT_MS = 10000

debug = False

# handle globals
h_fd       = 0
h_events   = 0
h_cb       = None
h_opaque   = None

# timeout globals
t_active   = 0
t_timeout  = -1
t_cb       = None
t_opaque   = None

#####################################################
# Callback Functions
#####################################################
def eventToString(event):
    eventStrings = ( "Added",
                     "Removed",
                     "Started",
                     "Suspended",
                     "Resumed",
                     "Stopped",
                     "Saved",
                     "Restored" );
    return eventStrings[event];

def myDomainEventCallback1 (conn, dom, event, detail, opaque):
    print "myDomainEventCallback1 EVENT: Domain %s(%s) %s %d" % (dom.name(), dom.ID(), eventToString(event), detail)

def myDomainEventCallback2 (conn, dom, event, detail, opaque):
    print "myDomainEventCallback2 EVENT: Domain %s(%s) %s %d" % (dom.name(), dom.ID(), eventToString(event), detail)

#####################################################
# EventImpl Functions
#####################################################
def myEventHandleTypeToPollEvent(events):
    ret = 0
    if events & libvirt.VIR_EVENT_HANDLE_READABLE:
        ret |= select.POLLIN
    if events & libvirt.VIR_EVENT_HANDLE_WRITABLE:
        ret |= select.POLLOUT
    if events & libvirt.VIR_EVENT_HANDLE_ERROR:
        ret |= select.POLLERR;
    if events & libvirt.VIR_EVENT_HANDLE_HANGUP:
        ret |= select.POLLHUP;
    return ret

def myPollEventToEventHandleType(events):
    ret = 0;
    if events & select.POLLIN:
        ret |= libvirt.VIR_EVENT_HANDLE_READABLE;
    if events & select.POLLOUT:
        ret |= libvirt.VIR_EVENT_HANDLE_WRITABLE;
    if events & select.POLLERR:
        ret |= libvirt.VIR_EVENT_HANDLE_ERROR;
    if events & select.POLLHUP:
        ret |= libvirt.VIR_EVENT_HANDLE_HANGUP;
    return ret;

def myAddHandle(fd, events, cb, opaque):
    global h_fd, h_events, h_cb, h_opaque, debug
    if debug:
        print "Adding Handle %s %s %s %s" % (str(fd), str(events), str(cb), str(opaque))
    h_fd = fd
    h_events = events
    h_cb = cb
    h_opaque = opaque
    mypoll.register(fd, myEventHandleTypeToPollEvent(events))
    return 0

def myUpdateHandle(watch, event):
    global h_fd, h_events, debug
    if debug:
        print "Updating Handle %s %s" % (str(h_fd), str(event))
    h_events = event
    mypoll.unregister(h_fd)
    mypoll.register(h_fd, myEventHandleTypeToPollEvent(event))

def myRemoveHandle(watch):
    global h_fd, debug
    if debug:
        print "Removing Handle %s" % str(h_fd)
    mypoll.unregister(h_fd)
    h_fd = 0
    return h_opaque

def myAddTimeout(timeout, cb, opaque):
    global t_active, t_timeout, t_cb, t_opaque, debug
    if debug:
        print "Adding Timeout %s %s %s" % (str(timeout), str(cb), str(opaque))
    if timeout == -1:
        t_active = 0
    else:
        t_active = 1
    t_timeout = timeout;
    t_cb = cb;
    t_opaque = opaque;
    return 0

def myUpdateTimeout(timer, timeout):
    global t_timeout, t_active, debug
    if debug:
        print "Updating Timeout %s %s" % (str(timer), str(timeout))
    if timeout == -1:
        t_active = 0
    else:
        t_active = 1
    t_timeout = timeout;

def myRemoveTimeout(timer):
    global t_active, debug
    if debug:
        print "Removing Timeout %s" % str(timer)
    t_active = 0;
    return t_opaque

##########################################
# Main
##########################################

def usage():
        print "usage: "+os.path.basename(sys.argv[0])+" [uri]"
        print "   uri will default to qemu:///system"

def main():
    try:
        opts, args = getopt.getopt(sys.argv[1:], "h", ["help"] )
    except getopt.GetoptError, err:
        # print help information and exit:
        print str(err) # will print something like "option -a not recognized"
        usage()
        sys.exit(2)
    for o, a in opts:
        if o in ("-h", "--help"):
            usage()
            sys.exit()

    if len(sys.argv) > 1:
        uri = sys.argv[1]
    else:
        uri = "qemu:///system"

    print "Using uri:" + uri

#    libvirt.virEventRegisterImpl( myAddHandle,
#                               myUpdateHandle,
#                               myRemoveHandle,
#                               myAddTimeout,
#                               myUpdateTimeout,
#                               myRemoveTimeout );
    vc = libvirt.open(uri)

    # Close connection on exit (to test cleanup paths)
    old_exitfunc = getattr(sys, 'exitfunc', None)
    def exit():
        print "Closing " + str(vc)
        vc.close()
        if (old_exitfunc): old_exitfunc()
    sys.exitfunc = exit

    #Add 2 callbacks to prove this works with more than just one
    vc.domainEventRegister(myDomainEventCallback1,None)
    vc.domainEventRegister(myDomainEventCallback2,None)

    poll()

def poll():
    while 1:
	print 'loop'
        try:
            if debug:
                print "Poll sleep %d" % t_active
            sts = mypoll.poll(TIMEOUT_MS)
        except select.error, err:
            if err[0] == errno.EINTR:
                    continue
            raise
        except KeyboardInterrupt:
            print "Keyboard Interrupt caught - exiting cleanly"
            break

        if t_cb and t_active == 1:
            if debug:
                print "Invoking Timeout CB"
            t_cb(t_timeout, t_opaque[0], t_opaque[1])

        if not sts:
            if debug:
                print "Timed out"
            continue

        rfd = sts[0][0]
        revents = sts[0][1]

        if revents & select.POLLHUP:
            print "Reset by peer";
            return -1;

        if h_cb != None:
            #print "Invoking Handle CB"
            h_cb(0, h_fd, myPollEventToEventHandleType(revents & h_events),
                 h_opaque[0], h_opaque[1])

libvirt.virEventRegisterImpl( myAddHandle,
                               myUpdateHandle,
                               myRemoveHandle,
                               myAddTimeout,
                               myUpdateTimeout,
                               myRemoveTimeout );
import threading
pollThread = threading.Thread(target=poll, name='libvirtEventPoll')
pollThread.setDaemon(True)
pollThread.start()

if __name__ == "__main__":
    main()


More information about the libvir-list mailing list