Statistics
| Branch: | Tag: | Revision:

root / image_creator / disk.py @ 1377b8a7

History | View | Annotate | Download (3.6 kB)

1 d57775d4 Nikos Skalkotos
#!/usr/bin/env python
2 d57775d4 Nikos Skalkotos
3 d57775d4 Nikos Skalkotos
import losetup
4 d57775d4 Nikos Skalkotos
import stat
5 d57775d4 Nikos Skalkotos
import os
6 d57775d4 Nikos Skalkotos
import tempfile
7 d57775d4 Nikos Skalkotos
import uuid
8 d57775d4 Nikos Skalkotos
import re
9 1377b8a7 Nikos Skalkotos
import sys
10 1377b8a7 Nikos Skalkotos
import guestfs
11 1377b8a7 Nikos Skalkotos
12 d57775d4 Nikos Skalkotos
from pbs import dmsetup
13 d57775d4 Nikos Skalkotos
from pbs import blockdev
14 d57775d4 Nikos Skalkotos
from pbs import dd
15 1377b8a7 Nikos Skalkotos
16 1377b8a7 Nikos Skalkotos
class DiskError(Exception): pass
17 d57775d4 Nikos Skalkotos
18 d57775d4 Nikos Skalkotos
class Disk(object):
19 d57775d4 Nikos Skalkotos
20 d57775d4 Nikos Skalkotos
    def __init__(self, source):
21 d57775d4 Nikos Skalkotos
        self._cleanup_jobs = []
22 d57775d4 Nikos Skalkotos
        self._devices = []
23 d57775d4 Nikos Skalkotos
        self.source = source
24 d57775d4 Nikos Skalkotos
25 d57775d4 Nikos Skalkotos
    def _add_cleanup(self, job, *args):
26 d57775d4 Nikos Skalkotos
        self._cleanup_jobs.append((job, args))
27 d57775d4 Nikos Skalkotos
28 d57775d4 Nikos Skalkotos
    def _losetup(self, fname):
29 d57775d4 Nikos Skalkotos
        loop = losetup.find_unused_loop_device()
30 d57775d4 Nikos Skalkotos
        loop.mount(fname)
31 d57775d4 Nikos Skalkotos
        self._add_cleanup(loop.unmount)
32 d57775d4 Nikos Skalkotos
        return loop.device
33 d57775d4 Nikos Skalkotos
34 d57775d4 Nikos Skalkotos
    def _dir_to_disk(self):
35 d57775d4 Nikos Skalkotos
        raise NotImplementedError
36 d57775d4 Nikos Skalkotos
37 d57775d4 Nikos Skalkotos
    def cleanup(self):
38 1377b8a7 Nikos Skalkotos
        while len(self._devices):
39 1377b8a7 Nikos Skalkotos
            device = self._devices.pop()
40 1377b8a7 Nikos Skalkotos
            device.destroy()
41 1377b8a7 Nikos Skalkotos
            
42 d57775d4 Nikos Skalkotos
        while len(self._cleanup_jobs):
43 d57775d4 Nikos Skalkotos
            job, args = self._cleanup_jobs.pop()
44 d57775d4 Nikos Skalkotos
            job(*args)
45 d57775d4 Nikos Skalkotos
46 d57775d4 Nikos Skalkotos
    def get_device(self):
47 d57775d4 Nikos Skalkotos
        sourcedev = self.source
48 d57775d4 Nikos Skalkotos
        mode = os.stat(self.source).st_mode
49 d57775d4 Nikos Skalkotos
        if stat.S_ISDIR(mode):
50 d57775d4 Nikos Skalkotos
            return self._losetup(self._dir_to_disk())
51 d57775d4 Nikos Skalkotos
        elif stat.S_ISREG(mode):
52 d57775d4 Nikos Skalkotos
            sourcedev = self._losetup(self.source)
53 d57775d4 Nikos Skalkotos
        elif not stat.S_ISBLK(mode):
54 d57775d4 Nikos Skalkotos
            raise ValueError("Value for self.source is invalid")
55 d57775d4 Nikos Skalkotos
56 d57775d4 Nikos Skalkotos
        # Take a snapshot and return it to the user
57 d57775d4 Nikos Skalkotos
        size = blockdev('--getsize', sourcedev)
58 d57775d4 Nikos Skalkotos
        cowfd, cow = tempfile.mkstemp()
59 d57775d4 Nikos Skalkotos
        self._add_cleanup(os.unlink, cow)
60 333ff548 Nikos Skalkotos
        # Create 1G cow file
61 333ff548 Nikos Skalkotos
        dd('if=/dev/null', 'of=%s' % cow, 'bs=1k' ,'seek=%d' % (1024*1024))
62 d57775d4 Nikos Skalkotos
        cowdev = self._losetup(cow)
63 d57775d4 Nikos Skalkotos
64 d57775d4 Nikos Skalkotos
        snapshot = uuid.uuid4().hex
65 d57775d4 Nikos Skalkotos
        tablefd, table = tempfile.mkstemp()
66 d57775d4 Nikos Skalkotos
        try:
67 d57775d4 Nikos Skalkotos
            os.write(tablefd, "0 %d snapshot %s %s n 8" % \
68 d57775d4 Nikos Skalkotos
                                        (int(size), sourcedev, cowdev))
69 d57775d4 Nikos Skalkotos
            dmsetup('create', snapshot, table)
70 d57775d4 Nikos Skalkotos
            self._add_cleanup(dmsetup, 'remove', snapshot)
71 d57775d4 Nikos Skalkotos
        finally:
72 d57775d4 Nikos Skalkotos
            os.unlink(table)
73 d57775d4 Nikos Skalkotos
74 1377b8a7 Nikos Skalkotos
        new_device = DiskDevice("/dev/mapper/%s" % snapshot)
75 d57775d4 Nikos Skalkotos
        self._devices.append(new_device)
76 d57775d4 Nikos Skalkotos
        return new_device
77 d57775d4 Nikos Skalkotos
78 1377b8a7 Nikos Skalkotos
    def destroy_device(self, device):
79 1377b8a7 Nikos Skalkotos
        self._devices.remove(device)
80 1377b8a7 Nikos Skalkotos
        device.destroy()
81 1377b8a7 Nikos Skalkotos
82 d57775d4 Nikos Skalkotos
class DiskDevice(object):
83 d57775d4 Nikos Skalkotos
84 1377b8a7 Nikos Skalkotos
    def __init__(self, device, bootable = True):
85 333ff548 Nikos Skalkotos
        self.device = device
86 1377b8a7 Nikos Skalkotos
        self.bootable = bootable
87 1377b8a7 Nikos Skalkotos
88 1377b8a7 Nikos Skalkotos
        self.g = guestfs.GuestFS()
89 1377b8a7 Nikos Skalkotos
        self.g.add_drive_opts(device, readonly = 0)
90 1377b8a7 Nikos Skalkotos
        self.g.launch()
91 1377b8a7 Nikos Skalkotos
        roots = self.g.inspect_os()
92 1377b8a7 Nikos Skalkotos
        if len(roots) == 0:
93 1377b8a7 Nikos Skalkotos
            raise DiskError("No operating system found")
94 1377b8a7 Nikos Skalkotos
        if len(roots) > 1:
95 1377b8a7 Nikos Skalkotos
            raise DiskError("Multiple operating systems found")
96 1377b8a7 Nikos Skalkotos
97 1377b8a7 Nikos Skalkotos
        self.root = roots[0]
98 1377b8a7 Nikos Skalkotos
    
99 1377b8a7 Nikos Skalkotos
    def destroy(self):
100 1377b8a7 Nikos Skalkotos
        self.g.umount_all()
101 1377b8a7 Nikos Skalkotos
        self.g.sync()
102 1377b8a7 Nikos Skalkotos
        # Close the guestfs handler
103 1377b8a7 Nikos Skalkotos
        del self.g
104 1377b8a7 Nikos Skalkotos
    
105 1377b8a7 Nikos Skalkotos
    def get_image_metadata(self):
106 1377b8a7 Nikos Skalkotos
        meta = {}
107 1377b8a7 Nikos Skalkotos
        meta["OSFAMILY"] = self.g.inspect_get_type(self.root)
108 1377b8a7 Nikos Skalkotos
        meta["OS"] = self.g.inspect_get_distro(self.root)
109 1377b8a7 Nikos Skalkotos
        meta["description"] = self.g.inspect_get_product_name(self.root)
110 1377b8a7 Nikos Skalkotos
        return meta
111 1377b8a7 Nikos Skalkotos
112 1377b8a7 Nikos Skalkotos
    def mount(self):
113 1377b8a7 Nikos Skalkotos
        mps = g.inspect_get_mountpoints(self.root)
114 1377b8a7 Nikos Skalkotos
        # Sort the keys to mount the fs in a correct order.
115 1377b8a7 Nikos Skalkotos
        # / should be mounted befor /boot, etc
116 1377b8a7 Nikos Skalkotos
        def compare (a, b):
117 1377b8a7 Nikos Skalkotos
            if len(a[0]) > len(b[0]): return 1
118 1377b8a7 Nikos Skalkotos
            elif len(a[0]) == len(b[0]): return 0
119 1377b8a7 Nikos Skalkotos
            else: return -1
120 1377b8a7 Nikos Skalkotos
        mps.sort(compare)
121 1377b8a7 Nikos Skalkotos
        for mp, dev in mps:
122 1377b8a7 Nikos Skalkotos
            try:
123 1377b8a7 Nikos Skalkotos
                self.g.mount(dev, mp)
124 1377b8a7 Nikos Skalkotos
            except RuntimeError as msg:
125 1377b8a7 Nikos Skalkotos
                print "%s (ignored)" % msg
126 d57775d4 Nikos Skalkotos
127 d57775d4 Nikos Skalkotos
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :