]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
allow to create storage volumes on disk backend
authorDaniel Veillard <veillard@redhat.com>
Fri, 26 Jun 2009 16:18:59 +0000 (16:18 +0000)
committerDaniel Veillard <veillard@redhat.com>
Fri, 26 Jun 2009 16:18:59 +0000 (16:18 +0000)
* src/libvirt_private.syms src/parthelper.c src/storage_backend_disk.c
  src/storage_conf.c src/storage_conf.h: allow to create storage
  volumes on disk backend, patches by Henrik Persson
* AUTHORS: add Henrik Persson
Daniel

AUTHORS
ChangeLog
src/libvirt_private.syms
src/parthelper.c
src/storage_backend_disk.c
src/storage_conf.c
src/storage_conf.h

diff --git a/AUTHORS b/AUTHORS
index 7c3104ab88c5db5735076002f3889a75fb45c3c6..4fbfb20e658744f628ea21f1544a626e4cfc62a8 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -75,6 +75,7 @@ Patches have also been contributed by:
   Javier Fontan        <jfontan@gmail.com>
   Federico Simoncelli  <federico.simoncelli@gmail.com>
   Amy Griffis          <amy.griffis@hp.com>
+  Henrik Persson E     <henrik.e.persson@ericsson.com>
 
   [....send patches to get your name here....]
 
index b45bb1335880e27e7e69b2a858b379e41c695612..27c8773d5fd15f553a7456bf5288dd326ae17544 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+Fri Jun 26 18:15:08 CEST 2009 Daniel Veillard <veillard@redhat.com>
+
+       * src/libvirt_private.syms src/parthelper.c src/storage_backend_disk.c
+         src/storage_conf.c src/storage_conf.h: allow to create storage
+         volumes on disk backend, patches by Henrik Persson
+       * AUTHORS: add Henrik Persson
+
 Fri Jun 26 17:06:18 CEST 2009 Daniel Veillard <veillard@redhat.com>
 
        * src/Makefile.am src/libvirt.c src/libvirt_private.syms src/logging.c
index 3958d9b4f3efd247f4d813355eaa6f5db899be74..52c49674c2199a8548cf00b68ce0ab5ed08e67c7 100644 (file)
@@ -307,6 +307,7 @@ virStoragePoolFormatFileSystemNetTypeToString;
 virStorageVolFormatFileSystemTypeToString;
 virStorageVolFormatFileSystemTypeFromString;
 virStoragePoolTypeFromString;
+virStoragePartedFsTypeTypeToString;
 virStoragePoolObjLock;
 virStoragePoolObjUnlock;
 
index c699035a9a6926a2c31f80e02265b43288ca7f77..ff5a1b6c21321a6514e94a36767b7616908bb5ba 100644 (file)
 #include <parted/parted.h>
 #include <stdio.h>
 
+/* we don't need to include the full internal.h just for this */
+#define STRNEQ(a,b) (strcmp((a),(b)) != 0)
+
 /* Make the comparisons below fail if your parted headers
    are so old that they lack the definition.  */
 #ifndef PED_PARTITION_PROTECTED
 # define PED_PARTITION_PROTECTED 0
 #endif
 
+enum diskCommand {
+    DISK_LAYOUT = 0,
+    DISK_GEOMETRY
+};
+
 int main(int argc, char **argv)
 {
     PedDevice *dev;
     PedDisk *disk;
     PedPartition *part;
+    int cmd = DISK_LAYOUT;
 
-    if (argc !=  2) {
-        fprintf(stderr, "syntax: %s DEVICE\n", argv[0]);
+    if (argc ==  3 && STRNEQ(argv[2], "-g")) {
+        cmd = DISK_GEOMETRY;
+    } else if (argc != 2) {
+        fprintf(stderr, "syntax: %s DEVICE [-g]\n", argv[0]);
         return 1;
     }
 
@@ -57,6 +68,15 @@ int main(int argc, char **argv)
         return 2;
     }
 
+    /* return the geometry of the disk and then exit */
+    if(cmd == DISK_GEOMETRY) {
+        printf("%d%c%d%c%d%c%",
+               dev->hw_geom.cylinders, '\0',
+               dev->hw_geom.heads, '\0',
+               dev->hw_geom.sectors, '\0');
+        return 0;
+    }
+
     if ((disk = ped_disk_new(dev)) == NULL) {
         fprintf(stderr, "unable to access disk %s\n", argv[1]);
         return 2;
index 4b0da3d7d40814c0932b6e3b980e58e5731c712f..eb694a7d2ea403b9dfac6a2bff28326d788e1629 100644 (file)
@@ -36,6 +36,8 @@
 
 #define PARTHELPER BINDIR "/libvirt_parthelper"
 
+#define SECTOR_SIZE 512
+
 static int
 virStorageBackendDiskMakeDataVol(virConnectPtr conn,
                                  virStoragePoolObjPtr pool,
@@ -128,6 +130,16 @@ virStorageBackendDiskMakeDataVol(virConnectPtr conn,
     if (virStorageBackendUpdateVolInfo(conn, vol, 1) < 0)
         return -1;
 
+    /* set partition type */
+    if(STREQ(groups[1], "normal"))
+       vol->target.type = VIR_STORAGE_VOL_DISK_TYPE_PRIMARY;
+    else if(STREQ(groups[1], "logical"))
+       vol->target.type = VIR_STORAGE_VOL_DISK_TYPE_LOGICAL;
+    else if(STREQ(groups[1], "extended"))
+       vol->target.type = VIR_STORAGE_VOL_DISK_TYPE_EXTENDED;
+    else
+       vol->target.type = VIR_STORAGE_VOL_DISK_TYPE_NONE;
+
     vol->type = VIR_STORAGE_VOL_BLOCK;
 
     /* The above gets allocation wrong for
@@ -158,6 +170,14 @@ virStorageBackendDiskMakeFreeExtent(virConnectPtr conn ATTRIBUTE_UNUSED,
            dev->nfreeExtent, 0,
            sizeof(dev->freeExtents[0]));
 
+    /* set type of free area */
+    if(STREQ(groups[1], "logical")) {
+        dev->freeExtents[dev->nfreeExtent].type = VIR_STORAGE_FREE_LOGICAL;
+    } else {
+        dev->freeExtents[dev->nfreeExtent].type = VIR_STORAGE_FREE_NORMAL;
+    }
+
+
     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 */
@@ -166,6 +186,11 @@ virStorageBackendDiskMakeFreeExtent(virConnectPtr conn ATTRIBUTE_UNUSED,
                          &dev->freeExtents[dev->nfreeExtent].end) < 0)
         return -1; /* Don't bother to re-alloc freeExtents - it'll be free'd shortly */
 
+    /* first block reported as free, even if it is not */
+    if (dev->freeExtents[dev->nfreeExtent].start == 0) {
+        dev->freeExtents[dev->nfreeExtent].start = SECTOR_SIZE;
+    }
+
     pool->def->available +=
         (dev->freeExtents[dev->nfreeExtent].end -
          dev->freeExtents[dev->nfreeExtent].start);
@@ -255,6 +280,35 @@ virStorageBackendDiskReadPartitions(virConnectPtr conn,
                                        vol);
 }
 
+static int
+virStorageBackendDiskMakePoolGeometry(virConnectPtr conn ATTRIBUTE_UNUSED,
+                                     virStoragePoolObjPtr pool,
+                                     size_t ntok ATTRIBUTE_UNUSED,
+                                     char **const groups,
+                                     void *data ATTRIBUTE_UNUSED)
+{
+
+       pool->def->source.devices[0].geometry.cyliders = atoi(groups[0]);
+       pool->def->source.devices[0].geometry.heads = atoi(groups[1]);
+       pool->def->source.devices[0].geometry.sectors = atoi(groups[2]);
+
+       return 0;
+}
+
+static int
+virStorageBackendDiskReadGeometry(virConnectPtr conn, virStoragePoolObjPtr pool)
+{
+    const char *prog[] = {
+        PARTHELPER, pool->def->source.devices[0].path, "-g", NULL,
+    };
+
+    return virStorageBackendRunProgNul(conn,
+                                       pool,
+                                       prog,
+                                       3,
+                                       virStorageBackendDiskMakePoolGeometry,
+                                       NULL);
+}
 
 static int
 virStorageBackendDiskRefreshPool(virConnectPtr conn,
@@ -265,6 +319,10 @@ virStorageBackendDiskRefreshPool(virConnectPtr conn,
 
     virStorageBackendWaitForDevices(conn);
 
+    if (virStorageBackendDiskReadGeometry(conn, pool) != 0) {
+        return -1;
+    }
+
     return virStorageBackendDiskReadPartitions(conn, pool, NULL);
 }
 
@@ -294,46 +352,218 @@ virStorageBackendDiskBuildPool(virConnectPtr conn,
     return 0;
 }
 
+/**
+ * Decides what kind of partition type that should be created.
+ * Important when the partition table is of msdos type
+ */
 static int
-virStorageBackendDiskCreateVol(virConnectPtr conn,
-                               virStoragePoolObjPtr pool,
-                               virStorageVolDefPtr vol)
+virStorageBackendDiskPartTypeToCreate(virStoragePoolObjPtr pool)
+{
+    if (pool->def->source.format == VIR_STORAGE_POOL_DISK_DOS) {
+        /* count primary and extended paritions,
+           can't be more than 3 to create a new primary partition */
+        int i;
+        int count = 0;
+        for (i = 0; i < pool->volumes.count; i++) {
+             if (pool->volumes.objs[i]->target.type == VIR_STORAGE_VOL_DISK_TYPE_PRIMARY ||
+                 pool->volumes.objs[i]->target.type == VIR_STORAGE_VOL_DISK_TYPE_EXTENDED) {
+                     count++;
+             }
+        }
+        if (count >= 4) {
+            return VIR_STORAGE_VOL_DISK_TYPE_LOGICAL;
+        }
+    }
+
+    /* for all other cases, all partitions are primary */
+    return VIR_STORAGE_VOL_DISK_TYPE_PRIMARY;
+}
+
+static int
+virStorageBackendDiskPartFormat(virConnectPtr conn, virStoragePoolObjPtr pool,
+                                virStorageVolDefPtr vol,
+                                char* partFormat)
+{
+    int i;
+    if (pool->def->source.format == VIR_STORAGE_POOL_DISK_DOS) {
+        const char *partedFormat = virStoragePartedFsTypeTypeToString(vol->target.format);
+        if(partedFormat == NULL) {
+           virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 "%s", _("Invalid partition type"));
+           return -1;
+        }
+        if (vol->target.format == VIR_STORAGE_VOL_DISK_EXTENDED) {
+            /* make sure we don't have a extended partition already */
+            for (i = 0; i < pool->volumes.count; i++) {
+                 if (pool->volumes.objs[i]->target.format == VIR_STORAGE_VOL_DISK_EXTENDED) {
+                     virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                           "%s", _("extended partition already exists"));
+                     return -1;
+                 }
+            }
+            sprintf(partFormat, "%s", partedFormat);
+        } else {
+            /* create primary partition as long as it is possible
+               and after that check if an extended partition exists
+               to create logical partitions. */
+            /* XXX Only support one extended partition */
+            switch (virStorageBackendDiskPartTypeToCreate(pool)) {
+                    case VIR_STORAGE_VOL_DISK_TYPE_PRIMARY:
+                         sprintf(partFormat, "primary %s", partedFormat);
+                         break;
+                    case VIR_STORAGE_VOL_DISK_TYPE_LOGICAL:
+                         /* make sure we have a extended partition */
+                         for (i = 0; i < pool->volumes.count; i++) {
+                              if (pool->volumes.objs[i]->target.format == VIR_STORAGE_VOL_DISK_EXTENDED) {
+                                  sprintf(partFormat, "logical %s", partedFormat);
+                                  break;
+                              }
+                         }
+                         if (i == pool->volumes.count) {
+                             virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                                   "%s", _("no extended partition found and no primary partition available"));
+                             return -1;
+                         }
+                         break;
+                    default:
+                         break;
+            }
+        }
+    } else {
+        sprintf(partFormat, "primary");
+    }
+    return 0;
+}
+
+/**
+ * Aligns a new partition to nearest cylinder boundry
+ * when haveing a msdos partition table type
+ * to avoid any problem with all ready existing
+ * partitions
+ */
+static int
+virStorageBackendDiskPartBoundries(virConnectPtr conn,
+                                   virStoragePoolObjPtr pool,
+                                   unsigned long long *start,
+                                   unsigned long long *end,
+                                   unsigned long long allocation)
 {
     int i;
-    char start[100], end[100];
-    unsigned long long startOffset, endOffset, smallestSize = 0;
     int smallestExtent = -1;
+    unsigned long long smallestSize = 0;
+    unsigned long long extraBytes = 0;
+    unsigned long long alignedAllocation = allocation;
     virStoragePoolSourceDevicePtr dev = &pool->def->source.devices[0];
-    /* XXX customizable partition types */
+    unsigned long long cylinderSize = dev->geometry.heads *
+                                      dev->geometry.sectors * SECTOR_SIZE;
+
+    DEBUG("find free area: allocation %llu,  cyl size %llu\n", allocation, cylinderSize);
+    int partType = virStorageBackendDiskPartTypeToCreate(pool);
+
+    /* how many extra bytes we have since we allocate
+       aligned to the cylinder boundry */
+    extraBytes = cylinderSize - (allocation % cylinderSize);
+
+    for (i = 0 ; i < dev->nfreeExtent ; i++) {
+         unsigned long long size =
+             dev->freeExtents[i].end -
+             dev->freeExtents[i].start;
+         unsigned long long neededSize = allocation;
+
+         if (pool->def->source.format == VIR_STORAGE_POOL_DISK_DOS) {
+             /* align to cylinder boundry */
+             neededSize += extraBytes;
+             if ((*start % cylinderSize) > extraBytes) {
+                 /* add an extra cylinder if the offset can't fit within
+                    the extra bytes we have */
+                 neededSize += cylinderSize;
+             }
+             /* if we are creating a logical patition, we need one extra
+                block between partitions (or actually move start one block) */
+             if (partType == VIR_STORAGE_VOL_DISK_TYPE_LOGICAL) {
+                 size -= SECTOR_SIZE;
+             }
+         }
+         if (size > neededSize &&
+             (smallestSize == 0 ||
+             size < smallestSize)) {
+             /* for logical partition, the free extent
+                must be within a logical free area */
+             if (partType == VIR_STORAGE_VOL_DISK_TYPE_LOGICAL &&
+                 dev->freeExtents[i].type != VIR_STORAGE_FREE_LOGICAL) {
+                 continue;
+                 /* for primary partition, the free extent
+                    must not be within a logical free area */
+             } else if(partType == VIR_STORAGE_VOL_DISK_TYPE_PRIMARY &&
+                       dev->freeExtents[i].type != VIR_STORAGE_FREE_NORMAL) {
+                       continue;
+             }
+             smallestSize = size;
+             smallestExtent = i;
+             alignedAllocation = neededSize;
+         }
+    }
+
+    if (smallestExtent == -1) {
+        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                              "%s", _("no large enough free extent"));
+        return -1;
+    }
+
+    DEBUG("aligned alloc %llu\n", alignedAllocation);
+    *start = dev->freeExtents[smallestExtent].start;
+
+    if (partType == VIR_STORAGE_VOL_DISK_TYPE_LOGICAL) {
+        /* for logical partition, skip one block */
+        *start += SECTOR_SIZE;
+    }
+
+    *end = *start + alignedAllocation;
+    if (pool->def->source.format == VIR_STORAGE_POOL_DISK_DOS) {
+        /* adjust our allocation if start is not at a cylinder boundry */
+        *end -= (*start % cylinderSize);
+    }
+
+    /* counting in byte, we want the last byte of the current sector */
+    *end -= 1;
+    DEBUG("final aligned start %llu, end %llu\n", *start, *end);
+    return 0;
+}
+
+
+static int
+virStorageBackendDiskCreateVol(virConnectPtr conn,
+                               virStoragePoolObjPtr pool,
+                               virStorageVolDefPtr vol)
+{
+    char start[100], end[100], partFormat[100];
+    unsigned long long startOffset = 0, endOffset = 0;
     const char *cmdargv[] = {
         PARTED,
         pool->def->source.devices[0].path,
         "mkpart",
         "--script",
-        "ext2",
+        partFormat,
         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 (virStorageBackendDiskPartFormat(conn, pool, vol, partFormat) != 0) {
+        return -1;
+    }
+
+    if (vol->key == NULL) {
+        /* XXX base off a unique key of the underlying disk */
+        if ((vol->key = strdup(vol->target.path)) == NULL) {
+            virReportOOMError(conn);
+            return -1;
         }
     }
-    if (smallestExtent == -1) {
-        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
-                              "%s", _("no large enough free extent"));
-        return -1;
+
+    if(virStorageBackendDiskPartBoundries(conn, pool, &startOffset, &endOffset, vol->allocation) != 0) {
+       return -1;
     }
-    startOffset = dev->freeExtents[smallestExtent].start;
-    endOffset = startOffset + vol->allocation;
 
     snprintf(start, sizeof(start)-1, "%lluB", startOffset);
     start[sizeof(start)-1] = '\0';
@@ -343,6 +573,9 @@ virStorageBackendDiskCreateVol(virConnectPtr conn,
     if (virRun(conn, cmdargv, NULL) < 0)
         return -1;
 
+    /* wait for device node to show up */
+    virStorageBackendWaitForDevices(conn);
+
     /* Blow away free extent info, as we're about to re-populate it */
     VIR_FREE(pool->def->source.devices[0].freeExtents);
     pool->def->source.devices[0].nfreeExtent = 0;
index 493eaa7492d741f18cfeffa39dfdaf18b0869399..f5d9b6ce72157e3a6aa6eb9840d2b8fee3508425 100644 (file)
@@ -93,6 +93,12 @@ VIR_ENUM_IMPL(virStorageVolFormatFileSystem,
               "cloop", "cow", "dmg", "iso",
               "qcow", "qcow2", "vmdk", "vpc")
 
+VIR_ENUM_IMPL(virStoragePartedFsType,
+              VIR_STORAGE_PARTED_FS_TYPE_LAST,
+              "none", "ext2", "fat16",
+              "fat32", "linux-swap",
+              "ext2", "ext2",
+              "extended")
 
 typedef const char *(*virStorageVolFormatToString)(int format);
 typedef int (*virStorageVolFormatFromString)(const char *format);
index a2a164e1a3fd51f2d6d7f8274438169544006859..61d3841007bb943e5405b3d2aba302b8e043b6e5 100644 (file)
@@ -76,6 +76,7 @@ struct _virStorageVolTarget {
     char *path;
     int format;
     virStoragePerms perms;
+    int type; /* only used by disk backend for partition type */
 };
 
 
@@ -154,6 +155,18 @@ struct _virStoragePoolSourceHost {
 };
 
 
+/*
+ * For MSDOS partitions, the free area
+ * is important when creating
+ * logical partitions
+ */
+enum virStorageFreeType {
+    VIR_STORAGE_FREE_NONE = 0,
+    VIR_STORAGE_FREE_NORMAL,
+    VIR_STORAGE_FREE_LOGICAL,
+    VIR_STORAGE_FREE_LAST
+};
+
 /*
  * Available extents on the underlying storage
  */
@@ -162,6 +175,7 @@ typedef virStoragePoolSourceDeviceExtent *virStoragePoolSourceDeviceExtentPtr;
 struct _virStoragePoolSourceDeviceExtent {
     unsigned long long start;
     unsigned long long end;
+    int type;  /* free space type */
 };
 
 
@@ -176,6 +190,13 @@ struct _virStoragePoolSourceDevice {
     virStoragePoolSourceDeviceExtentPtr freeExtents;
     char *path;
     int format;     /* Pool specific source format */
+    /* When the source device is a physical disk,
+       the geometry data is needed */
+    struct _geometry {
+        int cyliders;
+        int heads;
+        int sectors;
+    } geometry;
 };
 
 
@@ -443,6 +464,30 @@ enum virStorageVolFormatDisk {
 };
 VIR_ENUM_DECL(virStorageVolFormatDisk)
 
+enum virStorageVolTypeDisk {
+    VIR_STORAGE_VOL_DISK_TYPE_NONE = 0,
+    VIR_STORAGE_VOL_DISK_TYPE_PRIMARY,
+    VIR_STORAGE_VOL_DISK_TYPE_LOGICAL,
+    VIR_STORAGE_VOL_DISK_TYPE_EXTENDED,
+    VIR_STORAGE_VOL_DISK_TYPE_LAST,
+};
 
+/*
+ * Mapping of Parted fs-types
+ * MUST be kept in the same order
+ * as virStorageVolFormatDisk
+ */
+enum virStoragePartedFsType {
+    VIR_STORAGE_PARTED_FS_TYPE_NONE = 0,
+    VIR_STORAGE_PARTED_FS_TYPE_LINUX,
+    VIR_STORAGE_PARTED_FS_TYPE_FAT16,
+    VIR_STORAGE_PARTED_FS_TYPE_FAT32,
+    VIR_STORAGE_PARTED_FS_TYPE_LINUX_SWAP,
+    VIR_STORAGE_PARTED_FS_TYPE_LINUX_LVM,
+    VIR_STORAGE_PARTED_FS_TYPE_LINUX_RAID,
+    VIR_STORAGE_PARTED_FS_TYPE_EXTENDED,
+    VIR_STORAGE_PARTED_FS_TYPE_LAST,
+};
+VIR_ENUM_DECL(virStoragePartedFsType)
 
 #endif /* __VIR_STORAGE_CONF_H__ */