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 | 508 +++++++++++++++++++++++++++++++++++++++++++ b/src/storage_backend_disk.h | 45 +++ configure.in | 48 ++++ libvirt.spec.in | 5 src/Makefile.am | 18 + src/storage_backend.c | 14 + 8 files changed, 766 insertions(+) diff -r 4c283aac5fd6 .hgignore --- a/.hgignore Thu Feb 07 14:17:57 2008 -0500 +++ b/.hgignore Thu Feb 07 16:51:32 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 4c283aac5fd6 configure.in --- a/configure.in Thu Feb 07 14:17:57 2008 -0500 +++ b/configure.in Thu Feb 07 16:51:32 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 4c283aac5fd6 libvirt.spec.in --- a/libvirt.spec.in Thu Feb 07 14:17:57 2008 -0500 +++ b/libvirt.spec.in Thu Feb 07 16:51:32 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 4c283aac5fd6 src/Makefile.am --- a/src/Makefile.am Thu Feb 07 14:17:57 2008 -0500 +++ b/src/Makefile.am Thu Feb 07 16:51:32 2008 -0500 @@ -81,6 +81,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) @@ -100,6 +107,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 4c283aac5fd6 src/parthelper.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/parthelper.c Thu Feb 07 16:51:32 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 4c283aac5fd6 src/storage_backend.c --- a/src/storage_backend.c Thu Feb 07 14:17:57 2008 -0500 +++ b/src/storage_backend.c Thu Feb 07 16:51:32 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 }; @@ -105,6 +111,10 @@ int virStorageBackendFromString(const ch #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, "unknown storage backend type %s", type); return -1; @@ -127,6 +137,10 @@ const char *virStorageBackendToString(in #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 4c283aac5fd6 src/storage_backend_disk.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_disk.c Thu Feb 07 16:51:32 2008 -0500 @@ -0,0 +1,508 @@ +/* + * 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 "swap"; + case VIR_STORAGE_VOL_DISK_LINUX_LVM: + return "lvm"; + case VIR_STORAGE_VOL_DISK_LINUX_RAID: + return "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 (xstrtol_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 (xstrtol_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 (xstrtol_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 (xstrtol_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, 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 4c283aac5fd6 src/storage_backend_disk.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_disk.h Thu Feb 07 16:51:32 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