[libvirt] RFC: Add further domain event callbacks

Daniel Veillard veillard at redhat.com
Tue Mar 16 10:16:43 UTC 2010


On Thu, Mar 04, 2010 at 03:25:28PM +0000, Daniel P. Berrange wrote:
> 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
> 

  I think it's a bit of an exageration, it's 2 extra API for every kind
of usages, where kind can be:

  - Domain
  - I/O
  - Watchdog
  - Guest Usage

  Still that's not ideal. The pro's is that the domain event stuff
is "normal". That doesn't solve the callback structure problem as new
callback will be added to existing usage as time grows. We should
probably solve that generically instead.

> 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'

  That fine to me except that for example if you want to bind to a
specific object (a domain, an interface) to catch his mutation, then
you need to pass this too, ending up with

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

that means two casts. I like the unregister based on the ID token
though, that part can be completely generic.

That still doesn't solve the opaque callback object allocation, I'm
afraid, unless libvirt itself doesn't touch anything, but then that
mean that libvirt can only pass an event, but not provide any contextual
information (any of the extra stuff we use for Domain callbacks).


> 
> 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);
> 
> 

  Doesn't solve the real problem.

> 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.

  yeah, I know that glib has been doing that padding to ensure forward
compatibility over time. I find that a bit fishy. At least that solves
the allocation problem for data passed by libvirt as part of the
callback.

>     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);
> 
> 

  I'm undecided yet between 1/ 2/ and 4/ there are pros and cons to all
of them in terms of API.

> 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

  yes I don't see how we could avoid adding an extra target object

> 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.

  Then IMHO in that case we're stuck with 1/ , if we have per target
objects type of registrations, or as I suggested 2/ with an extra
void *object

   Still unclear to me what's the best way. My main problem is how to
reconcile generic APIs entry point and the fact we have diverging data
in registration and provided at the callback level. I think we need
to have static per registration fixed data coming from the user and
managed by the user, plus something available from libvirt to give more
information about the callback, and that is hard to manage between
callback objects and over evolution of the library.

Daniel

-- 
Daniel Veillard      | libxml Gnome XML XSLT toolkit  http://xmlsoft.org/
daniel at veillard.com  | Rpmfind RPM search engine http://rpmfind.net/
http://veillard.com/ | virtualization library  http://libvirt.org/




More information about the libvir-list mailing list