[libvirt] Python stream callback removal

Dave Allan dallan at redhat.com
Wed Aug 10 02:28:26 UTC 2011


On Tue, Aug 09, 2011 at 10:59:02AM +0100, Daniel P. Berrange wrote:
> On Mon, Aug 08, 2011 at 06:04:50PM -0400, Dave Allan wrote:
> > I'm trying to write an example serial console implementation in python
> > (attached), but I'm having some trouble getting stream events to do
> > what I want.  The console itself works fine as long as the domain
> > stays up, but as soon as the domain shuts down the python script goes
> > into a tight loop repeatedly calling the stream event callback.
> > Debugging indicates that the stream event callback is being requested
> > to be removed, but it never actually is removed which makes me think I
> > am not properly releasing some resource, but I was under the
> > impression that an error on a stream resulting in the stream aborting
> > was supposed to free all the resources for me.  Is that not correct?
> 
> No where in your code do you ever invoke eventRemoveCallback.
> When the stream is "aborted" this just means that libvirtd
> has released server side resource & reported the error back
> to the client. You still have to remove your event callbacks
> otherwise you'll just be invoked forever. See tools/console.c
> for example code doing what you're attempting, but in C.
> In particular the places which call virConsoleShutdown.
> Your code in Python should  basically be a straight conversion
> of tools/console.c from C into Python.

Thanks, that was the problem; calling remove fixed it.

What I'm trying to do is to write a console that does not exit when
the domain is down, and the code is now working, at least for a short
while.  However, I am seeing a strange behavior.  After the domain has
been powered off twice--regardless of whether the domain was started
or stopped when the console program is started--when starting the
domain the next time the console hangs and no callbacks are called.  I
attached to the process with gdb and the backtraces are very different
when the process is responsive vs. when it is ok.

Any ideas on what's going wrong?

Dave
-------------- next part --------------
When ok:
(gdb) bt
#0  0x00000030f2ad7248 in poll () from /lib64/libc.so.6
#1  0x00007ffd47608f4a in virEventPollRunOnce () at util/event_poll.c:594
#2  0x00007ffd47607ba5 in virEventRunDefaultImpl () at util/event.c:247
#3  0x00007ffd47a89f8e in libvirt_virEventRunDefaultImpl (self=<value optimized out>, args=<value optimized out>)
    at libvirt.c:3598
#4  0x00000030feae965b in PyEval_EvalFrameEx () from /usr/lib64/libpython2.7.so.1.0
#5  0x00000030feaea71d in PyEval_EvalFrameEx () from /usr/lib64/libpython2.7.so.1.0
#6  0x00000030feaeb04d in PyEval_EvalCodeEx () from /usr/lib64/libpython2.7.so.1.0
#7  0x00000030feaeb162 in PyEval_EvalCode () from /usr/lib64/libpython2.7.so.1.0
#8  0x00000030feb0639c in ?? () from /usr/lib64/libpython2.7.so.1.0
#9  0x00000030feb071d0 in PyRun_FileExFlags () from /usr/lib64/libpython2.7.so.1.0
#10 0x00000030feb07daf in PyRun_SimpleFileExFlags () from /usr/lib64/libpython2.7.so.1.0
#11 0x00000030feb198ce in Py_Main () from /usr/lib64/libpython2.7.so.1.0
#12 0x00000030f2a1ee5d in __libc_start_main () from /lib64/libc.so.6
#13 0x0000000000400649 in _start ()


When unresponsive:
#0  0x00000030f2ad7248 in poll () from /lib64/libc.so.6
#1  0x00007ffd476d259f in virNetClientIOEventLoop (client=0x7ffd47107010, thiscall=0xb5b5d0) at rpc/virnetclient.c:884
#2  0x00007ffd476d3adc in virNetClientIO (client=0x7ffd47107010, msg=<value optimized out>, expectReply=true)
    at rpc/virnetclient.c:1121
#3  virNetClientSend (client=0x7ffd47107010, msg=<value optimized out>, expectReply=true) at rpc/virnetclient.c:1196
#4  0x00007ffd476d4348 in virNetClientProgramCall (prog=0xb27d90, client=0x7ffd47107010, serial=11, proc=212, 
    args_filter=0x7ffd476d1110 <xdr_remote_domain_get_state_args>, args=0x7fff888267a0, 
    ret_filter=0x7ffd476d1180 <xdr_remote_domain_get_state_ret>, ret=0x7fff88826790) at rpc/virnetclientprogram.c:291
#5  0x00007ffd476b5049 in call (priv=0xb8a300, flags=<value optimized out>, proc_nr=212, 
    args_filter=0x7ffd476d1110 <xdr_remote_domain_get_state_args>, args=<value optimized out>, 
    ret_filter=<value optimized out>, ret=0x7fff88826790 "", conn=<value optimized out>) at remote/remote_driver.c:4039
#6  0x00007ffd476bc228 in remoteDomainGetState (domain=0xb139e0, state=0x7fff888268bc, reason=0x7fff888268b8, flags=0)
    at remote/remote_driver.c:1656
#7  0x00007ffd4768b4b7 in virDomainGetState (domain=0xb139e0, state=0x7fff888268bc, reason=0x7fff888268b8, flags=0)
    at libvirt.c:3655
#8  0x00007ffd47a808f5 in libvirt_virDomainGetState (self=<value optimized out>, args=<value optimized out>)
    at libvirt-override.c:1712
#9  0x00000030feae965b in PyEval_EvalFrameEx () from /usr/lib64/libpython2.7.so.1.0
#10 0x00000030feaea71d in PyEval_EvalFrameEx () from /usr/lib64/libpython2.7.so.1.0
#11 0x00000030feaea71d in PyEval_EvalFrameEx () from /usr/lib64/libpython2.7.so.1.0
#12 0x00000030feaeb04d in PyEval_EvalCodeEx () from /usr/lib64/libpython2.7.so.1.0
#13 0x00000030fea71c62 in ?? () from /usr/lib64/libpython2.7.so.1.0
#14 0x00000030fea48fc3 in PyObject_Call () from /usr/lib64/libpython2.7.so.1.0
#15 0x00000030fea5a65f in ?? () from /usr/lib64/libpython2.7.so.1.0
#16 0x00000030fea48fc3 in PyObject_Call () from /usr/lib64/libpython2.7.so.1.0
#17 0x00000030fea490ab in ?? () from /usr/lib64/libpython2.7.so.1.0
#18 0x00000030fea4935b in PyObject_CallMethod () from /usr/lib64/libpython2.7.so.1.0
#19 0x00007ffd47a82082 in libvirt_virConnectDomainEventCallback (conn=<value optimized out>, dom=<value optimized out>, 
    event=2, detail=0, opaque=0x7ffd47ccdb90) at libvirt-override.c:3210
#20 0x00007ffd4764f0c6 in virDomainEventDispatchDefaultFunc (conn=0xb02620, event=0xbbdf90, 
    cb=0x7ffd47a81f30 <libvirt_virConnectDomainEventCallback>, cbopaque=0x7ffd47ccdb90, opaque=<value optimized out>)
    at conf/domain_event.c:1023
#21 0x00007ffd476b48fa in remoteDomainEventDispatchFunc (conn=0xb02620, event=0xbbdf90, 
    cb=0x7ffd47a81f30 <libvirt_virConnectDomainEventCallback>, cbopaque=0x7ffd47ccdb90, opaque=0xb8a300)
    at remote/remote_driver.c:4063
#22 0x00007ffd4764f26f in virDomainEventDispatch (event=0xbbdf90, callbacks=0xa52ac0, dispatch=<value optimized out>, 
    opaque=0xb8a300) at conf/domain_event.c:1136
#23 0x00007ffd4764f30a in virDomainEventQueueDispatch (queue=0x7fff888273e0, callbacks=0xa52ac0, 
    dispatch=0x7ffd476b4870 <remoteDomainEventDispatchFunc>, opaque=0xb8a300) at conf/domain_event.c:1153
#24 0x00007ffd4764f4de in virDomainEventStateFlush (state=0xb5ad00, 
    dispatchFunc=0x7ffd476b4870 <remoteDomainEventDispatchFunc>, opaque=0xb8a300) at conf/domain_event.c:1195
#25 0x00007ffd476b4845 in remoteDomainEventQueueFlush (timer=<value optimized out>, opaque=0xb02620)
    at remote/remote_driver.c:4077
#26 0x00007ffd47609116 in virEventPollDispatchTimeouts () at util/event_poll.c:421
#27 virEventPollRunOnce () at util/event_poll.c:607
#28 0x00007ffd47607ba5 in virEventRunDefaultImpl () at util/event.c:247
#29 0x00007ffd47a89f8e in libvirt_virEventRunDefaultImpl (self=<value optimized out>, args=<value optimized out>)
    at libvirt.c:3598
#30 0x00000030feae965b in PyEval_EvalFrameEx () from /usr/lib64/libpython2.7.so.1.0
#31 0x00000030feaea71d in PyEval_EvalFrameEx () from /usr/lib64/libpython2.7.so.1.0
#32 0x00000030feaeb04d in PyEval_EvalCodeEx () from /usr/lib64/libpython2.7.so.1.0
#33 0x00000030feaeb162 in PyEval_EvalCode () from /usr/lib64/libpython2.7.so.1.0
#34 0x00000030feb0639c in ?? () from /usr/lib64/libpython2.7.so.1.0
#35 0x00000030feb071d0 in PyRun_FileExFlags () from /usr/lib64/libpython2.7.so.1.0
#36 0x00000030feb07daf in PyRun_SimpleFileExFlags () from /usr/lib64/libpython2.7.so.1.0
#37 0x00000030feb198ce in Py_Main () from /usr/lib64/libpython2.7.so.1.0
#38 0x00000030f2a1ee5d in __libc_start_main () from /lib64/libc.so.6
#39 0x0000000000400649 in _start ()

-------------- next part --------------
#!/usr/bin/python -u
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, right?
    # if (error[0] == libvirt.VIR_ERR_RPC and
    #    error[1] == libvirt.VIR_FROM_STREAMS):
    #    return
    logging.warn(error)

class Console(object):
    def __init__(self, uri, uuid):
        self.uri = uri
        self.uuid = uuid
        self.connection = libvirt.open(uri)
        self.domain = self.connection.lookupByUUIDString(uuid)
        self.state = self.domain.state(0)
        self.connection.domainEventRegister(lifecycle_callback, self)
        self.stream = None
        self.run_console = True
        logging.info("%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 console.stream == None:
            console.stream = console.connection.newStream(libvirt.VIR_STREAM_NONBLOCK)
            console.domain.openConsole(None, console.stream, 0)
            console.stream.eventAddCallback(libvirt.VIR_STREAM_EVENT_READABLE, stream_callback, console)
    else:
        if console.stream:
            console.stream.eventRemoveCallback()
            console.stream = None

    return console.run_console

def stdin_callback(watch, fd, events, console):
    readbuf = os.read(fd, 1024)
    if readbuf.startswith(""):
        console.run_console = False
        return
    if console.stream:
        console.stream.send(readbuf)

def stream_callback(stream, events, console):
    try:
        received_data = console.stream.recv(1024)
    except:
        return
    os.write(0, received_data)

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

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

logging.basicConfig(filename='msg.log', level=logging.DEBUG)
logging.info("URI: %s", uri)
logging.info("UUID: %s", uuid)

libvirt.virEventRegisterDefaultImpl()
libvirt.registerErrorHandler(error_handler, None)

atexit.register(reset_term)
attrs = termios.tcgetattr(0)
tty.setraw(0)

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

while check_console(console):
    libvirt.virEventRunDefaultImpl()


More information about the libvir-list mailing list