The following patch implements a framework for emitting events from the Xen driver, via monitoring xenstore. Currently, this only emits the STARTED, and STOPPED events, as this is all that seems to be available via monitoring xenstore. xen_unified.c | 162 +++++++++++++++++++++++ xen_unified.h | 38 +++++ xs_internal.c | 408 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- xs_internal.h | 51 +++++++ 4 files changed, 657 insertions(+), 2 deletions(-)
diff --git a/src/xen_unified.c b/src/xen_unified.c index 1ab204d..9314725 100644 --- a/src/xen_unified.c +++ b/src/xen_unified.c @@ -222,6 +222,7 @@ xenUnifiedOpen (virConnectPtr conn, xmlURIPtr uri, virConnectAuthPtr auth, int f { int i, ret = VIR_DRV_OPEN_DECLINED; xenUnifiedPrivatePtr priv; + virDomainEventCallbackListPtr cbList; /* Refuse any scheme which isn't "xen://" or "http://". */ if (uri->scheme && @@ -248,6 +249,13 @@ xenUnifiedOpen (virConnectPtr conn, xmlURIPtr uri, virConnectAuthPtr auth, int f } conn->privateData = priv; + /* Allocate callback list */ + if (VIR_ALLOC(cbList) < 0) { + xenUnifiedError (NULL, VIR_ERR_NO_MEMORY, "allocating callback list"); + return VIR_DRV_OPEN_ERROR; + } + priv->domainEventCallbacks = cbList; + priv->handle = -1; priv->xendConfigVersion = -1; priv->type = -1; @@ -345,6 +353,8 @@ xenUnifiedClose (virConnectPtr conn) int i; virCapabilitiesFree(priv->caps); + virDomainEventCallbackListFree(priv->domainEventCallbacks); + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) if (priv->opened[i] && drivers[i]->close) (void) drivers[i]->close (conn); @@ -1280,6 +1290,29 @@ xenUnifiedNodeGetFreeMemory (virConnectPtr conn) return(0); } +static int +xenUnifiedDomainEventRegister (virConnectPtr conn, + void *callback, + void *opaque) +{ + GET_PRIVATE (conn); + conn->refs++; + return virDomainEventCallbackListAdd(conn, priv->domainEventCallbacks, + callback, opaque); +} + +static int +xenUnifiedDomainEventDeregister (virConnectPtr conn, + void *callback) +{ + int ret; + GET_PRIVATE (conn); + ret = virDomainEventCallbackListRemove(conn, priv->domainEventCallbacks, + callback); + virUnrefConnect(conn); + return ret; +} + /*----- Register with libvirt.c, and initialise Xen drivers. -----*/ #define HV_VERSION ((DOM0_INTERFACE_VERSION >> 24) * 1000000 + \ @@ -1345,6 +1378,8 @@ static virDriver xenUnifiedDriver = { .domainBlockPeek = xenUnifiedDomainBlockPeek, .nodeGetCellsFreeMemory = xenUnifiedNodeGetCellsFreeMemory, .getFreeMemory = xenUnifiedNodeGetFreeMemory, + .domainEventRegister = xenUnifiedDomainEventRegister, + .domainEventDeregister = xenUnifiedDomainEventDeregister, }; /** @@ -1364,3 +1399,130 @@ xenUnifiedRegister (void) return virRegisterDriver (&xenUnifiedDriver); } +/** + * xenUnifiedDomainInfoListFree: + * + * Free the Domain Info List + */ +void +xenUnifiedDomainInfoListFree(xenUnifiedDomainInfoListPtr list) +{ + int i; + for (i=0; i<list->count; i++) { + VIR_FREE(list->doms[i]->name); + VIR_FREE(list->doms[i]->uuid); + } + VIR_FREE(list); +} + +/** + * xenUnifiedAddDomainInfo: + * + * Add name and uuid to the domain info list + * + * Returns: 0 on success, -1 on failure + */ +int +xenUnifiedAddDomainInfo(xenUnifiedDomainInfoListPtr list, + char *name, char *uuid) +{ + xenUnifiedDomainInfoPtr info; + int n; + + /* check if we already have this callback on our list */ + for (n=0; n < list->count; n++) { + if( STREQ(list->doms[n]->name, name) && + STREQ(list->doms[n]->uuid, uuid)) { + DEBUG0("WARNING: dom already tracked"); + return -1; + } + } + + if (VIR_ALLOC(info) < 0) { + xenUnifiedError (NULL, VIR_ERR_NO_MEMORY, "allocating info"); + return -1; + } + info->name = strdup(name); + info->uuid = strdup(uuid); + + /* Make space on list */ + n = list->count; + if (VIR_REALLOC_N(list->doms, n + 1) < 0) { + xenUnifiedError (NULL, VIR_ERR_NO_MEMORY, "reallocating list"); + VIR_FREE(info); + return -1; + } + + list->doms[n] = info; + list->count++; + return 0; +} + +/** + * xenUnifiedRemoveDomainInfo: + * + * Removes name and uuid to the domain info list + * + * Returns: 0 on success, -1 on failure + */ +int +xenUnifiedRemoveDomainInfo(xenUnifiedDomainInfoListPtr list, + char *name, char *uuid) +{ + int i; + for (i = 0 ; i < list->count ; i++) { + if( STREQ(list->doms[i]->name, name) && + STREQ(list->doms[i]->uuid, uuid)) { + VIR_FREE(list->doms[i]->name); + VIR_FREE(list->doms[i]->uuid); + VIR_FREE(list->doms[i]); + + if (i < (list->count - 1)) + memmove(list->doms + i, + list->doms + i + 1, + sizeof(*(list->doms)) * + (list->count - (i + 1))); + + if (VIR_REALLOC_N(list->doms, + list->count - 1) < 0) { + ; /* Failure to reduce memory allocation isn't fatal */ + } + list->count--; + + return 0; + } + } + return -1; +} + +/** + * xenUnifiedDomainEventDispatch: + * + * Dispatch domain events to registered callbacks + * + */ + void xenUnifiedDomainEventDispatch (xenUnifiedPrivatePtr priv, + virDomainPtr dom, + virDomainEventType evt) +{ + int i; + virDomainEventCallbackListPtr cbList; + + if(!priv) return; + + cbList = priv->domainEventCallbacks; + if(!cbList) return; + + for(i=0 ; i < cbList->count ; i++) { + if(cbList->callbacks[i] && cbList->callbacks[i]->cb) { + if (dom) { + DEBUG("Dispatching callback %p %p event %d", + cbList->callbacks[i], + cbList->callbacks[i]->cb, evt); + cbList->callbacks[i]->cb(cbList->callbacks[i]->conn, + dom, evt, + cbList->callbacks[i]->opaque); + } + } + } +} diff --git a/src/xen_unified.h b/src/xen_unified.h index 48415a8..2936a86 100644 --- a/src/xen_unified.h +++ b/src/xen_unified.h @@ -14,6 +14,8 @@ #include "internal.h" #include "capabilities.h" #include "driver.h" +#include "xs_internal.h" +#include "domain_event.h" #ifndef HAVE_WINSOCK2_H #include <sys/un.h> @@ -85,6 +87,25 @@ struct xenUnifiedDriver { virDrvDomainSetSchedulerParameters domainSetSchedulerParameters; }; +/* xenUnifiedDomainInfoPtr: + * The minimal state we have about active domains + * This is the minmal info necessary to still get a + * virDomainPtr when the domain goes away + */ +struct _xenUnifiedDomainInfo { + char *name; + char *uuid; +}; +typedef struct _xenUnifiedDomainInfo xenUnifiedDomainInfo; +typedef xenUnifiedDomainInfo *xenUnifiedDomainInfoPtr; + +struct _xenUnifiedDomainInfoList { + unsigned int count; + xenUnifiedDomainInfoPtr *doms; +}; +typedef struct _xenUnifiedDomainInfoList xenUnifiedDomainInfoList; +typedef xenUnifiedDomainInfoList *xenUnifiedDomainInfoListPtr; + /* xenUnifiedPrivatePtr: * * Per-connection private data, stored in conn->privateData. All Xen @@ -113,6 +134,15 @@ struct _xenUnifiedPrivate { * xen_unified.c. */ int opened[XEN_UNIFIED_NR_DRIVERS]; + + /* A list of xenstore watches */ + xenStoreWatchListPtr xsWatchList; + + /* An list of active domain name/uuids */ + xenUnifiedDomainInfoListPtr domInfoList; + + /* An list of callbacks */ + virDomainEventCallbackListPtr domainEventCallbacks; }; typedef struct _xenUnifiedPrivate *xenUnifiedPrivatePtr; @@ -122,4 +152,12 @@ int xenNbCells(virConnectPtr conn); int xenNbCpus(virConnectPtr conn); char *xenDomainUsedCpus(virDomainPtr dom); +void xenUnifiedDomainInfoListFree(xenUnifiedDomainInfoListPtr info); +int xenUnifiedAddDomainInfo(xenUnifiedDomainInfoListPtr info, + char *name, char *uuid); +int xenUnifiedRemoveDomainInfo(xenUnifiedDomainInfoListPtr info, + char *name, char *uuid); +void xenUnifiedDomainEventDispatch (xenUnifiedPrivatePtr priv, + virDomainPtr dom, + virDomainEventType evt); #endif /* __VIR_XEN_UNIFIED_H__ */ diff --git a/src/xs_internal.c b/src/xs_internal.c index b096acd..642f9ab 100644 --- a/src/xs_internal.c +++ b/src/xs_internal.c @@ -29,6 +29,8 @@ #include "virterror_internal.h" #include "datatypes.h" #include "driver.h" +#include "memory.h" +#include "event.h" #include "xen_unified.h" #include "xs_internal.h" #include "xen_internal.h" /* for xenHypervisorCheckID */ @@ -301,9 +303,55 @@ xenStoreOpen(virConnectPtr conn, virXenStoreError(NULL, VIR_ERR_NO_XEN, "%s", _("failed to connect to Xen Store")); } - return (-1); + return -1; } - return (0); + +#ifndef PROXY + /* Init domInfoList */ + if ( VIR_ALLOC(priv->domInfoList ) < 0) { + virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("failed to allocate domInfoList")); + return -1; + } + + /* Init watch list before filling in domInfoList, + so we can know if it is the first time through + when the callback fires */ + if ( VIR_ALLOC(priv->xsWatchList) < 0 ) { + virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("failed to allocate xsWatchList")); + return -1; + } + + /* This will get called once at start */ + if ( xenStoreAddWatch(conn, "@releaseDomain", + "releaseDomain", xenStoreDomainReleased, priv) < 0 ) + { + DEBUG0("Error Adding Watch @releaseDomain\n"); + return -1; + } + + /* The initial call of this will fill domInfoList */ + if( xenStoreAddWatch(conn, "@introduceDomain", + "introduceDomain", xenStoreDomainIntroduced, priv) < 0 ) + { + DEBUG0("Error Adding Watch @introduceDomain\n"); + return -1; + } + + /* Add an event handle */ + if (virEventAddHandle(xs_fileno(priv->xshandle), + VIR_EVENT_HANDLE_READABLE, + xenStoreWatchEvent, + conn) < 0) { + virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("failed to handle")); + + return -1; + } + +#endif //PROXY + return 0; } /** @@ -317,6 +365,7 @@ xenStoreOpen(virConnectPtr conn, int xenStoreClose(virConnectPtr conn) { + int fd; xenUnifiedPrivatePtr priv; if (conn == NULL) { @@ -325,10 +374,23 @@ xenStoreClose(virConnectPtr conn) } priv = (xenUnifiedPrivatePtr) conn->privateData; + + xenStoreRemoveWatch(conn, "@introduceDomain", "introduceDomain"); + xenStoreRemoveWatch(conn, "@releaseDomain", "releaseDomain"); + + xenStoreWatchListFree(priv->xsWatchList); +#ifndef PROXY + xenUnifiedDomainInfoListFree(priv->domInfoList); +#endif if (priv->xshandle == NULL) return(-1); + fd = xs_fileno(priv->xshandle); + virEventRemoveHandle(fd); + close(fd); xs_daemon_close(priv->xshandle); + priv->xshandle = NULL; + return (0); } @@ -921,3 +983,345 @@ char *xenStoreDomainGetName(virConnectPtr conn, return xs_read(priv->xshandle, 0, prop, &len); } +#ifndef PROXY +char *xenStoreDomainGetUUID(virConnectPtr conn, + int id) { + char prop[200]; + xenUnifiedPrivatePtr priv; + unsigned int len; + char *vm_string; + char *ret; + + priv = (xenUnifiedPrivatePtr) conn->privateData; + if (priv->xshandle == NULL) + return(NULL); + + snprintf(prop, 199, "/local/domain/%d/vm", id); + prop[199] = 0; + // This will return something like + // /vm/00000000-0000-0000-0000-000000000000 + vm_string = xs_read(priv->xshandle, 0, prop, &len); + + // remove "/vm/" + ret = strdup(vm_string + 4); + + VIR_FREE(vm_string); + + return ret; +} +#endif //PROXY + +void xenStoreWatchListFree(xenStoreWatchListPtr list) +{ + int i; + for (i=0; i<list->count; i++) { + VIR_FREE(list->watches[i]->path); + VIR_FREE(list->watches[i]->token); + VIR_FREE(list->watches[i]); + } + VIR_FREE(list); +} + +int xenStoreAddWatch(virConnectPtr conn, + const char *path, + const char *token, + xenStoreWatchCallback cb, + void *opaque) +{ + xenStoreWatchPtr watch; + int n; + xenStoreWatchListPtr list; + xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; + + if (priv->xshandle == NULL) + return -1; + + list = priv->xsWatchList; + if(!list) + return -1; + + /* check if we already have this callback on our list */ + for (n=0; n < list->count; n++) { + if( STREQ(list->watches[n]->path, path) && + STREQ(list->watches[n]->token, token)) { + DEBUG0("WARNING: Watch already tracked"); + return -1; + } + } + + if (VIR_ALLOC(watch) < 0) + return -1; + watch->path = strdup(path); + watch->token = strdup(token); + watch->cb = cb; + watch->opaque = opaque; + + /* Make space on list */ + n = list->count; + if (VIR_REALLOC_N(list->watches, n + 1) < 0) { + DEBUG0("Error reallocating list"); + VIR_FREE(watch); + return -1; + } + + list->watches[n] = watch; + list->count++; + + conn->refs++; + + return xs_watch(priv->xshandle, watch->path, watch->token); +} + +int xenStoreRemoveWatch(virConnectPtr conn, + const char *path, + const char *token) +{ + int i; + xenStoreWatchListPtr list; + xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; + + if (priv->xshandle == NULL) + return -1; + + list = priv->xsWatchList; + if(!list) + return -1; + + for (i = 0 ; i < list->count ; i++) { + if( STREQ(list->watches[i]->path, path) && + STREQ(list->watches[i]->token, token)) { + + xs_unwatch(priv->xshandle, + list->watches[i]->path, + list->watches[i]->path); + + VIR_FREE(list->watches[i]->path); + VIR_FREE(list->watches[i]->token); + VIR_FREE(list->watches[i]); + + if (i < (list->count - 1)) + memmove(list->watches + i, + list->watches + i + 1, + sizeof(*(list->watches)) * + (list->count - (i + 1))); + + if (VIR_REALLOC_N(list->watches, + list->count - 1) < 0) { + ; /* Failure to reduce memory allocation isn't fatal */ + } + list->count--; +#ifndef PROXY + virUnrefConnect(conn); +#endif + return 0; + } + } + return -1; +} + +xenStoreWatchPtr xenStoreFindWatch(xenStoreWatchListPtr list, + const char *path, + const char *token) +{ + int i; + for (i = 0 ; i < list->count ; i++) + if( STREQ(path, list->watches[i]->path) && + STREQ(token, list->watches[i]->token) ) + return list->watches[i]; + + return NULL; +} + +void xenStoreWatchEvent(int fd ATTRIBUTE_UNUSED, + int events ATTRIBUTE_UNUSED, + void *data) +{ + char **event; + char *path; + char *token; + unsigned int stringCount; + xenStoreWatchPtr sw; + + virConnectPtr conn = data; + xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; + if(!priv) return; + if(!priv->xshandle) return; + + event = xs_read_watch(priv->xshandle, &stringCount); + if (!event) + return; + + path = event[XS_WATCH_PATH]; + token = event[XS_WATCH_TOKEN]; + + sw = xenStoreFindWatch(priv->xsWatchList, path, token); + if( sw ) + sw->cb(conn, path, token, sw->opaque); + VIR_FREE(event); +} + +#ifndef PROXY +/* The domain callback for the @introduceDomain watch */ +int xenStoreDomainIntroduced(virConnectPtr conn, + const char *path ATTRIBUTE_UNUSED, + const char *token ATTRIBUTE_UNUSED, + void *opaque) +{ + int i, j; + int new_domain_cnt; + int *new_domids; + char *name; + char *uuid; + int nread; + + xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) opaque; + +retry: + new_domain_cnt = xenStoreNumOfDomains(conn); + if( VIR_ALLOC_N(new_domids,new_domain_cnt) < 0 ) { + virXenStoreError(NULL, VIR_ERR_NO_MEMORY, + "%s", _("failed to allocate domids")); + return -1; + } + nread = xenStoreListDomains(conn, new_domids, new_domain_cnt); + if (nread != new_domain_cnt) { + // mismatch. retry this read + VIR_FREE(new_domids); + goto retry; + } + + // Initial callback, fill list (This should at least contain dom0) + if(!priv->domInfoList->count) { + for (i=0 ; i < new_domain_cnt ; i++) { + name = xenStoreDomainGetName(conn, new_domids[i]); + uuid = xenStoreDomainGetUUID(conn, new_domids[i]); + xenUnifiedAddDomainInfo(priv->domInfoList,name,uuid); + VIR_FREE(name); + VIR_FREE(uuid); + } + VIR_FREE(new_domids); + return 0; + } + + for (i=0 ; i < new_domain_cnt ; i++) { + if (!(name = xenStoreDomainGetName(conn, new_domids[i]))) { + continue; + } + if (!(uuid = xenStoreDomainGetUUID(conn, new_domids[i]))) { + VIR_FREE(name); + continue; + } + /* We must iterate over both lists to avoid a race where + 2 domains were created by the time we get here */ + for (j=0 ; j < priv->domInfoList->count ; j++) { + if( !STREQ(name, priv->domInfoList->doms[j]->name) || + !STREQ(uuid, priv->domInfoList->doms[j]->uuid) ) { + virDomainPtr dom = virGetDomain(conn, name, (unsigned char*)uuid); + if(dom) { + dom->id = new_domids[i]; + + /* This domain was not in the old list. Emit an event */ + xenUnifiedDomainEventDispatch(priv, dom, + VIR_DOMAIN_EVENT_STARTED); + + /* Add to the list */ + xenUnifiedAddDomainInfo(priv->domInfoList,name,uuid); + } + virUnrefDomain(dom); + } + } + + VIR_FREE(name); + VIR_FREE(uuid); + } + VIR_FREE(new_domids); + return 0; +} + +/* The domain callback for the @destroyDomain watch */ +int xenStoreDomainReleased(virConnectPtr conn, + const char *path ATTRIBUTE_UNUSED, + const char *token ATTRIBUTE_UNUSED, + void *opaque) +{ + int i, j, found, removed; + int new_domain_cnt; + int *new_domids; + char *name; + char *uuid; + int nread; + + xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) opaque; + + if(!priv->domInfoList->count) return 0; + +retry: + new_domain_cnt = xenStoreNumOfDomains(conn); + + if( VIR_ALLOC_N(new_domids,new_domain_cnt) < 0 ) { + virXenStoreError(NULL, VIR_ERR_NO_MEMORY, + "%s", _("failed to allocate domids")); + return -1; + } + nread = xenStoreListDomains(conn, new_domids, new_domain_cnt); + if (nread != new_domain_cnt) { + // mismatch. retry this read + VIR_FREE(new_domids); + goto retry; + } + + removed = 0; + for (j=0 ; j < priv->domInfoList->count ; j++) { + found = 0; + for (i=0 ; i < new_domain_cnt ; i++) { + if (!(name = xenStoreDomainGetName(conn, new_domids[i]))) { + continue; + } + + if (!(uuid = xenStoreDomainGetUUID(conn, new_domids[i]))) { + VIR_FREE(name); + continue; + } + + if( STREQ(name, priv->domInfoList->doms[j]->name) || + STREQ(uuid, priv->domInfoList->doms[j]->uuid) ) { + found = 1; + VIR_FREE(name); + VIR_FREE(uuid); + break; + } + + VIR_FREE(name); + VIR_FREE(uuid); + } + + if (!found) { + virDomainPtr dom = virGetDomain(conn, + priv->domInfoList->doms[j]->name, + (unsigned char*)priv->domInfoList->doms[j]->uuid); + if(dom) { + dom->id = -1; + /* This domain was not in the new list. Emit an event */ + xenUnifiedDomainEventDispatch(priv, dom, + VIR_DOMAIN_EVENT_STOPPED); + /* Remove from the list */ + xenUnifiedRemoveDomainInfo(priv->domInfoList, + priv->domInfoList->doms[j]->name, + priv->domInfoList->doms[j]->uuid); + + virUnrefDomain(dom); + removed=1; + } + } + } + + VIR_FREE(new_domids); + if(!removed) { + // xenstore sometimes calls us back before + // domids are fully torn down + usleep(1000); + goto retry; + } + return 0; +} +#endif //PROXY diff --git a/src/xs_internal.h b/src/xs_internal.h index 6163ebc..006e0e1 100644 --- a/src/xs_internal.h +++ b/src/xs_internal.h @@ -52,5 +52,56 @@ char * xenStoreDomainGetDiskID(virConnectPtr conn, const char *dev); char * xenStoreDomainGetName(virConnectPtr conn, int id); +char * xenStoreDomainGetUUID(virConnectPtr conn, + int id); + +typedef int (*xenStoreWatchCallback)(virConnectPtr conn, + const char *path, + const char *token, + void *opaque); + +struct _xenStoreWatch { + char *path; + char *token; + xenStoreWatchCallback cb; + void *opaque; +}; +typedef struct _xenStoreWatch xenStoreWatch; +typedef xenStoreWatch *xenStoreWatchPtr; + +struct _xenStoreWatchList { + unsigned int count; + xenStoreWatchPtr *watches; +}; +typedef struct _xenStoreWatchList xenStoreWatchList; +typedef xenStoreWatchList *xenStoreWatchListPtr; + + +void xenStoreWatchListFree(xenStoreWatchListPtr head); + +int xenStoreAddWatch(virConnectPtr conn, + const char *path, + const char *token, + xenStoreWatchCallback cb, + void *opaque); +int xenStoreRemoveWatch(virConnectPtr conn, + const char *path, + const char *token); +xenStoreWatchPtr xenStoreFindWatch(xenStoreWatchListPtr list, + const char *path, + const char *token); + +void xenStoreWatchEvent(int fd, int events, void *data); + +/* domain events */ +int xenStoreDomainIntroduced(virConnectPtr conn, + const char *path, + const char *token, + void *opaque); +int xenStoreDomainReleased(virConnectPtr conn, + const char *path, + const char *token, + void *opaque); +int xenStoreDomainEventEmitted(virDomainEventType evt); #endif /* __VIR_XS_INTERNAL_H__ */
-- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list