Revision 22a6d232 image_creator/disk.py
b/image_creator/disk.py | ||
---|---|---|
31 | 31 |
# interpreted as representing official policies, either expressed |
32 | 32 |
# or implied, of GRNET S.A. |
33 | 33 |
|
34 |
from image_creator.util import get_command |
|
34 |
from image_creator.util import get_command, warn, progress_generator
|
|
35 | 35 |
from image_creator import FatalError |
36 |
from clint.textui import progress
|
|
36 |
from clint.textui import indent, puts, colored
|
|
37 | 37 |
|
38 | 38 |
import stat |
39 | 39 |
import os |
... | ... | |
99 | 99 |
This instance is a snapshot of the original source media of |
100 | 100 |
the Disk instance. |
101 | 101 |
""" |
102 |
sourcedev = self.source |
|
103 |
mode = os.stat(self.source).st_mode |
|
104 |
if stat.S_ISDIR(mode): |
|
105 |
return self._losetup(self._dir_to_disk()) |
|
106 |
elif stat.S_ISREG(mode): |
|
107 |
sourcedev = self._losetup(self.source) |
|
108 |
elif not stat.S_ISBLK(mode): |
|
109 |
raise ValueError("Value for self.source is invalid") |
|
102 |
|
|
103 |
puts("Examining source media `%s'" % self.source) |
|
104 |
with indent(4): |
|
105 |
sourcedev = self.source |
|
106 |
mode = os.stat(self.source).st_mode |
|
107 |
if stat.S_ISDIR(mode): |
|
108 |
puts(colored.green('Looks like a directory')) |
|
109 |
return self._losetup(self._dir_to_disk()) |
|
110 |
elif stat.S_ISREG(mode): |
|
111 |
puts(colored.green('Looks like an image file')) |
|
112 |
sourcedev = self._losetup(self.source) |
|
113 |
elif not stat.S_ISBLK(mode): |
|
114 |
raise ValueError("Invalid media source. Only block devices, " |
|
115 |
"regular files and directories are supported.") |
|
116 |
else: |
|
117 |
puts(colored.green('Looks like a block device')) |
|
118 |
#puts() |
|
110 | 119 |
|
111 | 120 |
# Take a snapshot and return it to the user |
112 |
size = blockdev('--getsize', sourcedev) |
|
113 |
cowfd, cow = tempfile.mkstemp() |
|
114 |
os.close(cowfd) |
|
115 |
self._add_cleanup(os.unlink, cow) |
|
116 |
# Create 1G cow sparse file |
|
117 |
dd('if=/dev/null', 'of=%s' % cow, 'bs=1k', 'seek=%d' % (1024 * 1024)) |
|
118 |
cowdev = self._losetup(cow) |
|
119 |
|
|
120 |
snapshot = uuid.uuid4().hex |
|
121 |
tablefd, table = tempfile.mkstemp() |
|
122 |
try: |
|
123 |
os.write(tablefd, "0 %d snapshot %s %s n 8" % \ |
|
124 |
(int(size), sourcedev, cowdev)) |
|
125 |
dmsetup('create', snapshot, table) |
|
126 |
self._add_cleanup(dmsetup, 'remove', snapshot) |
|
127 |
finally: |
|
128 |
os.unlink(table) |
|
121 |
puts("Snapshotting media source") |
|
122 |
with indent(4): |
|
123 |
size = blockdev('--getsize', sourcedev) |
|
124 |
cowfd, cow = tempfile.mkstemp() |
|
125 |
os.close(cowfd) |
|
126 |
self._add_cleanup(os.unlink, cow) |
|
127 |
# Create 1G cow sparse file |
|
128 |
dd('if=/dev/null', 'of=%s' % cow, 'bs=1k', \ |
|
129 |
'seek=%d' % (1024 * 1024)) |
|
130 |
cowdev = self._losetup(cow) |
|
131 |
|
|
132 |
snapshot = uuid.uuid4().hex |
|
133 |
tablefd, table = tempfile.mkstemp() |
|
134 |
try: |
|
135 |
os.write(tablefd, "0 %d snapshot %s %s n 8" % \ |
|
136 |
(int(size), sourcedev, cowdev)) |
|
137 |
dmsetup('create', snapshot, table) |
|
138 |
self._add_cleanup(dmsetup, 'remove', snapshot) |
|
139 |
# Sometimes dmsetup remove fails with Device or resource busy, |
|
140 |
# although everything is cleaned up and the snapshot is not |
|
141 |
# used by anyone. Add a 2 seconds delay to be on the safe side. |
|
142 |
self._add_cleanup(time.sleep, 2) |
|
143 |
|
|
144 |
finally: |
|
145 |
os.unlink(table) |
|
146 |
puts(colored.green('Done')) |
|
147 |
# puts() |
|
129 | 148 |
new_device = DiskDevice("/dev/mapper/%s" % snapshot) |
130 | 149 |
self._devices.append(new_device) |
131 | 150 |
new_device.enable() |
... | ... | |
139 | 158 |
device.destroy() |
140 | 159 |
|
141 | 160 |
|
142 |
def progress_generator(label=''): |
|
143 |
position = 0 |
|
144 |
for i in progress.bar(range(100), label): |
|
145 |
if i < position: |
|
146 |
continue |
|
147 |
position = yield |
|
148 |
yield # suppress the StopIteration exception |
|
149 |
|
|
150 |
|
|
151 | 161 |
class DiskDevice(object): |
152 | 162 |
"""This class represents a block device hosting an Operating System |
153 | 163 |
as created by the device-mapper. |
... | ... | |
170 | 180 |
|
171 | 181 |
def enable(self): |
172 | 182 |
"""Enable a newly created DiskDevice""" |
173 |
|
|
174 |
self.progressbar = progress_generator("VM lauch: ")
|
|
175 |
self.progressbar.next() |
|
176 |
eh = self.g.set_event_callback(self.progress_callback, |
|
183 |
self.progressbar = progress_generator("Launching helper VM: ") |
|
184 |
with indent(4):
|
|
185 |
self.progressbar.next()
|
|
186 |
eh = self.g.set_event_callback(self.progress_callback,
|
|
177 | 187 |
guestfs.EVENT_PROGRESS) |
178 |
self.g.launch() |
|
179 |
self.guestfs_enabled = True |
|
180 |
self.g.delete_event_callback(eh) |
|
181 |
if self.progressbar is not None: |
|
182 |
self.progressbar.send(100) |
|
183 |
self.progressbar = None |
|
184 |
|
|
185 |
roots = self.g.inspect_os() |
|
186 |
if len(roots) == 0: |
|
187 |
raise FatalError("No operating system found") |
|
188 |
if len(roots) > 1: |
|
189 |
raise FatalError("Multiple operating systems found") |
|
190 |
|
|
191 |
self.root = roots[0] |
|
192 |
self.ostype = self.g.inspect_get_type(self.root) |
|
193 |
self.distro = self.g.inspect_get_distro(self.root) |
|
188 |
self.g.launch() |
|
189 |
self.guestfs_enabled = True |
|
190 |
self.g.delete_event_callback(eh) |
|
191 |
if self.progressbar is not None: |
|
192 |
self.progressbar.send(100) |
|
193 |
self.progressbar = None |
|
194 |
puts(colored.green('Done')) |
|
195 |
|
|
196 |
puts('Inspecting Operating System') |
|
197 |
with indent(4): |
|
198 |
roots = self.g.inspect_os() |
|
199 |
if len(roots) == 0: |
|
200 |
raise FatalError("No operating system found") |
|
201 |
if len(roots) > 1: |
|
202 |
raise FatalError("Multiple operating systems found." |
|
203 |
"We only support images with one filesystem.") |
|
204 |
self.root = roots[0] |
|
205 |
self.ostype = self.g.inspect_get_type(self.root) |
|
206 |
self.distro = self.g.inspect_get_distro(self.root) |
|
207 |
puts(colored.green('Found a %s system' % self.distro)) |
|
208 |
puts() |
|
194 | 209 |
|
195 | 210 |
def destroy(self): |
196 | 211 |
"""Destroy this DiskDevice instance.""" |
... | ... | |
242 | 257 |
disk and then updating the partition table. The new disk size |
243 | 258 |
(in bytes) is returned. |
244 | 259 |
""" |
260 |
puts("Shrinking image (this may take a while)") |
|
261 |
|
|
245 | 262 |
dev = self.g.part_to_dev(self.root) |
246 | 263 |
parttype = self.g.part_get_parttype(dev) |
247 | 264 |
if parttype != 'msdos': |
... | ... | |
257 | 274 |
part_dev = "%s%d" % (dev, last_partition['part_num']) |
258 | 275 |
fs_type = self.g.vfs_type(part_dev) |
259 | 276 |
if not re.match("ext[234]", fs_type): |
260 |
print "Warning: Don't know how to resize %s partitions." % vfs_type
|
|
277 |
warn("Don't know how to resize %s partitions." % vfs_type)
|
|
261 | 278 |
return |
262 | 279 |
|
263 |
self.g.e2fsck_f(part_dev) |
|
264 |
self.g.resize2fs_M(part_dev) |
|
265 |
output = self.g.tune2fs_l(part_dev) |
|
266 |
block_size = int(filter(lambda x: x[0] == 'Block size', output)[0][1]) |
|
267 |
block_cnt = int(filter(lambda x: x[0] == 'Block count', output)[0][1]) |
|
280 |
with indent(4): |
|
281 |
self.g.e2fsck_f(part_dev) |
|
282 |
self.g.resize2fs_M(part_dev) |
|
283 |
|
|
284 |
output = self.g.tune2fs_l(part_dev) |
|
285 |
block_size = int( |
|
286 |
filter(lambda x: x[0] == 'Block size', output)[0][1]) |
|
287 |
block_cnt = int( |
|
288 |
filter(lambda x: x[0] == 'Block count', output)[0][1]) |
|
268 | 289 |
|
269 |
sector_size = self.g.blockdev_getss(dev) |
|
290 |
sector_size = self.g.blockdev_getss(dev)
|
|
270 | 291 |
|
271 |
start = last_partition['part_start'] / sector_size |
|
272 |
end = start + (block_size * block_cnt) / sector_size - 1 |
|
292 |
start = last_partition['part_start'] / sector_size
|
|
293 |
end = start + (block_size * block_cnt) / sector_size - 1
|
|
273 | 294 |
|
274 |
self.g.part_del(dev, last_partition['part_num']) |
|
275 |
self.g.part_add(dev, 'p', start, end) |
|
295 |
self.g.part_del(dev, last_partition['part_num'])
|
|
296 |
self.g.part_add(dev, 'p', start, end)
|
|
276 | 297 |
|
277 |
return (end + 1) * sector_size |
|
298 |
new_size = (end + 1) * sector_size |
|
299 |
puts(" New image size is %dMB\n" % (new_size // 2 ** 20)) |
|
300 |
return new_size |
|
278 | 301 |
|
279 | 302 |
def size(self): |
280 | 303 |
"""Returns the "payload" size of the device. |
Also available in: Unified diff