-
Nikos Skalkotos authored1377b8a7
disk.py 3.63 KiB
#!/usr/bin/env python
import losetup
import stat
import os
import tempfile
import uuid
import re
import sys
import guestfs
from pbs import dmsetup
from pbs import blockdev
from pbs import dd
class DiskError(Exception): pass
class Disk(object):
def __init__(self, source):
self._cleanup_jobs = []
self._devices = []
self.source = source
def _add_cleanup(self, job, *args):
self._cleanup_jobs.append((job, args))
def _losetup(self, fname):
loop = losetup.find_unused_loop_device()
loop.mount(fname)
self._add_cleanup(loop.unmount)
return loop.device
def _dir_to_disk(self):
raise NotImplementedError
def cleanup(self):
while len(self._devices):
device = self._devices.pop()
device.destroy()
while len(self._cleanup_jobs):
job, args = self._cleanup_jobs.pop()
job(*args)
def get_device(self):
sourcedev = self.source
mode = os.stat(self.source).st_mode
if stat.S_ISDIR(mode):
return self._losetup(self._dir_to_disk())
elif stat.S_ISREG(mode):
sourcedev = self._losetup(self.source)
elif not stat.S_ISBLK(mode):
raise ValueError("Value for self.source is invalid")
# Take a snapshot and return it to the user
size = blockdev('--getsize', sourcedev)
cowfd, cow = tempfile.mkstemp()
self._add_cleanup(os.unlink, cow)
# Create 1G cow file
dd('if=/dev/null', 'of=%s' % cow, 'bs=1k' ,'seek=%d' % (1024*1024))
cowdev = self._losetup(cow)
snapshot = uuid.uuid4().hex
tablefd, table = tempfile.mkstemp()
try:
os.write(tablefd, "0 %d snapshot %s %s n 8" % \
(int(size), sourcedev, cowdev))
dmsetup('create', snapshot, table)
self._add_cleanup(dmsetup, 'remove', snapshot)
finally:
os.unlink(table)
new_device = DiskDevice("/dev/mapper/%s" % snapshot)
self._devices.append(new_device)
return new_device
def destroy_device(self, device):
self._devices.remove(device)
device.destroy()
class DiskDevice(object):
def __init__(self, device, bootable = True):
self.device = device
self.bootable = bootable
self.g = guestfs.GuestFS()
self.g.add_drive_opts(device, readonly = 0)
self.g.launch()
roots = self.g.inspect_os()
if len(roots) == 0:
raise DiskError("No operating system found")
if len(roots) > 1:
raise DiskError("Multiple operating systems found")
self.root = roots[0]
def destroy(self):
self.g.umount_all()
self.g.sync()
# Close the guestfs handler
del self.g
def get_image_metadata(self):
meta = {}
meta["OSFAMILY"] = self.g.inspect_get_type(self.root)
meta["OS"] = self.g.inspect_get_distro(self.root)
meta["description"] = self.g.inspect_get_product_name(self.root)
return meta
def mount(self):
mps = g.inspect_get_mountpoints(self.root)
# Sort the keys to mount the fs in a correct order.
# / should be mounted befor /boot, etc
def compare (a, b):
if len(a[0]) > len(b[0]): return 1
elif len(a[0]) == len(b[0]): return 0
else: return -1
mps.sort(compare)
for mp, dev in mps:
try:
self.g.mount(dev, mp)
except RuntimeError as msg:
print "%s (ignored)" % msg
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :