On Tue, Feb 12, 2008 at 04:40:26AM +0000, Daniel P. Berrange wrote: > This implements a storage pool for partitioning local disks. > It uses parted as the tool for reading & writing disk partitions. > Since parted is GPLv3, we cannot link against the code directly. > So, this driver calls out to the regular 'parted' command line > tool for creationg/deletion. For listing of partiitons we have > a trivial helper program '/usr/libexec/libvirt_parthelper'. This > outputs partition listing in a easily parseable format - each > partition, or free space extent is a record, containing 5 fields. > Records and fields are separated by NULLs. The internal libvirt > helper API virStorageBackendRunProgNul can reliably parse this > data format (thanks to Jim for the suggestion of using NULLs > instead of whitespace, since the latter is not so easy to protect > against bogus files) > > Creating volumes from disk partitions is more complex than most > of the drivers. This is because partition tables have quite > strict placement constraints. There are free extent regions > and a partition must fall within one, and further more be aligned > to a suitable byte boundary. For this reason the pool XML format > will contain information about the free extents on a disk. The > volume XML will also detail the actual extents used by a partition > on disk. > > All of the parted partition table types are supported. A new disk > can be initialized with the 'build' operation which will write a > new partition table. When creating volumes a partition entry type > can be specified if required. For simply assigning volumes to guests > though, it is not neccessary to use anything other than the default > of 'none'. .hgignore | 1 b/src/parthelper.c | 127 ++++++++++ b/src/storage_backend_disk.c | 537 +++++++++++++++++++++++++++++++++++++++++++ b/src/storage_backend_disk.h | 45 +++ configure.in | 48 +++ libvirt.spec.in | 5 po/POTFILES.in | 1 src/Makefile.am | 18 + src/storage_backend.c | 14 + 9 files changed, 796 insertions(+) diff -r f54c0eb07193 .hgignore --- a/.hgignore Tue Feb 19 17:38:39 2008 -0500 +++ b/.hgignore Tue Feb 19 17:43:53 2008 -0500 @@ -18,6 +18,7 @@ Makefile\.in$ ^src/virsh ^src/libvirt.la ^src/\.libs +^src/parthelper ^aclocal.m4$ ^docs/devhelp/libvirt.devhelp$ ^libtool$ diff -r f54c0eb07193 configure.in --- a/configure.in Tue Feb 19 17:38:39 2008 -0500 +++ b/configure.in Tue Feb 19 17:43:53 2008 -0500 @@ -27,6 +27,7 @@ GNUTLS_REQUIRED="1.0.25" GNUTLS_REQUIRED="1.0.25" AVAHI_REQUIRED="0.6.0" POLKIT_REQUIRED="0.6" +PARTED_REQUIRED="1.8.0" dnl Checks for C compiler. AC_PROG_CC @@ -563,6 +564,8 @@ AC_ARG_WITH(storage-lvm, [ --with-storage-lvm with LVM backend for the storage driver (on)],[],[with_storage_lvm=check]) AC_ARG_WITH(storage-iscsi, [ --with-storage-iscsi with iSCSI backend for the storage driver (on)],[],[with_storage_iscsi=check]) +AC_ARG_WITH(storage-disk, +[ --with-storage-disk with GPartd Disk backend for the storage driver (on)],[],[with_storage_disk=check]) if test "$with_storage_fs" = "yes" -o "$with_storage_fs" = "check"; then AC_PATH_PROG(MOUNT, [mount], [], [$PATH:/sbin:/usr/sbin]) @@ -674,6 +677,50 @@ if test "$with_storage_iscsi" = "yes" -o fi fi AM_CONDITIONAL(WITH_STORAGE_ISCSI, [test "$with_storage_iscsi" = "yes"]) + + + +LIBPARTED_CFLAGS= +LIBPARTED_LIBS= +if test "$with_storage_disk" = "yes" -o "$with_storage_disk" = "check"; then + AC_PATH_PROG(PARTED, [parted], [], [$PATH:/sbin:/usr/sbin]) + if test -z "$PARTED" ; then with_storage_disk=no ; fi + + PARTED_FOUND=yes + if test "$with_storage_disk" != "no" -a "x$PKG_CONFIG" != "x" ; then + PKG_CHECK_MODULES(LIBPARTED, libparted >= $PARTED_REQUIRED, [], [PARTED_FOUND=no]) + fi + if test "$PARTED_FOUND" = "no"; then + # RHEL-5 vintage parted is missing pkg-config files + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + PARTED_FOUND=yes + AC_CHECK_HEADER(parted/parted.h,,[PARTED_FOUND=no]) + AC_CHECK_LIB(uuid, uuid_generate,,[PARTED_FOUND=no]) + AC_CHECK_LIB(parted, ped_device_read,,[PARTED_FOUND=no]) + LIBPARTED_LIBS="-luuid -lparted" + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + fi + + if test "$PARTED_FOUND" = "no" ; then + if test "$with_storage_disk" = "yes" ; then + AC_MSG_ERROR(We need parted for disk storage driver) + else + with_storage_disk=no + fi + else + with_storage_disk=yes + fi + + if test "$with_storage_disk" = "yes"; then + AC_DEFINE_UNQUOTED(WITH_STORAGE_DISK, 1, [whether Disk backend for storage driver is enabled]) + AC_DEFINE_UNQUOTED([PARTED],["$PARTED"], [Location or name of the parted program]) + fi +fi +AM_CONDITIONAL(WITH_STORAGE_DISK, [test "$with_storage_disk" = "yes"]) +AC_SUBST(LIBPARTED_CFLAGS) +AC_SUBST(LIBPARTED_LIBS) dnl @@ -891,6 +938,7 @@ AC_MSG_NOTICE([ NetFS: $with_storage_f AC_MSG_NOTICE([ NetFS: $with_storage_fs]) AC_MSG_NOTICE([ LVM: $with_storage_lvm]) AC_MSG_NOTICE([ iSCSI: $with_storage_iscsi]) +AC_MSG_NOTICE([ Disk: $with_storage_disk]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([Libraries]) AC_MSG_NOTICE([]) diff -r f54c0eb07193 libvirt.spec.in --- a/libvirt.spec.in Tue Feb 19 17:38:39 2008 -0500 +++ b/libvirt.spec.in Tue Feb 19 17:43:53 2008 -0500 @@ -53,6 +53,8 @@ Requires: lvm2 Requires: lvm2 # For ISCSI driver Requires: iscsi-initiator-utils +# For disk driver +Requires: parted BuildRequires: xen-devel BuildRequires: libxml2-devel BuildRequires: readline-devel @@ -81,6 +83,8 @@ BuildRequires: lvm2 BuildRequires: lvm2 # For ISCSI driver BuildRequires: iscsi-initiator-utils +# For disk driver +BuildRequires: parted-devel Obsoletes: libvir ExclusiveArch: i386 x86_64 ia64 @@ -203,6 +207,7 @@ fi %if %{with_proxy} == "yes" %attr(4755, root, root) %{_libexecdir}/libvirt_proxy %endif +%attr(0755, root, root) %{_libexecdir}/libvirt_parthelper %attr(0755, root, root) %{_sbindir}/libvirtd %doc docs/*.rng %doc docs/*.xml diff -r f54c0eb07193 po/POTFILES.in --- a/po/POTFILES.in Tue Feb 19 17:38:39 2008 -0500 +++ b/po/POTFILES.in Tue Feb 19 17:43:53 2008 -0500 @@ -14,6 +14,7 @@ src/storage_backend_fs.c src/storage_backend_fs.c src/storage_backend_logical.c src/storage_backend_iscsi.c +src/storage_backend_disk.c src/storage_conf.c src/storage_driver.c src/sexpr.c diff -r f54c0eb07193 src/Makefile.am --- a/src/Makefile.am Tue Feb 19 17:38:39 2008 -0500 +++ b/src/Makefile.am Tue Feb 19 17:43:53 2008 -0500 @@ -80,6 +80,13 @@ EXTRA_DIST += storage_backend_iscsi.h s EXTRA_DIST += storage_backend_iscsi.h storage_backend_iscsi.c endif +if WITH_STORAGE_DISK +CLIENT_SOURCES += storage_backend_disk.h storage_backend_disk.c +else +EXTRA_DIST += storage_backend_disk.h storage_backend_disk.c +endif + + libvirt_la_SOURCES = $(CLIENT_SOURCES) $(SERVER_SOURCES) @@ -99,6 +106,17 @@ virsh_LDADD = $(LDADDS) $(VIRSH_LIBS) virsh_LDADD = $(LDADDS) $(VIRSH_LIBS) virsh_CFLAGS = $(COVERAGE_CFLAGS) $(READLINE_CFLAGS) +if WITH_STORAGE_DISK +libexec_PROGRAMS = libvirt_parthelper + +libvirt_parthelper_SOURCES = parthelper.c +libvirt_parthelper_LDFLAGS = $(WARN_CFLAGS) $(COVERAGE_LDCFLAGS) +libvirt_parthelper_LDADD = $(LIBPARTED_LIBS) +libvirt_parthelper_CFLAGS = $(LIBPARTED_CFLAGS) +else +EXTRA_DIST += parthelper.c +endif + # # target to ease building test programs # diff -r f54c0eb07193 src/parthelper.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/parthelper.c Tue Feb 19 17:43:53 2008 -0500 @@ -0,0 +1,127 @@ +/* + * parthelper.c: Helper program to talk to parted with. + * + * This helper exists because parted is GPLv3+, while libvirt is LGPLv2+. + * Thus we can't link to parted in libvirt.so without the combined work + * being GPLv3+. Thus we separate via an external command. NB, this source + * code is still LGPLv2+, but the binary helper is effectively GPLv3+ + * + * The existing 'parted' command line tool is also incredibly hard to parse + * in a reliable fashion if merely after a list of partitions & sizes, + * though it is fine for creating partitions. + * + * 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@xxxxxxxxxx> + */ + +#include <config.h> + +#include <parted/parted.h> +#include <stdio.h> + +int main(int argc, char **argv) +{ + PedDevice *dev; + PedDisk *disk; + PedPartition *part; + + if (argc != 2) { + fprintf(stderr, "syntax: %s DEVICE\n", argv[0]); + return 1; + } + + if ((dev = ped_device_get(argv[1])) == NULL) { + fprintf(stderr, "unable to access device %s\n", argv[1]); + return 2; + } + + if ((disk = ped_disk_new(dev)) == NULL) { + fprintf(stderr, "unable to access disk %s\n", argv[1]); + return 2; + } + + /* Get the first partition, and then iterate over all */ + part = ped_disk_get_partition(disk, 1); + while (part) { + const char *type; + const char *content; + if (part->type & PED_PARTITION_LOGICAL) { + type = "logical"; + if (part->type & PED_PARTITION_FREESPACE) + content = "free"; + else if (part->type & PED_PARTITION_METADATA) + content = "metadata"; + else if (part->type & PED_PARTITION_PROTECTED) + content = "protected"; + else + content = "data"; + } else if (part->type == PED_PARTITION_EXTENDED) { + type = "extended"; + content = "metadata"; + } else { + type = "normal"; + if (part->type & PED_PARTITION_FREESPACE) + content = "free"; + else if (part->type & PED_PARTITION_METADATA) + content = "metadata"; + else if (part->type & PED_PARTITION_PROTECTED) + content = "protected"; + else + content = "data"; + } + + /* We do +1 on geom.end, because we want end of the last sector + * in bytes, not the last sector number + */ + if (part->num != -1) { + printf("%s%d%c%s%c%s%c%llu%c%llu%c%llu%c", + part->geom.dev->path, + part->num, '\0', + type, '\0', + content, '\0', + part->geom.start * 512llu, '\0', + (part->geom.end + 1 ) * 512llu, '\0', + part->geom.length * 512llu, '\0'); + } else { + printf("%s%c%s%c%s%c%llu%c%llu%c%llu%c", + "-", '\0', + type, '\0', + content, '\0', + part->geom.start * 512llu, '\0', + (part->geom.end + 1 ) * 512llu, '\0', + part->geom.length * 512llu, '\0'); + } + part = ped_disk_next_partition(disk, part); + } + + return 0; +} +/* + * 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 -r f54c0eb07193 src/storage_backend.c --- a/src/storage_backend.c Tue Feb 19 17:38:39 2008 -0500 +++ b/src/storage_backend.c Tue Feb 19 17:43:53 2008 -0500 @@ -42,6 +42,9 @@ #if WITH_STORAGE_ISCSI #include "storage_backend_iscsi.h" #endif +#if WITH_STORAGE_DISK +#include "storage_backend_disk.h" +#endif #include "util.h" @@ -60,6 +63,9 @@ static virStorageBackendPtr backends[] = #endif #if WITH_STORAGE_ISCSI &virStorageBackendISCSI, +#endif +#if WITH_STORAGE_DISK + &virStorageBackendDisk, #endif }; @@ -110,6 +116,10 @@ virStorageBackendFromString(const char * #if WITH_STORAGE_ISCSI if (STREQ(type, "iscsi")) return VIR_STORAGE_POOL_ISCSI; +#endif +#if WITH_STORAGE_DISK + if (STREQ(type, "disk")) + return VIR_STORAGE_POOL_DISK; #endif virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, @@ -135,6 +145,10 @@ virStorageBackendToString(int type) { #if WITH_STORAGE_ISCSI case VIR_STORAGE_POOL_ISCSI: return "iscsi"; +#endif +#if WITH_STORAGE_DISK + case VIR_STORAGE_POOL_DISK: + return "disk"; #endif } diff -r f54c0eb07193 src/storage_backend_disk.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_disk.c Tue Feb 19 17:43:53 2008 -0500 @@ -0,0 +1,537 @@ +/* + * storage_backend_disk.c: storage backend for disk 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@xxxxxxxxxx> + */ + +#include <config.h> + +#include "storage_backend_disk.h" +#include "util.h" + +enum { + VIR_STORAGE_POOL_DISK_DOS = 0, + VIR_STORAGE_POOL_DISK_DVH, + VIR_STORAGE_POOL_DISK_GPT, + VIR_STORAGE_POOL_DISK_MAC, + VIR_STORAGE_POOL_DISK_BSD, + VIR_STORAGE_POOL_DISK_PC98, + VIR_STORAGE_POOL_DISK_SUN, +}; + +/* + * XXX these are basically partition types. + * + * fdisk has a bazillion partition ID types + * parted has practically none, and splits the + * info across 3 different attributes. + * + * So this is a semi-generic set + */ +enum { + VIR_STORAGE_VOL_DISK_NONE = 0, + VIR_STORAGE_VOL_DISK_LINUX, + VIR_STORAGE_VOL_DISK_FAT16, + VIR_STORAGE_VOL_DISK_FAT32, + VIR_STORAGE_VOL_DISK_LINUX_SWAP, + VIR_STORAGE_VOL_DISK_LINUX_LVM, + VIR_STORAGE_VOL_DISK_LINUX_RAID, + VIR_STORAGE_VOL_DISK_EXTENDED, +}; + +#define PARTHELPER BINDIR "/libvirt_parthelper" + +static int +virStorageBackendDiskPoolFormatFromString(virConnectPtr conn, + const char *format) { + if (format == NULL) + return VIR_STORAGE_POOL_DISK_DOS; + + if (STREQ(format, "dos")) + return VIR_STORAGE_POOL_DISK_DOS; + if (STREQ(format, "dvh")) + return VIR_STORAGE_POOL_DISK_DVH; + if (STREQ(format, "gpt")) + return VIR_STORAGE_POOL_DISK_GPT; + if (STREQ(format, "mac")) + return VIR_STORAGE_POOL_DISK_MAC; + if (STREQ(format, "bsd")) + return VIR_STORAGE_POOL_DISK_BSD; + if (STREQ(format, "pc98")) + return VIR_STORAGE_POOL_DISK_PC98; + if (STREQ(format, "sun")) + return VIR_STORAGE_POOL_DISK_SUN; + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unsupported pool format %s"), format); + return -1; +} + +static const char * +virStorageBackendDiskPoolFormatToString(virConnectPtr conn, + int format) { + switch (format) { + case VIR_STORAGE_POOL_DISK_DOS: + return "dos"; + case VIR_STORAGE_POOL_DISK_DVH: + return "dvh"; + case VIR_STORAGE_POOL_DISK_GPT: + return "gpt"; + case VIR_STORAGE_POOL_DISK_MAC: + return "mac"; + case VIR_STORAGE_POOL_DISK_BSD: + return "bsd"; + case VIR_STORAGE_POOL_DISK_PC98: + return "pc98"; + case VIR_STORAGE_POOL_DISK_SUN: + return "sun"; + } + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unsupported pool format %d"), format); + return NULL; +} + +static int +virStorageBackendDiskVolFormatFromString(virConnectPtr conn, + const char *format) { + if (format == NULL) + return VIR_STORAGE_VOL_DISK_NONE; + + if (STREQ(format, "none")) + return VIR_STORAGE_VOL_DISK_NONE; + if (STREQ(format, "linux")) + return VIR_STORAGE_VOL_DISK_LINUX; + if (STREQ(format, "fat16")) + return VIR_STORAGE_VOL_DISK_FAT16; + if (STREQ(format, "fat32")) + return VIR_STORAGE_VOL_DISK_FAT32; + if (STREQ(format, "linux-swap")) + return VIR_STORAGE_VOL_DISK_LINUX_SWAP; + if (STREQ(format, "linux-lvm")) + return VIR_STORAGE_VOL_DISK_LINUX_LVM; + if (STREQ(format, "linux-raid")) + return VIR_STORAGE_VOL_DISK_LINUX_RAID; + if (STREQ(format, "extended")) + return VIR_STORAGE_VOL_DISK_EXTENDED; + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unsupported volume format %s"), format); + return -1; +} + +static const char * +virStorageBackendDiskVolFormatToString(virConnectPtr conn, + int format) { + switch (format) { + case VIR_STORAGE_VOL_DISK_NONE: + return "none"; + case VIR_STORAGE_VOL_DISK_LINUX: + return "linux"; + case VIR_STORAGE_VOL_DISK_FAT16: + return "fat16"; + case VIR_STORAGE_VOL_DISK_FAT32: + return "fat32"; + case VIR_STORAGE_VOL_DISK_LINUX_SWAP: + return "linux-swap"; + case VIR_STORAGE_VOL_DISK_LINUX_LVM: + return "linux-lvm"; + case VIR_STORAGE_VOL_DISK_LINUX_RAID: + return "linux-raid"; + case VIR_STORAGE_VOL_DISK_EXTENDED: + return "extended"; + } + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unsupported volume format %d"), format); + return NULL; +} + +static int +virStorageBackendDiskMakeDataVol(virConnectPtr conn, + virStoragePoolObjPtr pool, + char **const groups, + virStorageVolDefPtr vol) +{ + char *tmp, *devpath; + + if (vol == NULL) { + if ((vol = calloc(1, sizeof(virStorageVolDef))) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("volume")); + return -1; + } + + vol->next = pool->volumes; + pool->volumes = vol; + pool->nvolumes++; + + /* Prepended path will be same for all partitions, so we can + * strip the path to form a reasonable pool-unique name + */ + tmp = strrchr(groups[0], '/'); + if ((vol->name = strdup(tmp ? tmp + 1 : groups[0])) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("volume")); + return -1; + } + } + + if (vol->target.path == NULL) { + if ((devpath = strdup(groups[0])) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("volume")); + return -1; + } + + /* Now figure out the stable path + * + * XXX this method is O(N) because it scans the pool target + * dir every time its run. Should figure out a more efficient + * way of doing this... + */ + if ((vol->target.path = virStorageBackendStablePath(conn, + pool, + devpath)) == NULL) + return -1; + + if (devpath != vol->target.path) + free(devpath); + devpath = NULL; + } + + if (vol->key == NULL) { + /* XXX base off a unique key of the underlying disk */ + if ((vol->key = strdup(vol->target.path)) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("volume")); + return -1; + } + } + + if (vol->source.extents == NULL) { + if ((vol->source.extents = + calloc(1, sizeof(*(vol->source.extents)))) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, + _("volume extents")); + return -1; + } + vol->source.nextent = 1; + + if (virStrToLong_ull(groups[3], NULL, 10, + &vol->source.extents[0].start) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot parse device start location")); + return -1; + } + + if (virStrToLong_ull(groups[4], NULL, 10, + &vol->source.extents[0].end) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot parse device end location")); + return -1; + } + + if ((vol->source.extents[0].path = + strdup(pool->def->source.devices[0].path)) == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, _("extents")); + return -1; + } + } + + /* Refresh allocation/capacity/perms */ + if (virStorageBackendUpdateVolInfo(conn, vol, 1) < 0) + return -1; + + /* The above gets allocation wrong for + * extended partitions, so overwrite it */ + vol->allocation = vol->capacity = + (vol->source.extents[0].end - vol->source.extents[0].start); + + if (STRNEQ(groups[2], "metadata")) + pool->def->allocation += vol->allocation; + if (vol->source.extents[0].end > pool->def->capacity) + pool->def->capacity = vol->source.extents[0].end; + + return 0; +} + +static int +virStorageBackendDiskMakeFreeExtent(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolObjPtr pool, + char **const groups) +{ + virStoragePoolSourceDeviceExtentPtr tmp; + virStoragePoolSourceDevicePtr dev = &pool->def->source.devices[0]; + + if ((tmp = realloc(dev->freeExtents, + sizeof(*tmp) * (dev->nfreeExtent+1))) == NULL) + return -1; + dev->freeExtents = tmp; + + memset(dev->freeExtents + + dev->nfreeExtent, 0, sizeof(*tmp)); + + if (virStrToLong_ull(groups[3], NULL, 10, + &dev->freeExtents[dev->nfreeExtent].start) < 0) + return -1; /* Don't bother to re-alloc freeExtents - it'll be free'd shortly */ + + if (virStrToLong_ull(groups[4], NULL, 10, + &dev->freeExtents[dev->nfreeExtent].end) < 0) + return -1; /* Don't bother to re-alloc freeExtents - it'll be free'd shortly */ + + pool->def->available += + (dev->freeExtents[dev->nfreeExtent].end - + dev->freeExtents[dev->nfreeExtent].start); + if (dev->freeExtents[dev->nfreeExtent].end > pool->def->capacity) + pool->def->capacity = dev->freeExtents[dev->nfreeExtent].end; + + dev->nfreeExtent++; + + return 0; +} + + +static int +virStorageBackendDiskMakeVol(virConnectPtr conn, + virStoragePoolObjPtr pool, + size_t ntok ATTRIBUTE_UNUSED, + char **const groups, + void *data) +{ + /* + * Ignore normal+metadata, and logical+metadata partitions + * since they're basically internal book-keeping regions + * we have no control over. Do keep extended+metadata though + * because that's the MS-DOS extended partition region we + * need to be able to view/create/delete + */ + if ((STREQ(groups[1], "normal") || + STREQ(groups[1], "logical")) && + STREQ(groups[2], "metadata")) + return 0; + + /* Remaining data / metdata parts get turn into volumes... */ + if (STREQ(groups[2], "metadata") || + STREQ(groups[2], "data")) { + virStorageVolDefPtr vol = data; + /* We're searching for a specific vol only, so ignore others */ + if (vol && + STRNEQ(vol->name, groups[0])) + return 0; + + return virStorageBackendDiskMakeDataVol(conn, pool, groups, vol); + } else if (STREQ(groups[2], "free")) { + /* ....or free space extents */ + return virStorageBackendDiskMakeFreeExtent(conn, pool, groups); + } else { + /* This codepath should never happen unless someone changed + * libvirt_parthelper forgot to change this code */ + return -1; + } +} + + +/* To get a list of partitions we run an external helper + * tool which then uses parted APIs. This is because + * parted's API is not compatible with libvirt's license + * but we really really want to use parted because the + * other options all suck :-) + * + * All the other storage backends run an external tool for + * listing volumes so this really isn't too much of a pain, + * and we can even ensure the output is friendly. + */ +static int +virStorageBackendDiskReadPartitions(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol) +{ + + /* + * # libvirt_parthelper DEVICE + * /dev/sda1 normal data 32256 106928128 106896384 + * /dev/sda2 normal data 106928640 100027629568 99920701440 + * - normal metadata 100027630080 100030242304 2612736 + * + */ + const char *prog[] = { + PARTHELPER, pool->def->source.devices[0].path, NULL, + }; + + pool->def->allocation = pool->def->capacity = pool->def->available = 0; + + return virStorageBackendRunProgNul(conn, + pool, + prog, + 6, + virStorageBackendDiskMakeVol, + vol); +} + + +static int +virStorageBackendDiskRefreshPool(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + free(pool->def->source.devices[0].freeExtents); + pool->def->source.devices[0].nfreeExtent = 0; + pool->def->source.devices[0].freeExtents = NULL; + + return virStorageBackendDiskReadPartitions(conn, pool, NULL); +} + + +/** + * Write a new partition table header + */ +static int +virStorageBackendDiskBuildPool(virConnectPtr conn, + virStoragePoolObjPtr pool, + unsigned int flags ATTRIBUTE_UNUSED) +{ + /* eg parted /dev/sda mklabel msdos */ + const char *prog[] = { + PARTED, + pool->def->source.devices[0].path, + "mklabel", + virStorageBackendDiskPoolFormatToString(conn, pool->def->source.format), + NULL, + }; + + if (virRun(conn, (char**)prog, NULL) < 0) + return -1; + + return 0; +} + + +static int +virStorageBackendDiskDeleteVol(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol, + unsigned int flags); + +static int +virStorageBackendDiskCreateVol(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol) +{ + int i; + char start[100], end[100]; + unsigned long long startOffset, endOffset, smallestSize = 0; + int smallestExtent = -1; + virStoragePoolSourceDevicePtr dev = &pool->def->source.devices[0]; + /* XXX customizable partition types */ + const char *cmdargv[] = { + PARTED, + pool->def->source.devices[0].path, + "mkpart", + "--script", + "ext2", + start, + end, + NULL + }; + + for (i = 0 ; i < dev->nfreeExtent ; i++) { + unsigned long long size = + dev->freeExtents[i].end - + dev->freeExtents[i].start; + if (size > vol->allocation && + (smallestSize == 0 || + size < smallestSize)) { + smallestSize = size; + smallestExtent = i; + } + } + if (smallestExtent == -1) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("no large enough free extent")); + return -1; + } + startOffset = dev->freeExtents[smallestExtent].start; + endOffset = startOffset + vol->allocation; + + snprintf(start, sizeof(start)-1, "%lluB", startOffset); + start[sizeof(start)-1] = '\0'; + snprintf(end, sizeof(end)-1, "%lluB", endOffset); + end[sizeof(end)-1] = '\0'; + + if (virRun(conn, (char**)cmdargv, NULL) < 0) + return -1; + + /* Blow away free extent info, as we're about to re-populate it */ + free(pool->def->source.devices[0].freeExtents); + pool->def->source.devices[0].nfreeExtent = 0; + pool->def->source.devices[0].freeExtents = NULL; + + /* Fetch actual extent info */ + if (virStorageBackendDiskReadPartitions(conn, pool, vol) < 0) + return -1; + + return 0; +} + + +static int +virStorageBackendDiskDeleteVol(virConnectPtr conn, + virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, + virStorageVolDefPtr vol ATTRIBUTE_UNUSED, + unsigned int flags ATTRIBUTE_UNUSED) +{ + /* delete a partition */ + virStorageReportError(conn, VIR_ERR_NO_SUPPORT, + _("Disk pools are not yet supported")); + return -1; +} + + +virStorageBackend virStorageBackendDisk = { + .type = VIR_STORAGE_POOL_DISK, + + .buildPool = virStorageBackendDiskBuildPool, + .refreshPool = virStorageBackendDiskRefreshPool, + + .createVol = virStorageBackendDiskCreateVol, + .deleteVol = virStorageBackendDiskDeleteVol, + + .poolOptions = { + .flags = (VIR_STORAGE_BACKEND_POOL_SOURCE_DEVICE), + .formatFromString = virStorageBackendDiskPoolFormatFromString, + .formatToString = virStorageBackendDiskPoolFormatToString, + }, + .volOptions = { + .formatFromString = virStorageBackendDiskVolFormatFromString, + .formatToString = virStorageBackendDiskVolFormatToString, + }, + + .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 -r f54c0eb07193 src/storage_backend_disk.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_disk.h Tue Feb 19 17:43:53 2008 -0500 @@ -0,0 +1,45 @@ +/* + * storage_backend_disk.h: storage backend for disk 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@xxxxxxxxxx> + */ + +#ifndef __VIR_STORAGE_BACKEND_DISK_H__ +#define __VIR_STORAGE_BACKEND_DISK_H__ + +#include "storage_backend.h" + +extern virStorageBackend virStorageBackendDisk; + +#endif /* __VIR_STORAGE_BACKEND_DISK_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: + */ -- |=- 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@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list