Add support for gpt partitions in bundle_volume
authorNikos Skalkotos <skalkoto@grnet.gr>
Tue, 29 Jan 2013 13:27:54 +0000 (15:27 +0200)
committerNikos Skalkotos <skalkoto@grnet.gr>
Tue, 29 Jan 2013 13:27:54 +0000 (15:27 +0200)
image_creator/bundle_volume.py
image_creator/gpt.py

index f2aa22a..e373f88 100644 (file)
@@ -34,6 +34,7 @@
 import os
 import re
 import tempfile
+import uuid
 from collections import namedtuple
 
 import parted
@@ -43,6 +44,7 @@ from image_creator.util import get_command
 from image_creator.util import FatalError
 from image_creator.util import try_fail_repeat
 from image_creator.util import free_space
+from image_creator.gpt import GPTPartitionTable
 
 findfs = get_command('findfs')
 dd = get_command('dd')
@@ -128,17 +130,23 @@ class BundleVolume(object):
 
     def _create_partition_table(self, image):
 
-        if self.disk.type != 'msdos':
-            raise FatalError('Only msdos partition tables are supported')
-
         # Copy the MBR and the space between the MBR and the first partition.
-        # In Grub version 1 Stage 1.5 is located there.
+        # In msdos partitons tables Grub Stage 1.5 is located there.
+        # In gpt partition tables the Primary GPT Header is there.
         first_sector = self.disk.getPrimaryPartitions()[0].geometry.start
 
         dd('if=%s' % self.disk.device.path, 'of=%s' % image,
            'bs=%d' % self.disk.device.sectorSize,
            'count=%d' % first_sector, 'conv=notrunc')
 
+        if self.disk.type == 'gpt':
+            # Copy the Secondary GPT Header
+            table = GPTPartitionTable(self.disk.device.path)
+            dd('if=%s' % self.disk.device.path, 'of=%s' % image,
+            'bs=%d' % self.disk.device.sectorSize, 'conv=notrunc',
+            'seek=%d' % table.primary.last_usable_lba,
+            'skip=%d' % table.primary.last_usable_lba)
+
         # Create the Extended boot records (EBRs) in the image
         extended = self.disk.getExtendedPartition()
         if not extended:
@@ -172,8 +180,7 @@ class BundleVolume(object):
 
         new_end = self.disk.device.length
 
-        image_dev = parted.Device(image)
-        image_disk = parted.Disk(image_dev)
+        image_disk = parted.Disk(parted.Device(image))
 
         is_extended = lambda p: p.type == parted.PARTITION_EXTENDED
         is_logical = lambda p: p.type == parted.PARTITION_LOGICAL
@@ -188,19 +195,18 @@ class BundleVolume(object):
 
             image_disk.deletePartition(
                 image_disk.getPartitionBySector(last.start))
-            image_disk.commit()
+            image_disk.commitToDevice()
 
             if is_logical(last) and last.num == 5:
                 extended = image_disk.getExtendedPartition()
                 image_disk.deletePartition(extended)
-                image_disk.commit()
+                image_disk.commitToDevice()
                 partitions.remove(filter(is_extended, partitions)[0])
 
             partitions.remove(last)
             last = partitions[-1]
 
-            # Leave 2048 blocks at the end
-            new_end = last.end + 2048
+            new_end = last.end
 
         mount_options = self._get_mount_options(
             self.disk.getPartitionBySector(last.start).path)
@@ -220,30 +226,23 @@ class BundleVolume(object):
                 image_disk.getPartitionBySector(last.start),
                 parted.Constraint(device=image_disk.device),
                 start=last.start, end=part_end)
-            image_disk.commit()
+            image_disk.commitToDevice()
 
             # Parted may have changed this for better alignment
             part_end = image_disk.getPartitionBySector(last.start).geometry.end
             last = last._replace(end=part_end)
             partitions[-1] = last
 
-            # Leave 2048 blocks at the end.
-            new_end = part_end + 2048
+            new_end = part_end
 
             if last.type == parted.PARTITION_LOGICAL:
                 # Fix the extended partition
-                extended = disk.getExtendedPartition()
-
-                image_disk.setPartitionGeometry(
-                    extended, parted.Constraint(device=img_dev),
-                    ext.geometry.start, end=last.end)
-                image_disk.commit()
+                image_disk.minimizeExtendedPartition()
 
-        # image_dev.destroy()
-        return new_end
+        return (new_end, self._get_partitions(image_disk))
 
     def _map_partition(self, dev, num, start, end):
-        name = os.path.basename(dev)
+        name = os.path.basename(dev) + "_" + uuid.uuid4().hex
         tablefd, table = tempfile.mkstemp()
         try:
             size = end - start + 1
@@ -321,14 +320,13 @@ class BundleVolume(object):
                  '/boot/grub/menu.lst',
                  '/boot/grub/grub.conf']
 
-        orig = dict(map(
-            lambda p: (
-                p.number,
-                blkid('-s', 'UUID', '-o', 'value', p.path).stdout.strip()),
-            self.disk.partitions))
+        orig = {}
+        for p in self.disk.partitions:
+            if p.number in new_uuid.keys():
+                orig[p.number] = \
+                    blkid('-s', 'UUID', '-o', 'value', p.path).stdout.strip()
 
         for f in map(lambda f: target + f, files):
-
             if not os.path.exists(f):
                 continue
 
@@ -340,13 +338,12 @@ class BundleVolume(object):
                         line = re.sub(orig[i], uuid, line)
                     dest.write(line)
 
-    def _create_filesystems(self, image):
+    def _create_filesystems(self, image, partitions):
 
         filesystem = {}
         for p in self.disk.partitions:
             filesystem[p.number] = self._get_mount_options(p.path)
 
-        partitions = self._get_partitions(parted.Disk(parted.Device(image)))
         unmounted = filter(lambda p: filesystem[p.num] is None, partitions)
         mounted = filter(lambda p: filesystem[p.num] is not None, partitions)
 
@@ -429,10 +426,17 @@ class BundleVolume(object):
             os.close(fd)
 
         self._create_partition_table(image)
-
-        end_sector = self._shrink_partitions(image)
-
-        size = (end_sector + 1) * self.disk.device.sectorSize
+        end_sector, partitions = self._shrink_partitions(image)
+
+        if self.disk.type == 'gpt':
+            old_size = size
+            size = (end_sector + 1) * self.disk.device.sectorSize
+            ptable = GPTPartitionTable(image)
+            size = ptable.shrink(size, old_size)
+        else:
+            # Alighn to 2048
+            end_sector = ((end_sector + 2047) // 2048) * 2048
+            size = (end_sector + 1) * self.disk.device.sectorSize
 
         # Truncate image to the new size.
         fd = os.open(image, os.O_RDWR)
@@ -449,7 +453,7 @@ class BundleVolume(object):
                              dirname)
         self.out.success("sufficient")
 
-        self._create_filesystems(image)
+        self._create_filesystems(image, partitions)
 
         return image
 
index fa9dbde..98f7cc8 100644 (file)
@@ -296,6 +296,7 @@ class GPTPartitionTable(object):
             d.write(self.mbr.pack())
             d.write(self.primary.pack())
             d.write('\x00' * (BLOCKSIZE - self.primary.size()))
+            d.write(self.part_entries)
             d.seek(self.secondary.part_entry_start * BLOCKSIZE)
             d.write(self.part_entries)
             d.seek(self.primary.backup_lba * BLOCKSIZE)