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 : |