[libvirt] rebased multipath patch

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Here's the multipath patch, rebased against the current head.

Dave
>From 52cb0ff30dd5a52c5a246f3a2022f933cd2a0eab Mon Sep 17 00:00:00 2001
From: David Allan <dallan@xxxxxxxxxx>
Date: Tue, 21 Jul 2009 12:43:01 -0400
Subject: [PATCH] Add a simple pool type for multipath devices

This pool type contains volumes for the multipath devices that are present on the host.  It does not (yet) support any sort of multipath configuration, so that aspect of system administration must still be done at host build time.
---
 configure.in                |   24 +++
 po/POTFILES.in              |    1 +
 src/Makefile.am             |    9 +
 src/storage_backend.c       |   82 ++++++++++
 src/storage_backend.h       |    5 +-
 src/storage_backend_mpath.c |  352 +++++++++++++++++++++++++++++++++++++++++++
 src/storage_backend_mpath.h |   31 ++++
 src/storage_conf.c          |    7 +-
 src/storage_conf.h          |    1 +
 9 files changed, 510 insertions(+), 2 deletions(-)
 create mode 100644 src/storage_backend_mpath.c
 create mode 100644 src/storage_backend_mpath.h

diff --git a/configure.in b/configure.in
index d28c44a..7c93ab7 100644
--- a/configure.in
+++ b/configure.in
@@ -1046,6 +1046,8 @@ AC_ARG_WITH([storage-iscsi],
 [  --with-storage-iscsi        with iSCSI backend for the storage driver (on)],[],[with_storage_iscsi=check])
 AC_ARG_WITH([storage-scsi],
 [  --with-storage-scsi         with SCSI backend for the storage driver (on)],[],[with_storage_scsi=check])
+AC_ARG_WITH([storage-mpath],
+[  --with-storage-mpath        with mpath backend for the storage driver (on)],[],[with_storage_mpath=check])
 AC_ARG_WITH([storage-disk],
 [  --with-storage-disk         with GPartd Disk backend for the storage driver (on)],[],[with_storage_disk=check])
 
@@ -1056,6 +1058,7 @@ if test "$with_libvirtd" = "no"; then
   with_storage_lvm=no
   with_storage_iscsi=no
   with_storage_scsi=no
+  with_storage_mpath=no
   with_storage_disk=no
 fi
 if test "$with_storage_dir" = "yes" ; then
@@ -1177,6 +1180,26 @@ if test "$with_storage_scsi" = "check"; then
 fi
 AM_CONDITIONAL([WITH_STORAGE_SCSI], [test "$with_storage_scsi" = "yes"])
 
+if test "$with_storage_mpath" = "check"; then
+   with_storage_mpath=yes
+
+   AC_DEFINE_UNQUOTED([WITH_STORAGE_MPATH], 1,
+     [whether mpath backend for storage driver is enabled])
+fi
+AM_CONDITIONAL([WITH_STORAGE_MPATH], [test "$with_storage_mpath" = "yes"])
+
+if test "$with_storage_mpath" = "yes"; then
+   DEVMAPPER_REQUIRED=0.0
+   DEVMAPPER_CFLAGS=
+   DEVMAPPER_LIBS=
+   PKG_CHECK_MODULES(DEVMAPPER, devmapper >= $DEVMAPPER_REQUIRED,
+    [], [
+    AC_MSG_ERROR(
+    [You must install device-mapper-devel >= $DEVMAPPER_REQUIRED to compile libvirt])
+    ])
+fi
+AC_SUBST([DEVMAPPER_CFLAGS])
+AC_SUBST([DEVMAPPER_LIBS])
 
 LIBPARTED_CFLAGS=
 LIBPARTED_LIBS=
@@ -1677,6 +1700,7 @@ AC_MSG_NOTICE([   NetFS: $with_storage_fs])
 AC_MSG_NOTICE([     LVM: $with_storage_lvm])
 AC_MSG_NOTICE([   iSCSI: $with_storage_iscsi])
 AC_MSG_NOTICE([    SCSI: $with_storage_scsi])
+AC_MSG_NOTICE([   mpath: $with_storage_mpath])
 AC_MSG_NOTICE([    Disk: $with_storage_disk])
 AC_MSG_NOTICE([])
 AC_MSG_NOTICE([Security Drivers])
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 0a53dab..1586368 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -38,6 +38,7 @@ src/storage_backend_fs.c
 src/storage_backend_iscsi.c
 src/storage_backend_logical.c
 src/storage_backend_scsi.c
+src/storage_backend_mpath.c
 src/storage_conf.c
 src/storage_driver.c
 src/storage_encryption_conf.c
diff --git a/src/Makefile.am b/src/Makefile.am
index bedeb84..cf3420b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -199,6 +199,9 @@ STORAGE_DRIVER_ISCSI_SOURCES =					\
 STORAGE_DRIVER_SCSI_SOURCES =					\
 		storage_backend_scsi.h storage_backend_scsi.c
 
+STORAGE_DRIVER_MPATH_SOURCES =					\
+		storage_backend_mpath.h storage_backend_mpath.c
+
 STORAGE_DRIVER_DISK_SOURCES =					\
 		storage_backend_disk.h storage_backend_disk.c
 
@@ -478,6 +481,10 @@ if WITH_STORAGE_SCSI
 libvirt_driver_storage_la_SOURCES += $(STORAGE_DRIVER_SCSI_SOURCES)
 endif
 
+if WITH_STORAGE_MPATH
+libvirt_driver_storage_la_SOURCES += $(STORAGE_DRIVER_MPATH_SOURCES)
+endif
+
 if WITH_STORAGE_DISK
 libvirt_driver_storage_la_SOURCES += $(STORAGE_DRIVER_DISK_SOURCES)
 endif
@@ -539,6 +546,7 @@ EXTRA_DIST +=							\
 		$(STORAGE_DRIVER_LVM_SOURCES)			\
 		$(STORAGE_DRIVER_ISCSI_SOURCES)			\
 		$(STORAGE_DRIVER_SCSI_SOURCES)			\
+		$(STORAGE_DRIVER_MPATH_SOURCES)			\
 		$(STORAGE_DRIVER_DISK_SOURCES)			\
 		$(NODE_DEVICE_DRIVER_SOURCES)			\
 		$(NODE_DEVICE_DRIVER_HAL_SOURCES)		\
@@ -607,6 +615,7 @@ libvirt_la_LDFLAGS = $(VERSION_SCRIPT_FLAGS)libvirt.syms \
                     $(COVERAGE_CFLAGS:-f%=-Wc,-f%) \
                     $(LIBXML_LIBS) $(SELINUX_LIBS) \
 		    $(XEN_LIBS) $(DRIVER_MODULE_LIBS) \
+		    $(DEVMAPPER_LIBS) \
 		    @CYGWIN_EXTRA_LDFLAGS@ @MINGW_EXTRA_LDFLAGS@
 libvirt_la_CFLAGS = $(COVERAGE_CFLAGS) -DIN_LIBVIRT
 libvirt_la_DEPENDENCIES = $(libvirt_la_LIBADD) libvirt.syms
diff --git a/src/storage_backend.c b/src/storage_backend.c
index c818142..4dec06b 100644
--- a/src/storage_backend.c
+++ b/src/storage_backend.c
@@ -59,6 +59,9 @@
 #if WITH_STORAGE_SCSI
 #include "storage_backend_scsi.h"
 #endif
+#if WITH_STORAGE_MPATH
+#include "storage_backend_mpath.h"
+#endif
 #if WITH_STORAGE_DISK
 #include "storage_backend_disk.h"
 #endif
@@ -89,6 +92,9 @@ static virStorageBackendPtr backends[] = {
 #if WITH_STORAGE_SCSI
     &virStorageBackendSCSI,
 #endif
+#if WITH_STORAGE_MPATH
+    &virStorageBackendMpath,
+#endif
 #if WITH_STORAGE_DISK
     &virStorageBackendDisk,
 #endif
@@ -812,6 +818,82 @@ virStorageBackendUpdateVolTargetInfoFD(virConnectPtr conn,
     return 0;
 }
 
+
+struct diskType {
+    int part_table_type;
+    unsigned short offset;
+    unsigned short length;
+    unsigned long long magic;
+};
+
+
+static struct diskType const disk_types[] = {
+    { VIR_STORAGE_POOL_DISK_LVM2, 0x218, 8, 0x31303020324D564CULL },
+    { VIR_STORAGE_POOL_DISK_GPT,  0x200, 8, 0x5452415020494645ULL },
+    { VIR_STORAGE_POOL_DISK_DVH,  0x0,   4, 0x41A9E50BULL },
+    { VIR_STORAGE_POOL_DISK_MAC,  0x0,   2, 0x5245ULL },
+    { VIR_STORAGE_POOL_DISK_BSD,  0x40,  4, 0x82564557ULL },
+    { VIR_STORAGE_POOL_DISK_SUN,  0x1fc, 2, 0xBEDAULL },
+    /*
+     * NOTE: pc98 is funky; the actual signature is 0x55AA (just like dos), so
+     * we can't use that.  At the moment I'm relying on the "dummy" IPL
+     * bootloader data that comes from parted.  Luckily, the chances of running
+     * into a pc98 machine running libvirt are approximately nil.
+     */
+    /*{ 0x1fe, 2, 0xAA55UL },*/
+    { VIR_STORAGE_POOL_DISK_PC98, 0x0,   8, 0x314C5049000000CBULL },
+    /*
+     * NOTE: the order is important here; some other disk types (like GPT and
+     * and PC98) also have 0x55AA at this offset.  For that reason, the DOS
+     * one must be the last one.
+     */
+    { VIR_STORAGE_POOL_DISK_DOS,  0x1fe, 2, 0xAA55ULL },
+    { -1,                         0x0,   0, 0x0ULL },
+};
+
+
+int
+virStorageBackendUpdateVolTargetFormatFD(virConnectPtr conn,
+                                         virStorageVolTargetPtr target,
+                                         int fd)
+{
+    int i;
+    off_t start;
+    unsigned char buffer[1024];
+    ssize_t bytes;
+
+    /* make sure to set the target format "unknown" to begin with */
+    target->format = VIR_STORAGE_POOL_DISK_UNKNOWN;
+
+    start = lseek(fd, 0, SEEK_SET);
+    if (start < 0) {
+        virReportSystemError(conn, errno,
+                             _("cannot seek to beginning of file '%s'"),
+                             target->path);
+        return -1;
+    }
+    bytes = saferead(fd, buffer, sizeof(buffer));
+    if (bytes < 0) {
+        virReportSystemError(conn, errno,
+                             _("cannot read beginning of file '%s'"),
+                             target->path);
+        return -1;
+    }
+
+    for (i = 0; disk_types[i].part_table_type != -1; i++) {
+        if (disk_types[i].offset + disk_types[i].length > bytes)
+            continue;
+        if (memcmp(buffer+disk_types[i].offset, &disk_types[i].magic,
+            disk_types[i].length) == 0) {
+            target->format = disk_types[i].part_table_type;
+            break;
+        }
+    }
+
+    return 0;
+}
+
+
 void virStorageBackendWaitForDevices(virConnectPtr conn)
 {
     virWaitForDevices(conn);
diff --git a/src/storage_backend.h b/src/storage_backend.h
index e80b05d..eb5bf87 100644
--- a/src/storage_backend.h
+++ b/src/storage_backend.h
@@ -91,7 +91,10 @@ int virStorageBackendUpdateVolTargetInfoFD(virConnectPtr conn,
                                            int fd,
                                            unsigned long long *allocation,
                                            unsigned long long *capacity);
-
+int
+virStorageBackendUpdateVolTargetFormatFD(virConnectPtr conn,
+                                         virStorageVolTargetPtr target,
+                                         int fd);
 void virStorageBackendWaitForDevices(virConnectPtr conn);
 
 char *virStorageBackendStablePath(virConnectPtr conn,
diff --git a/src/storage_backend_mpath.c b/src/storage_backend_mpath.c
new file mode 100644
index 0000000..41dadf1
--- /dev/null
+++ b/src/storage_backend_mpath.c
@@ -0,0 +1,352 @@
+/*
+ * storage_backend_mpath.c: storage backend for multipath handling
+ *
+ * Copyright (C) 2007-2008 Red Hat, Inc.
+ * Copyright (C) 2007-2008 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: Daniel P. Berrange <berrange redhat com>
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <fcntl.h>
+
+#include <libdevmapper.h>
+
+#include "virterror_internal.h"
+#include "storage_conf.h"
+#include "storage_backend.h"
+#include "memory.h"
+#include "logging.h"
+
+#define VIR_FROM_THIS VIR_FROM_STORAGE
+
+static int
+virStorageBackendMpathUpdateVolTargetInfo(virConnectPtr conn,
+                                          virStorageVolTargetPtr target,
+                                          unsigned long long *allocation,
+                                          unsigned long long *capacity)
+{
+    int ret = 0;
+    int fd = -1;
+
+    if ((fd = open(target->path, O_RDONLY)) < 0) {
+        virReportSystemError(conn, errno,
+                             _("cannot open volume '%s'"),
+                             target->path);
+        ret = -1;
+        goto out;
+    }
+
+    if (virStorageBackendUpdateVolTargetInfoFD(conn,
+                                               target,
+                                               fd,
+                                               allocation,
+                                               capacity) < 0) {
+
+        VIR_ERROR(_("Failed to update volume target info for %s"),
+            target->path);
+
+        ret = -1;
+        goto out;
+    }
+
+    if (virStorageBackendUpdateVolTargetFormatFD(conn,
+                                                 target,
+                                                 fd) < 0) {
+        VIR_ERROR(_("Failed to update volume target format for %s"),
+            target->path);
+
+        ret = -1;
+        goto out;
+    }
+
+out:
+    if (fd != -1) {
+        close(fd);
+    }
+    return ret;
+}
+
+
+static int
+virStorageBackendMpathNewVol(virConnectPtr conn,
+                             virStoragePoolObjPtr pool,
+                             const int devnum,
+                             const char *dev)
+{
+    virStorageVolDefPtr vol;
+    int retval = 0;
+
+    if (VIR_ALLOC(vol) < 0) {
+        virReportOOMError(conn);
+        retval = -1;
+        goto out;
+    }
+
+    vol->type = VIR_STORAGE_VOL_BLOCK;
+
+    if (virAsprintf(&(vol->name), "dm-%u", devnum) < 0) {
+        virReportOOMError(conn);
+        retval = -1;
+        goto free_vol;
+    }
+
+    if (virAsprintf(&vol->target.path, "/dev/%s", dev) < 0) {
+        virReportOOMError(conn);
+        retval = -1;
+        goto free_vol;
+    }
+
+    if (virStorageBackendMpathUpdateVolTargetInfo(conn,
+                                                  &vol->target,
+                                                  &vol->allocation,
+                                                  &vol->capacity) < 0) {
+
+        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                              _("Failed to update volume for '%s'"),
+                              vol->target.path);
+        retval = -1;
+        goto free_vol;
+    }
+
+    /* XXX should use logical unit's UUID instead */
+    vol->key = strdup(vol->target.path);
+    if (vol->key == NULL) {
+        virReportOOMError(conn);
+        retval = -1;
+        goto free_vol;
+    }
+
+    pool->def->capacity += vol->capacity;
+    pool->def->allocation += vol->allocation;
+
+    if (VIR_REALLOC_N(pool->volumes.objs,
+                      pool->volumes.count + 1) < 0) {
+        virReportOOMError(conn);
+        retval = -1;
+        goto free_vol;
+    }
+    pool->volumes.objs[pool->volumes.count++] = vol;
+
+    goto out;
+
+free_vol:
+    virStorageVolDefFree(vol);
+out:
+    return retval;
+}
+
+
+static int
+virStorageBackendIsMultipath(const char *devname)
+{
+    int ret = 0;
+    struct dm_task *dmt = NULL;
+    void *next = NULL;
+    uint64_t start, length;
+    char *target_type = NULL;
+    char *params = NULL;
+
+    dmt = dm_task_create(DM_DEVICE_TABLE);
+    if (dmt == NULL) {
+        ret = -1;
+        goto out;
+    }
+
+    if (dm_task_set_name(dmt, devname) == 0) {
+        ret = -1;
+        goto out;
+    }
+
+    dm_task_no_open_count(dmt);
+
+    if (!dm_task_run(dmt)) {
+        ret = -1;
+        goto out;
+    }
+
+    next = dm_get_next_target(dmt, next, &start, &length,
+                              &target_type, &params);
+
+    if (target_type == NULL) {
+        ret = -1;
+        goto out;
+    }
+
+    if (!(strcmp(target_type, "multipath"))) {
+        ret = 1;
+    }
+
+out:
+    if (dmt != NULL) {
+        dm_task_destroy(dmt);
+    }
+    return ret;
+}
+
+
+static int
+virStorageBackendGetMinorNumber(const char *devname, uint32_t *minor)
+{
+	int ret = -1;
+	struct dm_task *dmt;
+	struct dm_info info;
+
+	if (!(dmt = dm_task_create(DM_DEVICE_INFO))) {
+		goto out;
+	}
+
+	if (!dm_task_set_name(dmt, devname)) {
+		goto out;
+	}
+
+	if (!dm_task_run(dmt)) {
+		goto out;
+	}
+
+	if (!dm_task_get_info(dmt, &info)) {
+		goto out;
+	}
+
+	*minor = info.minor;
+	ret = 0;
+
+out:
+	if (dmt != NULL) {
+		dm_task_destroy(dmt);
+	}
+
+	return ret;
+}
+
+
+static int
+virStorageBackendCreateVols(virConnectPtr conn,
+                            virStoragePoolObjPtr pool,
+                            struct dm_names *names)
+{
+    int retval = 0, is_mpath = 0;
+    char *map_device = NULL;
+    uint32_t minor = -1;
+
+    do {
+        is_mpath = virStorageBackendIsMultipath(names->name);
+
+        if (is_mpath < 0) {
+            retval = -1;
+            goto out;
+        }
+
+        if (is_mpath == 1) {
+
+            if (virAsprintf(&map_device, "mapper/%s", names->name) < 0) {
+                virReportOOMError(conn);
+                retval = -1;
+                goto out;
+            }
+
+            if (virStorageBackendGetMinorNumber(names->name, &minor) < 0) {
+                retval = -1;
+                goto out;
+            }
+
+            virStorageBackendMpathNewVol(conn,
+                                         pool,
+                                         minor,
+                                         map_device);
+
+            VIR_FREE(map_device);
+        }
+
+        /* Given the way libdevmapper returns its data, I don't see
+         * any way to avoid this series of casts. */
+        names = (struct dm_names *)(((char *)names) + names->next);
+
+    } while (names->next);
+
+out:
+    return retval;
+}
+
+
+static int
+virStorageBackendGetMaps(virConnectPtr conn,
+                         virStoragePoolObjPtr pool)
+{
+    int retval = 0;
+    struct dm_task *dmt = NULL;
+    struct dm_names *names = NULL;
+
+    if (!(dmt = dm_task_create(DM_DEVICE_LIST))) {
+        retval = 1;
+        goto out;
+    }
+
+    dm_task_no_open_count(dmt);
+
+    if (!dm_task_run(dmt)) {
+        retval = 1;
+        goto out;
+    }
+
+    if (!(names = dm_task_get_names(dmt))) {
+        retval = 1;
+        goto out;
+    }
+
+    if (!names->dev) {
+        /* No devices found */
+        goto out;
+    }
+
+    virStorageBackendCreateVols(conn, pool, names);
+
+out:
+    if (dmt != NULL) {
+        dm_task_destroy (dmt);
+    }
+    return retval;
+}
+
+
+static int
+virStorageBackendMpathRefreshPool(virConnectPtr conn,
+                                  virStoragePoolObjPtr pool)
+{
+    int retval = 0;
+
+    VIR_ERROR(_("in %s"), __func__);
+
+    pool->def->allocation = pool->def->capacity = pool->def->available = 0;
+
+    virStorageBackendWaitForDevices(conn);
+
+    virStorageBackendGetMaps(conn, pool);
+
+    return retval;
+}
+
+
+virStorageBackend virStorageBackendMpath = {
+    .type = VIR_STORAGE_POOL_MPATH,
+
+    .refreshPool = virStorageBackendMpathRefreshPool,
+};
diff --git a/src/storage_backend_mpath.h b/src/storage_backend_mpath.h
new file mode 100644
index 0000000..9de2b79
--- /dev/null
+++ b/src/storage_backend_mpath.h
@@ -0,0 +1,31 @@
+/*
+ * storage_backend_scsi.h: storage backend for SCSI handling
+ *
+ * Copyright (C) 2007-2008 Red Hat, Inc.
+ * Copyright (C) 2007-2008 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: Daniel P. Berrange <berrange redhat com>
+ */
+
+#ifndef __VIR_STORAGE_BACKEND_MPATH_H__
+#define __VIR_STORAGE_BACKEND_MPATH_H__
+
+#include "storage_backend.h"
+
+extern virStorageBackend virStorageBackendMpath;
+
+#endif /* __VIR_STORAGE_BACKEND_MPATH_H__ */
diff --git a/src/storage_conf.c b/src/storage_conf.c
index c446069..af04080 100644
--- a/src/storage_conf.c
+++ b/src/storage_conf.c
@@ -53,7 +53,7 @@ VIR_ENUM_IMPL(virStoragePool,
               VIR_STORAGE_POOL_LAST,
               "dir", "fs", "netfs",
               "logical", "disk", "iscsi",
-              "scsi")
+              "scsi", "mpath")
 
 VIR_ENUM_IMPL(virStoragePoolFormatFileSystem,
               VIR_STORAGE_POOL_FS_LAST,
@@ -198,6 +198,11 @@ static virStoragePoolTypeInfo poolTypeInfo[] = {
             .formatToString = virStoragePoolFormatDiskTypeToString,
         }
     },
+    { .poolType = VIR_STORAGE_POOL_MPATH,
+      .volOptions = {
+            .formatToString = virStoragePoolFormatDiskTypeToString,
+        }
+    },
     { .poolType = VIR_STORAGE_POOL_DISK,
       .poolOptions = {
             .flags = (VIR_STORAGE_POOL_SOURCE_DEVICE),
diff --git a/src/storage_conf.h b/src/storage_conf.h
index bcf9b93..421d305 100644
--- a/src/storage_conf.h
+++ b/src/storage_conf.h
@@ -119,6 +119,7 @@ enum virStoragePoolType {
     VIR_STORAGE_POOL_DISK,     /* Disk partitions */
     VIR_STORAGE_POOL_ISCSI,    /* iSCSI targets */
     VIR_STORAGE_POOL_SCSI,     /* SCSI HBA */
+    VIR_STORAGE_POOL_MPATH,    /* Multipath devices */
 
     VIR_STORAGE_POOL_LAST,
 };
-- 
1.6.0.6

--
Libvir-list mailing list
Libvir-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/libvir-list

[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]