So currently, as we all know, the QEMU driver is just a shim through to the QEMU daemon. Looking at the code for Rich's remote driver I quite like the way the remote driver / server works like, and don't like the ugliness of having the special case QEMU code in there. We always had a long term plan to try & make the QEMU driver fit into the regular driver API more cleanly but not really tackled it yet. Similarly we've periodically brought up the idea of having asynchronous notifications of 'interesting' state changes in the drivers, to eliminate the need for polling.
After a little thinking I have an idea that may let us address both issues in one go. Or to be more precise, put infrastructure in place to let us move closer to allowing async notiifcations. First let me consider the files under qemud/ .... Self-contained helper routines, ignore for rest of discussion bridge.c bridge.h iptables.c iptables.h uuid.c uuid.h buf.c buf.h The on the wire protcol definition, this is the bit we want to kill in favour of the generic remote wire protocol: protocol.h The code to convert from wire -> function calls. Again this ought to die in favour of the generic remote dispatcher. dispatch.c dispatch.h The QEMU config handling routines. conf.c conf.h The impl of core APIs: driver.c driver.h Finally the 'server': internal.h qemud.c The end-goal is that 'qemud/driver.c' would become 'src/qemu_internal.c' implementing the official internal driver API. The 'qemud/conf.c' would likely become 'src/qemu_config.c'. The key difference between the internal driver API, and the QEMU specific driver 'API' inside the daemon is the prescense of some global state via the 'qemud_server' struct. If we can figure out a way to store this state inside the regular driver API we can refactor that without much problem. Then there is the need for an event loop for monitoring the file handles of the QEMU processes, which at the moment is fairly tied into the event loop used to monitor the client/server socket handles. This existing code is rather ugly & has bad structure IMHO. The result is that code which ought to be isolated in driver.c, is instead part of qemud.c because of event loop integration. Finally, there is some code which runs on startup, and shutdown of the daemon, to do stuff like auto-start of VMs / networks, and clean shutdown of the same. Oh, and we need to make sure the QEMU driver is never activated locally, only when libvirt.so is being used as part of 'libvirtd'. So at a high level what I think we'd need to add to the internal driver API to make this possible is In driver.h: typedef int (*virDrvStartup)(void); typedef int (*virDrvShutdown)(void); Adding these to the 'virDriver' struct. Regular apps using libvirt.so would never trigger these APIs - they're only intended for the daemon, so I figure we'd have to private APIs in the libivrt.so (but not in the libvirt.h) __virStartup(void); __virShutdown(void); Since these are only called by the daemon, we now conveniently also have a way to stop the QEMU driver being locally activated. Next up, event loops. First, we don't want to mandate a particular impl of an event loop. If you get into the argument about QT vs GLib vs libevent and libvirt picks one, then you make it hard for libvirt to integrate with apps using the other. Second, we don't want to assume that 1 connection == 1 file handle. We also don't want to assume that the number of file handles used by a driver stays the same over time. Basically we need to expect that any single driver connection will have zero or more file handles that it is interested in. The DBus library deals with this exact same issue & the solution is very simple. The app using the libvirt registers a callback which the internal driver than then use to (un)register file handles. So in src/internal.h we'd add methods for internal drivers to call to add / remove file handles to/from the event loop: /* * @fd: the file handle on which the event occurred * @event: bitset of one or more of POLLxxx constants from poll.h * @opaque: data associated with 'fd' * * Return 0 to continue monitoring, -1 if an error occurred * and the handle should no longer be monitored */ typedef int (*virEventCallback)(int fd, int events, void *opaque); /* * @fd: the file handle to start monitoring * @events: bitset of POLLxxx contants to monitor * @cb: callback to invoke when an event ocurrs * @opaque: data to pass into callback's opaque parameter */ int virEventAddHandle(int fd, int events, virEventCallback cb, void *opaque); /* * @fd: the file handle to stop monitoring */ int virEventRemoveHandle(int fd); And we'd also add methods for a user of libvirt to register an event loop implementation: typedef int (*virEventAddHandleImpl)(int fd, int events, opaque); typedef int (*virEventRemoveHandleImpl)(int fd); void virEventInitialize(virEventAddHandleImpl add, virEventRemoveHandleImpl remove); If we want to add support for async events into the public API, then the virEventInitialize method could instead be in libvirt.h, and exported in the libvirt.so. Otherwise we could mark it private __virEventInitialize in libvirt.so. That I think should be enough to let us move QEMU into a regular style libvirt driver. NB, what follows now is a quick brain-dump - I'm not suggesting implementing this yet - its really a rough idea of general architecture. We'd definitely want to prototype it in a test app before committing to something like this. There's also many many scenarios to be thought about beyond just watching create/destroy events Thinking about a public API we could have: enum { VIR_CONN_DOMAIN_CREATED, VIR_CONN_DOMAIN_DESTROYED; } virConnectEvent; typedef int (*virConnectNotifyCallback)(virConnectPtr dom, unsigned char uuid[16], int event); virConnectNotify(virConnectPtr conn, virConnectNofifyCallback cb); An application using this would thus be able to do something like /* Assume there's a convenient libvirtglib.so addon providing these for apps */ extern virEventAddHandleImpl virGLibEventAddHandle; extern virEventRemoveHandleImpl virGLibEventRemoveHandle; int domainWatcher(virConnectPtr dom, unsigned char uuid[16], int event) { ... do something ... } int main () { virConnectPtr con; con = virConnectOpen(NULL); /* Setup an event loop via glib */ virEventInitilize(virGLibEventAddHandle, virGLibEventRemoveHandle); virConnectNotify(conn, domainWatcher); Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=| -- Libvir-list mailing list Libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list