---
imgcreate/fs.py | 223 +++++++++++++++++++++++++++++++++++++++++++++++-
imgcreate/kickstart.py | 4 +
2 files changed, 225 insertions(+), 2 deletions(-)
diff --git a/imgcreate/fs.py b/imgcreate/fs.py
index 314a776..aaff4c3 100644
--- a/imgcreate/fs.py
+++ b/imgcreate/fs.py
@@ -357,11 +357,229 @@ class ExtDiskMount(DiskMount):
def resparse(self, size = None):
self.cleanup()
-
minsize = self.__resize_to_minimal()
-
self.disk.truncate(minsize)
return minsize
+
+class PartitionedMount(Mount):
+ def __init__(self, disks, mountdir):
+ Mount.__init__(self, mountdir)
+ self.disks = {}
+ for name in disks.keys():
+ self.disks[name] = { 'disk': disks[name], # Disk object
+ 'mapped': False, # True if kpartx mapping
exists
+ 'numpart': 0, # Number of allocate partitions
+ 'partitions': [], # indexes to self.partitions
+ 'extended': 0, # Size of extended partition
+ 'offset': 0 } # Offset of next partition
+
+ self.partitions = []
+ self.mapped = False
+ self.mountOrder = []
+ self.unmountOrder = []
+
+ def add_partition(self, size, disk, mountpoint, fstype = None):
+ self.partitions.append({'size': size,
+ 'mountpoint': mountpoint, # Mount relative to
chroot
+ 'fstype': fstype,
+ 'disk': disk, # physical disk name holding
partition
+ 'device': None, # kpartx device node for
partition
+ 'mount': None, # Mount object
+ 'num': None}) # Partition number
+
+ def __format_disks(self):
+ logging.debug("Formatting disks")
+ for dev in self.disks.keys():
+ d = self.disks[dev]
+ logging.debug("Initializing partition table for %s" %
(d['disk'].device))
+ rc = subprocess.call(["/sbin/parted", "-s",
d['disk'].device, "mklabel", "msdos"])
+ if rc != 0:
+ raise MountError("Error writing partition table on %s" %
d.device)
+
+ logging.debug("Assigning partitions to disks")
+ for n in range(len(self.partitions)):
+ p = self.partitions[n]
+
+ if not self.disks.has_key(p['disk']):
+ raise MountError("No disk %s for partition %s" %
(p['disk'], p['mountpoint']))
+
+ d = self.disks[p['disk']]
+ d['numpart'] += 1
+ if d['numpart'] > 3:
+ # Increase allocation of extended partition to hold this partition
+ d['extended'] += p['size']
+ p['type'] = 'logical'
+ p['num'] = d['numpart'] + 1
+ else:
+ p['type'] = 'primary'
+ p['num'] = d['numpart']
+
+ p['start'] = d['offset']
+ d['offset'] += p['size']
+ d['partitions'].append(n)
+ logging.debug("Assigned %s to %s%d at %d at size %d" %
(p['mountpoint'], p['disk'], p['num'], p['start'],
p['size']))
+
+ # XXX we should probably work in cylinder units to keep fdisk happier..
+ start = 0
+ logging.debug("Creating partitions")
+ for p in self.partitions:
+ d = self.disks[p['disk']]
+ if p['num'] == 5:
+ logging.debug("Added extended part at %d of size %d" %
(p['start'], d['extended']))
+ rc = subprocess.call(["/sbin/parted", "-s",
d['disk'].device, "mkpart", "extended",
+ "%dM" % p['start'],
"%dM" % (p['start'] + d['extended'])])
+
+ logging.debug("Add %s part at %d of size %d" % (p['type'],
p['start'], p['size']))
+ rc = subprocess.call(["/sbin/parted", "-s",
d['disk'].device, "mkpart",
+ p['type'], "%dM" %
p['start'], "%dM" % (p['start']+p['size'])])
+
+ # XXX disabled return code check because parted always fails to
+ # reload part table with loop devices. Annoying because we can't
+ # distinguish this failure from real partition failures :-(
+ if rc != 0 and 1 == 0:
+ raise MountError("Error creating partition on %s" %
d['disk'].device)
+
+ def __map_partitions(self):
+ for dev in self.disks.keys():
+ d = self.disks[dev]
+ if d['mapped']:
+ continue
+
+ kpartx = subprocess.Popen(["/sbin/kpartx", "-l",
d['disk'].device],
+ stdout=subprocess.PIPE)
+
+ kpartxOutput = kpartx.communicate()[0].split("\n")
+ # Strip trailing blank
+ kpartxOutput = kpartxOutput[0:len(kpartxOutput)-1]
+
+ if kpartx.returncode:
+ raise MountError("Failed to query partition mapping for
'%s'" %
+ d.device)
+
+ # Quick sanity check that the number of partitions matches
+ # our expectation. If it doesn't, someone broke the code
+ # further up
+ if len(kpartxOutput) != d['numpart']:
+ raise MountError("Unexpected number of partitions from kpartx: %d !=
%d" %
+ (len(kpartxOutput), d['numpart']))
+
+ for i in range(len(kpartxOutput)):
+ line = kpartxOutput[i]
+ newdev = line.split()[0]
+ mapperdev = "/dev/mapper/" + newdev
+ loopdev = d['disk'].device + newdev[-1]
+
+ logging.debug("Dev %s: %s -> %s" % (newdev, loopdev,
mapperdev))
+ pnum = d['partitions'][i]
+ self.partitions[pnum]['device'] = loopdev
+
+ # grub's install wants partitions to be named
+ # to match their parent device + partition num
+ # kpartx doesn't work like this, so we add compat
+ # symlinks to point to /dev/mapper
+ os.symlink(mapperdev, loopdev)
+
+ logging.debug("Adding partx mapping for %s" %
d['disk'].device)
+ rc = subprocess.call(["/sbin/kpartx", "-a",
d['disk'].device])
+ if rc != 0:
+ raise MountError("Failed to map partitions for '%s'" %
+ d['disk'].device)
+ d['mapped'] = True
+
+
+ def __unmap_partitions(self):
+ for dev in self.disks.keys():
+ d = self.disks[dev]
+ if not d['mapped']:
+ continue
+
+ logging.debug("Removing compat symlinks")
+ for pnum in d['partitions']:
+ if self.partitions[pnum]['device'] != None:
+ os.unlink(self.partitions[pnum]['device'])
+ self.partitions[pnum]['device'] = None
+
+ logging.debug("Unmapping %s" % d['disk'].device)
+ rc = subprocess.call(["/sbin/kpartx", "-d",
d['disk'].device])
+ if rc != 0:
+ raise MountError("Failed to unmap partitions for '%s'"
%
+ d['disk'].device)
+
+ d['mapped'] = False
+
+
+ def __calculate_mountorder(self):
+ for p in self.partitions:
+ self.mountOrder.append(p['mountpoint'])
+ self.unmountOrder.append(p['mountpoint'])
+
+ self.mountOrder.sort()
+ self.unmountOrder.sort()
+ self.unmountOrder.reverse()
+ print str(self.mountOrder)
+
+ def cleanup(self):
+ Mount.cleanup(self)
+ self.__unmap_partitions()
+ for dev in self.disks.keys():
+ d = self.disks[dev]
+ try:
+ d['disk'].cleanup()
+ except:
+ pass
+
+ def unmount(self):
+ for mp in self.unmountOrder:
+ if mp == 'swap':
+ continue
+ p = None
+ for p1 in self.partitions:
+ if p1['mountpoint'] == mp:
+ p = p1
+ break
+
+ if p['mount'] != None:
+ try:
+ p['mount'].cleanup()
+ except:
+ pass
+ p['mount'] = None
+
+ def mount(self):
+ for dev in self.disks.keys():
+ d = self.disks[dev]
+ d['disk'].create()
+
+ self.__format_disks()
+ self.__map_partitions()
+ self.__calculate_mountorder()
+
+ for mp in self.mountOrder:
+ p = None
+ for p1 in self.partitions:
+ if p1['mountpoint'] == mp:
+ p = p1
+ break
+
+ if mp == 'swap':
+ subprocess.call(["/sbin/mkswap", p['device']])
+ continue
+
+ rmmountdir = False
+ if p['mountpoint'] == "/":
+ rmmountdir = True
+ pdisk = ExtDiskMount(RawDisk(p['size'] * 1024 * 1024,
p['device']),
+ self.mountdir + p['mountpoint'],
+ p['fstype'],
+ 4096,
+ p['mountpoint'],
+ rmmountdir)
+ pdisk.mount()
+ p['mount'] = pdisk
+
+ def resparse(self, size = None):
+ # Can't re-sparse a disk image - too hard
+ pass
class DeviceMapperSnapshot(object):
def __init__(self, imgloop, cowloop):
@@ -478,3 +696,4 @@ def create_image_minimizer(path, image, minimal_size):
mksquashfs(cowloop.lofile, path)
os.unlink(cowloop.lofile)
+
diff --git a/imgcreate/kickstart.py b/imgcreate/kickstart.py
index ef7b9e4..eff28bb 100644
--- a/imgcreate/kickstart.py
+++ b/imgcreate/kickstart.py
@@ -474,6 +474,9 @@ def get_groups(ks, required = []):
def get_excluded(ks, required = []):
return ks.handler.packages.excludedList + required
+def get_partitions(ks, required = []):
+ return ks.handler.partition.partitions
+
def ignore_missing(ks):
return ks.handler.packages.handleMissing == ksconstants.KS_MISSING_IGNORE
@@ -490,3 +493,4 @@ def get_post_scripts(ks):
def selinux_enabled(ks):
return ks.handler.selinux.selinux == ksconstants.SELINUX_ENFORCING
+
--
1.5.4.1