[libvirt] RFC: Add further domain event callbacks

Daniel P. Berrange berrange at redhat.com
Thu Mar 4 15:25:28 UTC 2010


In the current domain APIs, we currently have support for getting notified
of domain lifecycle transition events.

THis is done using two methods

  int virConnectDomainEventRegister(virConnectPtr conn,
                                    virConnectDomainEventCallback cb,
                                    void *opaque,
                                    virFreeCallback freecb);

  int virConnectDomainEventDeregister(virConnectPtr conn,
                                      virConnectDomainEventCallback cb);

This allows an app to register a callback that looks like this

  typedef int (*virConnectDomainEventCallback)(virConnectPtr conn,
                                               virDomainPtr dom,
                                               int event,
                                               int detail,
                                               void *opaque);

Where 'event' is the lifecycle transition (suspended, stopped, started, etc)
and 'detail' is the cause of he transition (pause, migration, shutdown, etc)


I have outstanding feature requests to add a lot more event notifications
to the libvirt API. In particular

 - IO Errors. Parameter 'alias' the name of the block device with the error

 - Reboot. No parameters

   You can argue whether this should be part of the lifecycle events. On
   the one hand it is not guest visible, because the guest does a internal
   machine reset without the host ever seeing a change. This is different
   from other cases where QEMU itself is stopping/starting.

 - Watchdog. Parameter 'action', saying what is going to happen to the
   guest due to this watchdog firing (ignored, shutdown, paused, etc)

 - VNC client. In fact three events, connect, authenticated and disconnect.
     
   Parameters, TCP address & port number of client, and also of the server.
   Optionally a SASL username and TLS certificate name of the authenticated
   user

 - Guest user. Logon/logoff events. 

   This requires co-operation from a guest agent, so it may not actually
   be of scope of libvirt.

 - Disk 'high watermark'. Emitted when a QCow volume grows beyond a certain
   physical allocation. THis allows an app to enlarge the underlying storage
   holding the qcow volume before an 'out of space' occurs. Parameter is
   the disk alias name.


So we can see there are events with a wide variety of parameters and we need
to figure out how to represent this in the API.


Option 1
--------

Follow the existing lifecycle event model. For each new event, add a
virConnectXXXXXEventRegister & virConnectEventDeregister method, and
typedef a new callback for them. eg

  typedef int (*virConnectDomainBlockIOEventCallback)(virConnectPtr conn,
                                                      virDomainPtr dom,
                                                      const char *diskname,
                                                      const char *alias,
                                                      void *opaque);

  int virConnectDomainBlockIOEventRegister(virConnectPtr conn,
                                           virConnectDomainBlockIOEventCallback cb,
                                           void *opaque,
                                           virFreeCallback freecb);

  int virConnectDomainBlockIOEventDeregister(virConnectPtr conn,
                                             virConnectDomainEventCallback cb);


So we'll have 2 extra APIs for every event + a new typedef. We'll also need
to add new APIs in src/conf/domain_event.h to cope with dispatch


Option 2
--------

GLib/GObject take a very loosely typed approach to registering/unregistering
events. The have a single pair of methods that work for any event & a generic
callback signature, requiring application casts.

     typedef int (*virConnectEventCallback)(void *opaque);

    int virConnectEventRegister(virConnectPtr conn,
                                const char *eventname,
                                virConnectEventCallback cb,
                                void *opaque,
                                virFreeCallback freecb);

    int virCOnnectEventUnregister(virConnectPtr conn,
                                  int eventID);

In this model, the register method returns a unique integer ID for the 
callback which can be used to unregister it. Application's using this
will still need a strongly typed callback for receiving the event, but
when calling virConnectEventRegister(), the would do an explicit 'bad'
cast to 'virConnectEventCallback'


Option 3
--------

A hybrid of both approaches. Have a new 'register' method for each type of
event that takes a strongly typed callback, but have a generic 'unregister'
method that just uses the 'int eventID'

   int virConnectDomainBlockIOEventRegister(virConnectPtr conn,
                                            virConnectDomainBlockIOEventCallback cb,
                                            void *opaque,
                                            virFreeCallback freecb);

   int virCOnnectEventUnregister(virConnectPtr conn,
                                  int eventID);



Option 4
--------

Have one pair of register/unregister events, but instead of passing diffeerent
parameters to each callback, have a generic callback that takes a single
parameter. This parameter would be declared as a union. So depending on
the type of event being received, you'd access different parts of the union


    typedef union {
       virConnectDomainBlockIOEvent blockio;
       virConnectDomainWatchdogEvent watchdog;
       ...other events...
    } virConnectEvent;


Either we could include a dummy member in the union with padding  to 1024
bytes in size for future expansion, or we could simply declare that apps
must never allocate this data type themselves, thus allowing us to enlarge
it at will.

    typedef int (*virConnectEventCallback)(int eventType, virConnectEvent, void *opaque);

    int virConnectEventRegister(virConnectPtr conn,
                                const char *eventname,
                                virConnectEventCallback cb,
                                void *opaque,
                                virFreeCallback freecb);

    int virConnectEventUnregister(virConnectPtr conn,
                                  int eventID);



There is one final question unrelated to these 4 options. For the lifecycle
events we always registered against the 'virConnectPtr' since that is 
needed to capture 'domain created' events where there's no virDomainPtr
to register a callback against yet.

Do we want to always register all events aganist the virConnectPtr, and
then pass a 'virDomainPtr' as a parameter to the callbacks as needed. Or
should we allow registering events against the virDomainPtr directly.
The latter might make it simpler to map libvirt into GLib/GObjects event
system in the future.

Daniel
-- 
|: Red Hat, Engineering, London    -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org -o- http://virt-manager.org -o- http://deltacloud.org :|
|: http://autobuild.org        -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|




More information about the libvir-list mailing list