[libvirt] PATCH: 4/5: Locking in the LXC driver

Daniel P. Berrange berrange at redhat.com
Fri Oct 17 14:04:50 UTC 2008


This implements the same logic as described for the QEMU driver,
in the LXC driver & its API calls. The concurrency on this is
already very good, since the LXC driver has no long running
code by virtue of not having a 'monitor' console to interact
with.

Daniel


diff --git a/src/lxc_conf.h b/src/lxc_conf.h
--- a/src/lxc_conf.h
+++ b/src/lxc_conf.h
@@ -36,6 +36,8 @@
 
 typedef struct __lxc_driver lxc_driver_t;
 struct __lxc_driver {
+    pthread_mutex_t lock;
+
     virCapsPtr caps;
 
     virDomainObjList domains;
diff --git a/src/lxc_driver.c b/src/lxc_driver.c
--- a/src/lxc_driver.c
+++ b/src/lxc_driver.c
@@ -52,6 +52,19 @@ static lxc_driver_t *lxc_driver = NULL;
 static lxc_driver_t *lxc_driver = NULL;
 
 /* Functions */
+
+static int
+lxcDriverLock(lxc_driver_t *driver)
+{
+    return pthread_mutex_lock(&driver->lock);
+}
+
+static int
+lxcDriverUnlock(lxc_driver_t *driver)
+{
+    return pthread_mutex_unlock(&driver->lock);
+}
+
 
 static const char *lxcProbe(void)
 {
@@ -104,8 +117,12 @@ static virDomainPtr lxcDomainLookupByID(
                                         int id)
 {
     lxc_driver_t *driver = (lxc_driver_t *)conn->privateData;
-    virDomainObjPtr vm = virDomainFindByID(&driver->domains, id);
+    virDomainObjPtr vm;
     virDomainPtr dom;
+
+    lxcDriverLock(driver);
+    vm = virDomainFindByID(&driver->domains, id);
+    lxcDriverUnlock(driver);
 
     if (!vm) {
         lxcError(conn, NULL, VIR_ERR_NO_DOMAIN, NULL);
@@ -117,6 +134,7 @@ static virDomainPtr lxcDomainLookupByID(
         dom->id = vm->def->id;
     }
 
+    virDomainUnlock(vm);
     return dom;
 }
 
@@ -124,8 +142,12 @@ static virDomainPtr lxcDomainLookupByUUI
                                           const unsigned char *uuid)
 {
     lxc_driver_t *driver = (lxc_driver_t *)conn->privateData;
-    virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, uuid);
+    virDomainObjPtr vm;
     virDomainPtr dom;
+
+    lxcDriverLock(driver);
+    vm = virDomainFindByUUID(&driver->domains, uuid);
+    lxcDriverUnlock(driver);
 
     if (!vm) {
         lxcError(conn, NULL, VIR_ERR_NO_DOMAIN, NULL);
@@ -137,6 +159,7 @@ static virDomainPtr lxcDomainLookupByUUI
         dom->id = vm->def->id;
     }
 
+    virDomainUnlock(vm);
     return dom;
 }
 
@@ -144,8 +167,12 @@ static virDomainPtr lxcDomainLookupByNam
                                           const char *name)
 {
     lxc_driver_t *driver = (lxc_driver_t *)conn->privateData;
-    virDomainObjPtr vm = virDomainFindByName(&driver->domains, name);
+    virDomainObjPtr vm;
     virDomainPtr dom;
+
+    lxcDriverLock(driver);
+    vm = virDomainFindByName(&driver->domains, name);
+    lxcDriverUnlock(driver);
 
     if (!vm) {
         lxcError(conn, NULL, VIR_ERR_NO_DOMAIN, NULL);
@@ -157,6 +184,7 @@ static virDomainPtr lxcDomainLookupByNam
         dom->id = vm->def->id;
     }
 
+    virDomainUnlock(vm);
     return dom;
 }
 
@@ -164,9 +192,16 @@ static int lxcListDomains(virConnectPtr 
     lxc_driver_t *driver = (lxc_driver_t *)conn->privateData;
     int got = 0, i;
 
-    for (i = 0 ; i < driver->domains.count && got < nids ; i++)
+    lxcDriverLock(driver);
+
+    for (i = 0 ; i < driver->domains.count && got < nids ; i++) {
+        virDomainLock(driver->domains.objs[i]);
         if (virDomainIsActive(driver->domains.objs[i]))
             ids[got++] = driver->domains.objs[i]->def->id;
+        virDomainUnlock(driver->domains.objs[i]);
+    }
+
+    lxcDriverUnlock(driver);
 
     return got;
 }
@@ -174,9 +209,16 @@ static int lxcNumDomains(virConnectPtr c
     lxc_driver_t *driver = (lxc_driver_t *)conn->privateData;
     int n = 0, i;
 
-    for (i = 0 ; i < driver->domains.count ; i++)
+    lxcDriverLock(driver);
+
+    for (i = 0 ; i < driver->domains.count ; i++) {
+        virDomainLock(driver->domains.objs[i]);
         if (virDomainIsActive(driver->domains.objs[i]))
             n++;
+        virDomainUnlock(driver->domains.objs[i]);
+    }
+
+    lxcDriverUnlock(driver);
 
     return n;
 }
@@ -186,21 +228,28 @@ static int lxcListDefinedDomains(virConn
     lxc_driver_t *driver = (lxc_driver_t *)conn->privateData;
     int got = 0, i;
 
+    lxcDriverLock(driver);
+
     for (i = 0 ; i < driver->domains.count && got < nnames ; i++) {
+        virDomainLock(driver->domains.objs[i]);
         if (!virDomainIsActive(driver->domains.objs[i])) {
             if (!(names[got++] = strdup(driver->domains.objs[i]->def->name))) {
                 lxcError(conn, NULL, VIR_ERR_NO_MEMORY,
                          "%s", _("failed to allocate space for VM name string"));
+                virDomainUnlock(driver->domains.objs[i]);
                 goto cleanup;
             }
         }
+        virDomainUnlock(driver->domains.objs[i]);
     }
 
+    lxcDriverUnlock(driver);
     return got;
 
  cleanup:
     for (i = 0 ; i < got ; i++)
         VIR_FREE(names[i]);
+    lxcDriverUnlock(driver);
     return -1;
 }
 
@@ -209,9 +258,14 @@ static int lxcNumDefinedDomains(virConne
     lxc_driver_t *driver = (lxc_driver_t *)conn->privateData;
     int n = 0, i;
 
-    for (i = 0 ; i < driver->domains.count ; i++)
+    lxcDriverLock(driver);
+    for (i = 0 ; i < driver->domains.count ; i++) {
+        virDomainLock(driver->domains.objs[i]);
         if (!virDomainIsActive(driver->domains.objs[i]))
             n++;
+        virDomainUnlock(driver->domains.objs[i]);
+    }
+    lxcDriverUnlock(driver);
 
     return n;
 }
@@ -225,27 +279,28 @@ static virDomainPtr lxcDomainDefine(virC
     virDomainObjPtr vm;
     virDomainPtr dom;
 
+    lxcDriverLock(driver);
+
     if (!(def = virDomainDefParseString(conn, driver->caps, xml)))
-        return NULL;
+        goto error;
 
     if ((def->nets != NULL) && !(driver->have_netns)) {
         lxcError(conn, NULL, VIR_ERR_NO_SUPPORT,
                  "%s", _("System lacks NETNS support"));
-        virDomainDefFree(def);
-        return NULL;
+        goto error;
     }
 
-    if (!(vm = virDomainAssignDef(conn, &driver->domains, def))) {
-        virDomainDefFree(def);
-        return NULL;
-    }
+    if (!(vm = virDomainAssignDef(conn, &driver->domains, def)))
+        goto error;
+
     vm->persistent = 1;
+    def = NULL;
 
     if (virDomainSaveConfig(conn,
                             driver->configDir,
                             vm->newDef ? vm->newDef : vm->def) < 0) {
         virDomainRemoveInactive(&driver->domains, vm);
-        return NULL;
+        goto error;
     }
 
     dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
@@ -253,48 +308,73 @@ static virDomainPtr lxcDomainDefine(virC
         dom->id = vm->def->id;
     }
 
+    virDomainUnlock(vm);
+    lxcDriverUnlock(driver);
+
     return dom;
+
+error:
+    if (def)
+        virDomainDefFree(def);
+
+    lxcDriverUnlock(driver);
+    return NULL;
 }
 
 static int lxcDomainUndefine(virDomainPtr dom)
 {
     lxc_driver_t *driver = (lxc_driver_t *)dom->conn->privateData;
-    virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+    virDomainObjPtr vm;
+
+    lxcDriverLock(driver);
+    vm = virDomainFindByUUID(&driver->domains, dom->uuid);
 
     if (!vm) {
         lxcError(dom->conn, dom, VIR_ERR_INVALID_DOMAIN,
                  "%s", _("no domain with matching uuid"));
-        return -1;
+        goto error;
     }
 
     if (virDomainIsActive(vm)) {
         lxcError(dom->conn, dom, VIR_ERR_INTERNAL_ERROR,
                  "%s", _("cannot delete active domain"));
-        return -1;
+        goto error;
     }
 
     if (!vm->persistent) {
         lxcError(dom->conn, dom, VIR_ERR_INTERNAL_ERROR,
                  "%s", _("cannot undefine transient domain"));
-        return -1;
+        goto error;
     }
 
     if (virDomainDeleteConfig(dom->conn,
                               driver->configDir,
                               driver->autostartDir,
                               vm) <0)
-        return -1;
+        goto error;
 
+    virDomainUnlock(vm);
     virDomainRemoveInactive(&driver->domains, vm);
 
+    lxcDriverUnlock(driver);
     return 0;
+
+error:
+    if (vm)
+        virDomainUnlock(vm);
+    lxcDriverUnlock(driver);
+    return -1;
 }
 
 static int lxcDomainGetInfo(virDomainPtr dom,
                             virDomainInfoPtr info)
 {
     lxc_driver_t *driver = (lxc_driver_t *)dom->conn->privateData;
-    virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+    virDomainObjPtr vm;
+
+    lxcDriverLock(driver);
+    vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+    lxcDriverUnlock(driver);
 
     if (!vm) {
         lxcError(dom->conn, dom, VIR_ERR_INVALID_DOMAIN,
@@ -314,13 +394,19 @@ static int lxcDomainGetInfo(virDomainPtr
     info->memory = vm->def->memory;
     info->nrVirtCpu = 1;
 
+    virDomainUnlock(vm);
     return 0;
 }
 
 static char *lxcGetOSType(virDomainPtr dom)
 {
     lxc_driver_t *driver = (lxc_driver_t *)dom->conn->privateData;
-    virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+    virDomainObjPtr vm;
+    char *ret;
+
+    lxcDriverLock(driver);
+    vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+    lxcDriverUnlock(driver);
 
     if (!vm) {
         lxcError(dom->conn, dom, VIR_ERR_INVALID_DOMAIN,
@@ -328,14 +414,21 @@ static char *lxcGetOSType(virDomainPtr d
         return NULL;
     }
 
-    return strdup(vm->def->os.type);
+    ret = strdup(vm->def->os.type);
+    virDomainUnlock(vm);
+    return ret;
 }
 
 static char *lxcDomainDumpXML(virDomainPtr dom,
                               int flags)
 {
     lxc_driver_t *driver = (lxc_driver_t *)dom->conn->privateData;
-    virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+    virDomainObjPtr vm;
+    char *ret;
+
+    lxcDriverLock(driver);
+    vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+    lxcDriverUnlock(driver);
 
     if (!vm) {
         lxcError(dom->conn, dom, VIR_ERR_INVALID_DOMAIN,
@@ -343,10 +436,12 @@ static char *lxcDomainDumpXML(virDomainP
         return NULL;
     }
 
-    return virDomainDefFormat(dom->conn,
-                              (flags & VIR_DOMAIN_XML_INACTIVE) &&
-                              vm->newDef ? vm->newDef : vm->def,
+    ret = virDomainDefFormat(dom->conn,
+                             (flags & VIR_DOMAIN_XML_INACTIVE) &&
+                             vm->newDef ? vm->newDef : vm->def,
                               flags);
+    virDomainUnlock(vm);
+    return ret;
 }
 
 
@@ -590,19 +685,29 @@ static void lxcMonitorEvent(int fd,
     virDomainObjPtr vm = NULL;
     unsigned int i;
 
+    lxcDriverLock(driver);
+
     for (i = 0 ; i < driver->domains.count ; i++) {
+        virDomainLock(driver->domains.objs[i]);
         if (driver->domains.objs[i]->monitor == fd) {
             vm = driver->domains.objs[i];
             break;
         }
+        virDomainUnlock(driver->domains.objs[i]);
     }
+
     if (!vm) {
         virEventRemoveHandle(fd);
-        return;
+        goto cleanup;
     }
 
     if (lxcVmTerminate(NULL, driver, vm, SIGINT) < 0)
         virEventRemoveHandle(fd);
+
+cleanup:
+    if (vm)
+        virDomainUnlock(vm);
+    lxcDriverUnlock(driver);
 }
 
 
@@ -851,7 +956,10 @@ static int lxcDomainStart(virDomainPtr d
     int rc = -1;
     virConnectPtr conn = dom->conn;
     lxc_driver_t *driver = (lxc_driver_t *)(conn->privateData);
-    virDomainObjPtr vm = virDomainFindByName(&driver->domains, dom->name);
+    virDomainObjPtr vm;
+
+    lxcDriverLock(driver);
+    vm = virDomainFindByName(&driver->domains, dom->name);
 
     if (!vm) {
         lxcError(conn, dom, VIR_ERR_INVALID_DOMAIN,
@@ -868,6 +976,9 @@ static int lxcDomainStart(virDomainPtr d
     rc = lxcVmStart(conn, driver, vm);
 
 cleanup:
+    if (vm)
+        virDomainUnlock(vm);
+    lxcDriverUnlock(driver);
     return rc;
 }
 
@@ -890,6 +1001,8 @@ lxcDomainCreateAndStart(virConnectPtr co
     virDomainDefPtr def;
     virDomainPtr dom = NULL;
 
+    lxcDriverLock(driver);
+
     if (!(def = virDomainDefParseString(conn, driver->caps, xml)))
         goto return_point;
 
@@ -917,6 +1030,9 @@ lxcDomainCreateAndStart(virConnectPtr co
     }
 
 return_point:
+    if (vm)
+        virDomainUnlock(vm);
+    lxcDriverUnlock(driver);
     return dom;
 }
 
@@ -931,15 +1047,25 @@ static int lxcDomainShutdown(virDomainPt
 static int lxcDomainShutdown(virDomainPtr dom)
 {
     lxc_driver_t *driver = (lxc_driver_t*)dom->conn->privateData;
-    virDomainObjPtr vm = virDomainFindByID(&driver->domains, dom->id);
+    virDomainObjPtr vm;
+    int ret = -1;
+
+    lxcDriverLock(driver);
+    vm = virDomainFindByID(&driver->domains, dom->id);
 
     if (!vm) {
         lxcError(dom->conn, dom, VIR_ERR_INVALID_DOMAIN,
                  _("no domain with id %d"), dom->id);
-        return -1;
+        goto cleanup;
     }
 
-    return lxcVmTerminate(dom->conn, driver, vm, 0);
+    ret = lxcVmTerminate(dom->conn, driver, vm, 0);
+
+cleanup:
+    if (vm)
+        virDomainUnlock(vm);
+    lxcDriverUnlock(driver);
+    return ret;
 }
 
 
@@ -954,15 +1080,24 @@ static int lxcDomainDestroy(virDomainPtr
 static int lxcDomainDestroy(virDomainPtr dom)
 {
     lxc_driver_t *driver = (lxc_driver_t*)dom->conn->privateData;
-    virDomainObjPtr vm = virDomainFindByID(&driver->domains, dom->id);
+    virDomainObjPtr vm;
+    int ret = -1;
+
+    lxcDriverLock(driver);
+    vm = virDomainFindByID(&driver->domains, dom->id);
 
     if (!vm) {
         lxcError(dom->conn, dom, VIR_ERR_INVALID_DOMAIN,
                  _("no domain with id %d"), dom->id);
-        return -1;
+        goto cleanup;
     }
 
-    return lxcVmTerminate(dom->conn, driver, vm, SIGKILL);
+    ret = lxcVmTerminate(dom->conn, driver, vm, SIGKILL);
+cleanup:
+    if (vm)
+        virDomainUnlock(vm);
+    lxcDriverUnlock(driver);
+    return ret;
 }
 
 static int lxcCheckNetNsSupport(void)
@@ -990,35 +1125,31 @@ static int lxcStartup(void)
         return -1;
     }
 
-    if (VIR_ALLOC(lxc_driver) < 0) {
-        return -1;
-    }
-
     /* Check that this is a container enabled kernel */
     if(lxcContainerAvailable(0) < 0)
         return -1;
 
+    if (VIR_ALLOC(lxc_driver) < 0)
+        return -1;
+
+    pthread_mutex_init(&lxc_driver->lock, NULL);
+    lxcDriverLock(lxc_driver);
+
     lxc_driver->have_netns = lxcCheckNetNsSupport();
 
     /* Call function to load lxc driver configuration information */
-    if (lxcLoadDriverConfig(lxc_driver) < 0) {
-        lxcShutdown();
-        return -1;
-    }
+    if (lxcLoadDriverConfig(lxc_driver) < 0)
+        goto error;
 
-    if ((lxc_driver->caps = lxcCapsInit()) == NULL) {
-        lxcShutdown();
-        return -1;
-    }
+    if ((lxc_driver->caps = lxcCapsInit()) == NULL)
+        goto error;
 
     if (virDomainLoadAllConfigs(NULL,
                                 lxc_driver->caps,
                                 &lxc_driver->domains,
                                 lxc_driver->configDir,
-                                lxc_driver->autostartDir) < 0) {
-        lxcShutdown();
-        return -1;
-    }
+                                lxc_driver->autostartDir) < 0)
+        goto error;
 
     for (i = 0 ; i < lxc_driver->domains.count ; i++) {
         virDomainObjPtr vm = lxc_driver->domains.objs[i];
@@ -1058,7 +1189,13 @@ static int lxcStartup(void)
         }
     }
 
+    lxcDriverUnlock(lxc_driver);
     return 0;
+
+error:
+    lxcDriverUnlock(lxc_driver);
+    lxcShutdown();
+    return -1;
 }
 
 static void lxcFreeDriver(lxc_driver_t *driver)
@@ -1075,7 +1212,9 @@ static int lxcShutdown(void)
     if (lxc_driver == NULL)
         return(-1);
 
+    lxcDriverLock(lxc_driver);
     virDomainObjListFree(&lxc_driver->domains);
+    lxcDriverUnlock(lxc_driver);
     lxcFreeDriver(lxc_driver);
     lxc_driver = NULL;
 
@@ -1091,17 +1230,20 @@ static int lxcShutdown(void)
  */
 static int
 lxcActive(void) {
-    unsigned int i;
+    unsigned int i, active = 0;
 
     if (lxc_driver == NULL)
         return(0);
 
-    for (i = 0 ; i < lxc_driver->domains.count ; i++)
+    lxcDriverLock(lxc_driver);
+    for (i = 0 ; i < lxc_driver->domains.count ; i++) {
+        virDomainLock(lxc_driver->domains.objs[i]);
         if (virDomainIsActive(lxc_driver->domains.objs[i]))
-            return 1;
+            active = 1;
+        virDomainUnlock(lxc_driver->domains.objs[i]);
+    }
 
-    /* Otherwise we're happy to deal with a shutdown */
-    return 0;
+    return active;
 }
 
 static int lxcVersion(virConnectPtr conn, unsigned long *version)
@@ -1137,84 +1279,99 @@ static char *lxcGetSchedulerType(virDoma
     return strdup("posix");
 }
 
-static int lxcSetSchedulerParameters(virDomainPtr _domain,
+static int lxcSetSchedulerParameters(virDomainPtr domain,
                                      virSchedParameterPtr params,
                                      int nparams)
 {
+    lxc_driver_t *driver = domain->conn->privateData;
     int i;
-    int rc;
     virCgroupPtr group;
-    virDomainObjPtr domain;
+    virDomainObjPtr vm;
 
     if (virCgroupHaveSupport() != 0)
         return 0;
 
-    domain = virDomainFindByUUID(&lxc_driver->domains, _domain->uuid);
-    if (domain == NULL) {
+    lxcDriverLock(driver);
+    vm = virDomainFindByUUID(&lxc_driver->domains, domain->uuid);
+    lxcDriverUnlock(driver);
+
+    if (vm == NULL) {
         lxcError(NULL, _domain, VIR_ERR_INTERNAL_ERROR,
-                 _("No such domain %s"), _domain->uuid);
-        return -EINVAL;
+                 _("No such domain %s"), domain->uuid);
+        goto error;
     }
 
-    rc = virCgroupForDomain(domain->def, "lxc", &group);
-    if (rc != 0)
-        return rc;
+    if (virCgroupForDomain(vm->def, "lxc", &group) != 0)
+        goto error;
 
     for (i = 0; i < nparams; i++) {
         virSchedParameterPtr param = &params[i];
 
         if (STREQ(param->field, "cpu_shares")) {
-            rc = virCgroupSetCpuShares(group, params[i].value.ui);
+            if (virCgroupSetCpuShares(group, params[i].value.ui) != 0)
+                goto error;
         } else {
-            lxcError(NULL, _domain, VIR_ERR_INVALID_ARG,
+            lxcError(NULL, domain, VIR_ERR_INVALID_ARG,
                      _("Invalid parameter `%s'"), param->field);
-            rc = -ENOENT;
-            goto out;
+            goto error;
         }
     }
 
-    rc = 0;
-out:
     virCgroupFree(&group);
+    return 0;
 
-    return rc;
+error:
+    if (group)
+        virCgroupFree(&group);
+    if (vm)
+        virDomainUnlock(vm);
+    lxcDriverUnlock(driver);
+    return -1;
 }
 
-static int lxcGetSchedulerParameters(virDomainPtr _domain,
+static int lxcGetSchedulerParameters(virDomainPtr domain,
                                      virSchedParameterPtr params,
                                      int *nparams)
 {
-    int rc = 0;
+    lxc_driver_t *driver = domain->conn->privateData;
     virCgroupPtr group;
-    virDomainObjPtr domain;
+    virDomainObjPtr vm;
 
     if (virCgroupHaveSupport() != 0)
         return 0;
 
     if ((*nparams) != 1) {
-        lxcError(NULL, _domain, VIR_ERR_INVALID_ARG,
+        lxcError(NULL, domain, VIR_ERR_INVALID_ARG,
                  "%s", _("Invalid parameter count"));
-        return -1;
+        goto error;
     }
 
-    domain = virDomainFindByUUID(&lxc_driver->domains, _domain->uuid);
-    if (domain == NULL) {
-        lxcError(NULL, _domain, VIR_ERR_INTERNAL_ERROR,
-                 _("No such domain %s"), _domain->uuid);
-        return -ENOENT;
+    lxcDriverLock(driver);
+    vm = virDomainFindByUUID(&lxc_driver->domains, domain->uuid);
+    lxcDriverUnlock(driver);
+    if (vm == NULL) {
+        lxcError(NULL, domain, VIR_ERR_INTERNAL_ERROR,
+                 _("No such domain %s"), domain->uuid);
+        goto error;
     }
 
-    rc = virCgroupForDomain(domain->def, "lxc", &group);
-    if (rc != 0)
-        return rc;
+    if (virCgroupForDomain(vm->def, "lxc", &group) != 0)
+        goto error;
 
-    rc = virCgroupGetCpuShares(group, (unsigned long *)&params[0].value.ul);
+    if (virCgroupGetCpuShares(group, (unsigned long *)&params[0].value.ul) != 0)
+        goto error;
+
     strncpy(params[0].field, "cpu_shares", sizeof(params[0].field));
     params[0].type = VIR_DOMAIN_SCHED_FIELD_ULLONG;
 
     virCgroupFree(&group);
 
-    return rc;
+    return 0;
+
+error:
+    if (vm)
+        virDomainUnlock(vm);
+    return -1;
 }
 
 /* Function Tables */


-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.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