]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
Added logical volume storage pool driver
authorDaniel P. Berrange <berrange@redhat.com>
Wed, 20 Feb 2008 15:45:33 +0000 (15:45 +0000)
committerDaniel P. Berrange <berrange@redhat.com>
Wed, 20 Feb 2008 15:45:33 +0000 (15:45 +0000)
ChangeLog
configure.in
libvirt.spec.in
po/POTFILES.in
src/Makefile.am
src/storage_backend.c
src/storage_backend_logical.c [new file with mode: 0644]
src/storage_backend_logical.h [new file with mode: 0644]

index 8bf04fbcfebca1d255b721d47b6aeb8af50c5c82..d79bb4b91bd794bb248bd7f343239d2c3ded9074 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+Wed Feb 20 10:39:27 EST 2008 Daniel P. Berrange <berrange@redhat.com>
+
+       * configure.in: Added checks for LVM tools
+       * docs/storage/pool-logical.xml: Example XML description
+       * libvirt.spec.in: Added dep on LVM tools
+       * po/POTFILES.in: Added storage_backend_logical.c
+       * src/Makefile.am, src/storage_backend.c,
+       src/storage_backend_logical.c, src/storage_backend_logical.h:
+       Added logical volume backend
+
 Wed Feb 20 10:32:27 EST 2008 Daniel P. Berrange <berrange@redhat.com>
 
        * configure.in: Add checks for mount/unmount/qemu-img/qcow-create
index 5c75e7f2e30600045ec5ce7e129f3f4582632a1b..2cdc80ac01cfc5084c2f90253b92e18834640dfc 100644 (file)
@@ -559,6 +559,8 @@ dnl
 
 AC_ARG_WITH(storage-fs,
 [  --with-storage-fs           with FileSystem backend for the storage driver (on)],[],[with_storage_fs=check])
+AC_ARG_WITH(storage-lvm,
+[  --with-storage-lvm          with LVM backend for the storage driver (on)],[],[with_storage_lvm=check])
 
 if test "$with_storage_fs" = "yes" -o "$with_storage_fs" = "check"; then
   AC_PATH_PROG(MOUNT, [mount], [], [$PATH:/sbin:/usr/sbin])
@@ -598,6 +600,61 @@ if test -n "$QCOW_CREATE" ; then
 fi
 
 
+if test "$with_storage_lvm" = "yes" -o "$with_storage_lvm" = "check"; then
+  AC_PATH_PROG(PVCREATE, [pvcreate], [], [$PATH:/sbin:/usr/sbin])
+  AC_PATH_PROG(VGCREATE, [vgcreate], [], [$PATH:/sbin:/usr/sbin])
+  AC_PATH_PROG(LVCREATE, [lvcreate], [], [$PATH:/sbin:/usr/sbin])
+  AC_PATH_PROG(PVREMOVE, [pvremove], [], [$PATH:/sbin:/usr/sbin])
+  AC_PATH_PROG(VGREMOVE, [vgremove], [], [$PATH:/sbin:/usr/sbin])
+  AC_PATH_PROG(LVREMOVE, [lvremove], [], [$PATH:/sbin:/usr/sbin])
+  AC_PATH_PROG(VGCHANGE, [vgchange], [], [$PATH:/sbin:/usr/sbin])
+  AC_PATH_PROG(PVS, [pvs], [], [$PATH:/sbin:/usr/sbin])
+  AC_PATH_PROG(VGS, [vgs], [], [$PATH:/sbin:/usr/sbin])
+  AC_PATH_PROG(LVS, [lvs], [], [$PATH:/sbin:/usr/sbin])
+
+  if test "$with_storage_lvm" = "yes" ; then
+    if test -z "$PVCREATE" ; then AC_MSG_ERROR(We need pvcreate for LVM storage driver) ; fi
+    if test -z "$VGCREATE" ; then AC_MSG_ERROR(We need vgcreate for LVM storage driver) ; fi
+    if test -z "$LVCREATE" ; then AC_MSG_ERROR(We need lvcreate for LVM storage driver) ; fi
+    if test -z "$PVREMOVE" ; then AC_MSG_ERROR(We need pvremove for LVM storage driver) ; fi
+    if test -z "$VGREMOVE" ; then AC_MSG_ERROR(We need vgremove for LVM storage driver) ; fi
+    if test -z "$LVREMOVE" ; then AC_MSG_ERROR(We need lvremove for LVM storage driver) ; fi
+    if test -z "$VGCHANGE" ; then AC_MSG_ERROR(We need vgchange for LVM storage driver) ; fi
+    if test -z "$PVS" ; then AC_MSG_ERROR(We need pvs for LVM storage driver) ; fi
+    if test -z "$VGS" ; then AC_MSG_ERROR(We need vgs for LVM storage driver) ; fi
+    if test -z "$LVS" ; then AC_MSG_ERROR(We need lvs for LVM storage driver) ; fi
+  else
+    if test -z "$PVCREATE" ; then with_storage_lvm=no ; fi
+    if test -z "$VGCREATE" ; then with_storage_lvm=no ; fi
+    if test -z "$LVCREATE" ; then with_storage_lvm=no ; fi
+    if test -z "$PVREMOVE" ; then with_storage_lvm=no ; fi
+    if test -z "$VGREMOVE" ; then with_storage_lvm=no ; fi
+    if test -z "$LVREMOVE" ; then with_storage_lvm=no ; fi
+    if test -z "VGCHANGE" ; then with_storage_lvm=no ; fi
+    if test -z "$PVS" ; then with_storage_lvm=no ; fi
+    if test -z "$VGS" ; then with_storage_lvm=no ; fi
+    if test -z "$LVS" ; then with_storage_lvm=no ; fi
+
+    if test "$with_storage_lvm" = "check" ; then with_storage_lvm=yes ; fi
+  fi
+
+  if test "$with_storage_lvm" = "yes" ; then
+    AC_DEFINE_UNQUOTED(WITH_STORAGE_LVM, 1, [whether LVM backend for storage driver is enabled])
+    AC_DEFINE_UNQUOTED([PVCREATE],["$PVCREATE"],[Location of pvcreate program])
+    AC_DEFINE_UNQUOTED([VGCREATE],["$VGCREATE"],[Location of vgcreate program])
+    AC_DEFINE_UNQUOTED([LVCREATE],["$LVCREATE"],[Location of lvcreate program])
+    AC_DEFINE_UNQUOTED([PVREMOVE],["$PVREMOVE"],[Location of pvcreate program])
+    AC_DEFINE_UNQUOTED([VGREMOVE],["$VGREMOVE"],[Location of vgcreate program])
+    AC_DEFINE_UNQUOTED([LVREMOVE],["$LVREMOVE"],[Location of lvcreate program])
+    AC_DEFINE_UNQUOTED([VGCHANGE],["$VGCHANGE"],[Location of vgchange program])
+    AC_DEFINE_UNQUOTED([PVS],["$PVS"],[Location of pvs program])
+    AC_DEFINE_UNQUOTED([VGS],["$VGS"],[Location of vgs program])
+    AC_DEFINE_UNQUOTED([LVS],["$LVS"],[Location of lvs program])
+  fi
+fi
+AM_CONDITIONAL(WITH_STORAGE_LVM, [test "$with_storage_lvm" = "yes"])
+
+
 dnl
 dnl check for python
 dnl
@@ -811,6 +868,7 @@ AC_MSG_NOTICE([])
 AC_MSG_NOTICE([     Dir: yes])
 AC_MSG_NOTICE([      FS: $with_storage_fs])
 AC_MSG_NOTICE([   NetFS: $with_storage_fs])
+AC_MSG_NOTICE([     LVM: $with_storage_lvm])
 AC_MSG_NOTICE([])
 AC_MSG_NOTICE([Libraries])
 AC_MSG_NOTICE([])
index 677b40698a04b89acc7d3297f2727d5efd055896..a95b22ac125c045b12834ce927ef549bf84aecef 100644 (file)
@@ -49,6 +49,8 @@ Requires: /usr/bin/qemu-img
 # From Xen RPMs
 Requires: /usr/sbin/qcow-create
 %endif
+# For LVM drivers
+Requires: lvm2
 BuildRequires: xen-devel
 BuildRequires: libxml2-devel
 BuildRequires: readline-devel
@@ -73,6 +75,8 @@ BuildRequires: /usr/bin/qemu-img
 # From Xen RPMs
 BuildRequires: /usr/sbin/qcow-create
 %endif
+# For LVM drivers
+BuildRequires: lvm2
 Obsoletes: libvir
 ExclusiveArch: i386 x86_64 ia64
 
index e0a917f3bc754ca325dd6e6df5fec20d0c3be46a..3f2351e0748fcb3a9896ff98c6aa7ab1b29247e2 100644 (file)
@@ -12,6 +12,7 @@ src/qemu_driver.c
 src/remote_internal.c
 src/storage_backend.c
 src/storage_backend_fs.c
+src/storage_backend_logical.c
 src/storage_conf.c
 src/storage_driver.c
 src/sexpr.c
index 38efa66734396b58997b4462fbdc64bd6585fbb7..f95aad2fb1b37c39e944bfcc15513ea3462bfa9a 100644 (file)
@@ -68,6 +68,14 @@ CLIENT_SOURCES =                                             \
 SERVER_SOURCES =                                               \
                ../qemud/remote_protocol.c ../qemud/remote_protocol.h
 
+if WITH_STORAGE_LVM
+CLIENT_SOURCES += storage_backend_logical.h storage_backend_logical.c
+else
+EXTRA_DIST += storage_backend_logical.h storage_backend_logical.c
+endif
+
+
+
 libvirt_la_SOURCES = $(CLIENT_SOURCES) $(SERVER_SOURCES)
 libvirt_la_LIBADD = $(LIBXML_LIBS) $(GNUTLS_LIBS) $(SASL_LIBS) $(SELINUX_LIBS) \
                    @CYGWIN_EXTRA_LIBADD@ ../gnulib/lib/libgnu.la
index 4d3a8cb53d1298b6dfb5baa4b478269038423f6f..698e1c9c5539ef95e2a7fb3abb1d834ed3f2e3f8 100644 (file)
@@ -36,6 +36,9 @@
 #if HAVE_SELINUX
 #include <selinux/selinux.h>
 #endif
+#if WITH_STORAGE_LVM
+#include "storage_backend_logical.h"
+#endif
 
 #include "util.h"
 
@@ -48,6 +51,9 @@ static virStorageBackendPtr backends[] = {
     &virStorageBackendFileSystem,
     &virStorageBackendNetFileSystem,
 #endif
+#if WITH_STORAGE_LVM
+    &virStorageBackendLogical,
+#endif
 };
 
 
@@ -90,6 +96,10 @@ virStorageBackendFromString(const char *type) {
     if (STREQ(type, "netfs"))
         return VIR_STORAGE_POOL_NETFS;
 #endif
+#if WITH_STORAGE_LVM
+    if (STREQ(type, "logical"))
+        return VIR_STORAGE_POOL_LOGICAL;
+#endif
 
     virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR,
                           _("unknown storage backend type %s"), type);
@@ -106,6 +116,10 @@ virStorageBackendToString(int type) {
         return "fs";
     case VIR_STORAGE_POOL_NETFS:
         return "netfs";
+#endif
+#if WITH_STORAGE_LVM
+    case VIR_STORAGE_POOL_LOGICAL:
+        return "logical";
 #endif
     }
 
diff --git a/src/storage_backend_logical.c b/src/storage_backend_logical.c
new file mode 100644 (file)
index 0000000..c14e788
--- /dev/null
@@ -0,0 +1,531 @@
+/*
+ * storage_backend_logvol.c: storage backend for logical volume 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 <sys/wait.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <regex.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "storage_backend_logical.h"
+#include "storage_conf.h"
+#include "util.h"
+
+
+#define PV_BLANK_SECTOR_SIZE 512
+
+enum {
+    VIR_STORAGE_POOL_LOGICAL_LVM2 = 0,
+};
+
+
+static int
+virStorageBackendLogicalPoolFormatFromString(virConnectPtr conn,
+                                             const char *format) {
+    if (format == NULL)
+        return VIR_STORAGE_POOL_LOGICAL_LVM2;
+
+    if (STREQ(format, "lvm2"))
+        return VIR_STORAGE_POOL_LOGICAL_LVM2;
+
+    virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                          _("unsupported pool format %s"), format);
+    return -1;
+}
+
+static const char *
+virStorageBackendLogicalPoolFormatToString(virConnectPtr conn,
+                                           int format) {
+    switch (format) {
+    case VIR_STORAGE_POOL_LOGICAL_LVM2:
+        return "lvm2";
+    }
+
+    virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                          _("unsupported pool format %d"), format);
+    return NULL;
+}
+
+static int
+virStorageBackendLogicalSetActive(virConnectPtr conn,
+                                  virStoragePoolObjPtr pool,
+                                  int on)
+{
+    const char *cmdargv[4];
+
+    cmdargv[0] = VGCHANGE;
+    cmdargv[1] = on ? "-ay" : "-an";
+    cmdargv[2] = pool->def->name;
+    cmdargv[3] = NULL;
+
+    if (virRun(conn, (char**)cmdargv, NULL) < 0)
+        return -1;
+
+    return 0;
+}
+
+
+static int
+virStorageBackendLogicalMakeVol(virConnectPtr conn,
+                                virStoragePoolObjPtr pool,
+                                char **const groups,
+                                void *data)
+{
+    virStorageVolDefPtr vol = NULL;
+    virStorageVolSourceExtentPtr tmp;
+    unsigned long long offset, size, length;
+
+    /* See if we're only looking for a specific volume */
+    if (data != NULL) {
+        vol = data;
+        if (STRNEQ(vol->name, groups[0]))
+            return 0;
+    }
+
+    /* Or filling in more data on an existing volume */
+    if (vol == NULL)
+        vol = virStorageVolDefFindByName(pool, groups[0]);
+
+    /* Or a completely new volume */
+    if (vol == NULL) {
+        if ((vol = calloc(1, sizeof(*vol))) == NULL) {
+            virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("volume"));
+            return -1;
+        }
+
+        if ((vol->name = strdup(groups[0])) == NULL) {
+            virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("volume"));
+            return -1;
+        }
+
+        vol->next = pool->volumes;
+        pool->volumes = vol;
+        pool->nvolumes++;
+    }
+
+    if (vol->target.path == NULL) {
+        if ((vol->target.path = malloc(strlen(pool->def->target.path) +
+                                       1 + strlen(vol->name) + 1)) == NULL) {
+            virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("volume"));
+            return -1;
+        }
+        strcpy(vol->target.path, pool->def->target.path);
+        strcat(vol->target.path, "/");
+        strcat(vol->target.path, vol->name);
+    }
+
+    if (vol->key == NULL &&
+        (vol->key = strdup(groups[1])) == NULL) {
+        virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("volume"));
+        return -1;
+    }
+
+    if (virStorageBackendUpdateVolInfo(conn, vol, 1) < 0)
+        return -1;
+
+
+    /* Finally fill in extents information */
+    if ((tmp = realloc(vol->source.extents, sizeof(*tmp)
+                       * (vol->source.nextent + 1))) == NULL) {
+        virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("extents"));
+        return -1;
+    }
+    vol->source.extents = tmp;
+
+    if ((vol->source.extents[vol->source.nextent].path =
+         strdup(groups[2])) == NULL) {
+        virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("extents"));
+        return -1;
+    }
+
+    if (virStrToLong_ull(groups[3], NULL, 10, &offset) < 0) {
+        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                              _("malformed volume extent offset value"));
+        return -1;
+    }
+    if (virStrToLong_ull(groups[4], NULL, 10, &length) < 0) {
+        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                              _("malformed volume extent length value"));
+        return -1;
+    }
+    if (virStrToLong_ull(groups[5], NULL, 10, &size) < 0) {
+        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                              _("malformed volume extent size value"));
+        return -1;
+    }
+
+    vol->source.extents[vol->source.nextent].start = offset * size;
+    vol->source.extents[vol->source.nextent].end = (offset * size) + length;
+    vol->source.nextent++;
+
+    return 0;
+}
+
+static int
+virStorageBackendLogicalFindLVs(virConnectPtr conn,
+                                virStoragePoolObjPtr pool,
+                                virStorageVolDefPtr vol)
+{
+    /*
+     *  # lvs --separator : --noheadings --units b --unbuffered --nosuffix --options "lv_name,uuid,devices,seg_size,vg_extent_size" VGNAME
+     *  RootLV:06UgP5-2rhb-w3Bo-3mdR-WeoL-pytO-SAa2ky:/dev/hda2(0):5234491392:33554432
+     *  SwapLV:oHviCK-8Ik0-paqS-V20c-nkhY-Bm1e-zgzU0M:/dev/hda2(156):1040187392:33554432
+     *  Test2:3pg3he-mQsA-5Sui-h0i6-HNmc-Cz7W-QSndcR:/dev/hda2(219):1073741824:33554432
+     *  Test3:UB5hFw-kmlm-LSoX-EI1t-ioVd-h7GL-M0W8Ht:/dev/hda2(251):2181038080:33554432
+     *  Test3:UB5hFw-kmlm-LSoX-EI1t-ioVd-h7GL-M0W8Ht:/dev/hda2(187):1040187392:33554432
+     *
+     * Pull out name & uuid, device, device extent start #, segment size, extent size.
+     *
+     * NB can be multiple rows per volume if they have many extents
+     */
+    const char *regexes[] = {
+        "^\\s*(\\S+):(\\S+):(\\S+)\\((\\S+)\\):(\\S+):(\\S+)\\s*$"
+    };
+    int vars[] = {
+        6
+    };
+    const char *prog[] = {
+        LVS, "--separator", ":", "--noheadings", "--units", "b",
+        "--unbuffered", "--nosuffix", "--options",
+        "lv_name,uuid,devices,seg_size,vg_extent_size",
+        pool->def->name, NULL
+    };
+
+    return virStorageBackendRunProgRegex(conn,
+                                         pool,
+                                         prog,
+                                         1,
+                                         regexes,
+                                         vars,
+                                         virStorageBackendLogicalMakeVol,
+                                         vol);
+}
+
+static int
+virStorageBackendLogicalRefreshPoolFunc(virConnectPtr conn ATTRIBUTE_UNUSED,
+                                        virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
+                                        char **const groups,
+                                        void *data ATTRIBUTE_UNUSED)
+{
+    if (virStrToLong_ull(groups[0], NULL, 10, &pool->def->capacity) < 0)
+        return -1;
+    if (virStrToLong_ull(groups[1], NULL, 10, &pool->def->available) < 0)
+        return -1;
+    pool->def->allocation = pool->def->capacity - pool->def->available;
+
+    return 0;
+}
+
+
+static int
+virStorageBackendLogicalStartPool(virConnectPtr conn,
+                                  virStoragePoolObjPtr pool)
+{
+    if (virStorageBackendLogicalSetActive(conn, pool, 1) < 0)
+        return -1;
+
+    return 0;
+}
+
+
+static int
+virStorageBackendLogicalBuildPool(virConnectPtr conn,
+                                  virStoragePoolObjPtr pool,
+                                  unsigned int flags ATTRIBUTE_UNUSED)
+{
+    const char **vgargv;
+    const char *pvargv[3];
+    int n = 0, i, fd;
+    char zeros[PV_BLANK_SECTOR_SIZE];
+
+    memset(zeros, 0, sizeof(zeros));
+
+    /* XXX multiple pvs */
+    if ((vgargv = malloc(sizeof(char*) * (1))) == NULL) {
+        virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("command line"));
+        return -1;
+    }
+
+    vgargv[n++] = VGCREATE;
+    vgargv[n++] = pool->def->name;
+
+    pvargv[0] = PVCREATE;
+    pvargv[2] = NULL;
+    for (i = 0 ; i < pool->def->source.ndevice ; i++) {
+        /*
+         * LVM requires that the first sector is blanked if using
+         * a whole disk as a PV. So we just blank them out regardless
+         * rather than trying to figure out if we're a disk or partition
+         */
+        if ((fd = open(pool->def->source.devices[i].path, O_WRONLY)) < 0) {
+            virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                  _("cannot open device %s"),
+                                  strerror(errno));
+            goto cleanup;
+        }
+        if (write(fd, zeros, sizeof(zeros)) != sizeof(zeros)) {
+            virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                  _("cannot clear device header %s"),
+                                  strerror(errno));
+            close(fd);
+            goto cleanup;
+        }
+        if (close(fd) < 0) {
+            virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                  _("cannot close device %s"),
+                                  strerror(errno));
+            goto cleanup;
+        }
+
+        /*
+         * Initialize the physical volume because vgcreate is not
+         * clever enough todo this for us :-(
+         */
+        vgargv[n++] = pool->def->source.devices[i].path;
+        pvargv[1] = pool->def->source.devices[i].path;
+        if (virRun(conn, (char**)pvargv, NULL) < 0)
+            goto cleanup;
+    }
+
+    vgargv[n++] = NULL;
+
+    /* Now create the volume group itself */
+    if (virRun(conn, (char**)vgargv, NULL) < 0)
+        goto cleanup;
+
+    free(vgargv);
+
+    return 0;
+
+ cleanup:
+    free(vgargv);
+    return -1;
+}
+
+
+static int
+virStorageBackendLogicalRefreshPool(virConnectPtr conn,
+                                    virStoragePoolObjPtr pool)
+{
+    /*
+     *  # vgs --separator : --noheadings --units b --unbuffered --nosuffix --options "vg_size,vg_free" VGNAME
+     *    10603200512:4328521728
+     *
+     * Pull out size & free
+     */
+    const char *regexes[] = {
+        "^\\s*(\\S+):(\\S+)\\s*$"
+    };
+    int vars[] = {
+        2
+    };
+    const char *prog[] = {
+        VGS, "--separator", ":", "--noheadings", "--units", "b", "--unbuffered",
+        "--nosuffix", "--options", "vg_size,vg_free",
+        pool->def->name, NULL
+    };
+
+    /* Get list of all logical volumes */
+    if (virStorageBackendLogicalFindLVs(conn, pool, NULL) < 0) {
+        virStoragePoolObjClearVols(pool);
+        return -1;
+    }
+
+    /* Now get basic volgrp metadata */
+    if (virStorageBackendRunProgRegex(conn,
+                                      pool,
+                                      prog,
+                                      1,
+                                      regexes,
+                                      vars,
+                                      virStorageBackendLogicalRefreshPoolFunc,
+                                      NULL) < 0) {
+        virStoragePoolObjClearVols(pool);
+        return -1;
+    }
+
+    return 0;
+}
+
+
+/* XXX should we set LVM to inactive ? Probably not - it would
+ * suck if this were your LVM root fs :-)
+ */
+#if 0
+static int
+virStorageBackendLogicalStopPool(virConnectPtr conn,
+                                 virStoragePoolObjPtr pool)
+{
+    if (virStorageBackendLogicalSetActive(conn, pool, 0) < 0)
+        return -1;
+
+    return 0;
+}
+#endif
+
+static int
+virStorageBackendLogicalDeletePool(virConnectPtr conn,
+                                   virStoragePoolObjPtr pool,
+                                   unsigned int flags ATTRIBUTE_UNUSED)
+{
+    const char *cmdargv[] = {
+        VGREMOVE, "-f", pool->def->name, NULL
+    };
+
+    if (virRun(conn, (char**)cmdargv, NULL) < 0)
+        return -1;
+
+    /* XXX clear the PVs too ? ie pvremove ? probably ought to */
+
+    return 0;
+}
+
+
+static int
+virStorageBackendLogicalDeleteVol(virConnectPtr conn,
+                                  virStoragePoolObjPtr pool,
+                                  virStorageVolDefPtr vol,
+                                  unsigned int flags);
+
+
+static int
+virStorageBackendLogicalCreateVol(virConnectPtr conn,
+                                  virStoragePoolObjPtr pool,
+                                  virStorageVolDefPtr vol)
+{
+    int fd = -1;
+    char size[100];
+    const char *cmdargv[] = {
+        LVCREATE, "--name", vol->name, "-L", size,
+        pool->def->target.path, NULL
+    };
+
+    snprintf(size, sizeof(size)-1, "%lluK", vol->capacity/1024);
+    size[sizeof(size)-1] = '\0';
+
+    if (virRun(conn, (char**)cmdargv, NULL) < 0)
+        return -1;
+
+    if ((fd = open(vol->target.path, O_RDONLY)) < 0) {
+        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                              _("cannot read path '%s': %s"),
+                              vol->target.path, strerror(errno));
+        goto cleanup;
+    }
+
+    /* We can only chown/grp if root */
+    if (getuid() == 0) {
+        if (fchown(fd, vol->target.perms.uid, vol->target.perms.gid) < 0) {
+            virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                  _("cannot set file owner '%s': %s"),
+                                  vol->target.path, strerror(errno));
+            goto cleanup;
+        }
+    }
+    if (fchmod(fd, vol->target.perms.mode) < 0) {
+        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                              _("cannot set file mode '%s': %s"),
+                              vol->target.path, strerror(errno));
+        goto cleanup;
+    }
+
+    if (close(fd) < 0) {
+        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                              _("cannot close file '%s': %s"),
+                              vol->target.path, strerror(errno));
+        goto cleanup;
+    }
+    fd = -1;
+
+    /* Fill in data about this new vol */
+    if (virStorageBackendLogicalFindLVs(conn, pool, vol) < 0) {
+        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                              _("cannot find newly created volume '%s': %s"),
+                              vol->target.path, strerror(errno));
+        goto cleanup;
+    }
+
+    return 0;
+
+ cleanup:
+    if (fd != -1)
+        close(fd);
+    virStorageBackendLogicalDeleteVol(conn, pool, vol, 0);
+    return -1;
+}
+
+static int
+virStorageBackendLogicalDeleteVol(virConnectPtr conn,
+                                  virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
+                                  virStorageVolDefPtr vol,
+                                  unsigned int flags ATTRIBUTE_UNUSED)
+{
+    const char *cmdargv[] = {
+        LVREMOVE, "-f", vol->target.path, NULL
+    };
+
+    if (virRun(conn, (char**)cmdargv, NULL) < 0)
+        return -1;
+
+    return 0;
+}
+
+
+virStorageBackend virStorageBackendLogical = {
+    .type = VIR_STORAGE_POOL_LOGICAL,
+
+    .startPool = virStorageBackendLogicalStartPool,
+    .buildPool = virStorageBackendLogicalBuildPool,
+    .refreshPool = virStorageBackendLogicalRefreshPool,
+#if 0
+    .stopPool = virStorageBackendLogicalStopPool,
+#endif
+    .deletePool = virStorageBackendLogicalDeletePool,
+    .createVol = virStorageBackendLogicalCreateVol,
+    .deleteVol = virStorageBackendLogicalDeleteVol,
+
+    .poolOptions = {
+        .formatFromString = virStorageBackendLogicalPoolFormatFromString,
+        .formatToString = virStorageBackendLogicalPoolFormatToString,
+    },
+
+    .volType = VIR_STORAGE_VOL_BLOCK,
+};
+
+/*
+ * vim: set tabstop=4:
+ * vim: set shiftwidth=4:
+ * vim: set expandtab:
+ */
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff --git a/src/storage_backend_logical.h b/src/storage_backend_logical.h
new file mode 100644 (file)
index 0000000..b3fade4
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * storage_backend_logical.h: storage backend for logical volume 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_LOGICAL_H__
+#define __VIR_STORAGE_BACKEND_LOGICAL_H__
+
+#include "storage_backend.h"
+
+extern virStorageBackend virStorageBackendLogical;
+
+#endif /* __VIR_STORAGE_BACKEND_LOGVOL_H__ */
+
+/*
+ * vim: set tabstop=4:
+ * vim: set shiftwidth=4:
+ * vim: set expandtab:
+ */
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */