summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Zanussi <tom.zanussi@linux.intel.com>2014-08-03 18:49:20 -0500
committerRichard Purdie <richard.purdie@linuxfoundation.org>2014-08-11 10:53:09 +0100
commitf0499a07b5641a92993cb878e824ef4cca7911ad (patch)
tree2e4c19344b591582e4b630bcac97e861a40c072b
parent93a60f7e99268fd66db12f30b237862500294266 (diff)
downloadpoky-f0499a07b5641a92993cb878e824ef4cca7911ad.tar.gz
wic: Remove unused fs_related code
wic doesn't use it, so remove it. (From OE-Core rev: e18d5393a556e1ff4826c1e5b1a75f4ac998b10e) Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r--scripts/lib/mic/utils/fs_related.py930
1 files changed, 0 insertions, 930 deletions
diff --git a/scripts/lib/mic/utils/fs_related.py b/scripts/lib/mic/utils/fs_related.py
index 182171ffd3..e6e362d0ba 100644
--- a/scripts/lib/mic/utils/fs_related.py
+++ b/scripts/lib/mic/utils/fs_related.py
@@ -31,19 +31,6 @@ from mic.utils import runner
31from mic.utils.errors import * 31from mic.utils.errors import *
32from mic.utils.oe.misc import * 32from mic.utils.oe.misc import *
33 33
34def find_binary_inchroot(binary, chroot):
35 paths = ["/usr/sbin",
36 "/usr/bin",
37 "/sbin",
38 "/bin"
39 ]
40
41 for path in paths:
42 bin_path = "%s/%s" % (path, binary)
43 if os.path.exists("%s/%s" % (chroot, bin_path)):
44 return bin_path
45 return None
46
47def find_binary_path(binary): 34def find_binary_path(binary):
48 if os.environ.has_key("PATH"): 35 if os.environ.has_key("PATH"):
49 paths = os.environ["PATH"].split(":") 36 paths = os.environ["PATH"].split(":")
@@ -72,176 +59,6 @@ def makedirs(dirname):
72 if err.errno != errno.EEXIST: 59 if err.errno != errno.EEXIST:
73 raise 60 raise
74 61
75def mksquashfs(in_img, out_img):
76 fullpathmksquashfs = find_binary_path("mksquashfs")
77 args = [fullpathmksquashfs, in_img, out_img]
78
79 if not sys.stdout.isatty():
80 args.append("-no-progress")
81
82 ret = runner.show(args)
83 if ret != 0:
84 raise SquashfsError("'%s' exited with error (%d)" % (' '.join(args), ret))
85
86def resize2fs(fs, size):
87 resize2fs = find_binary_path("resize2fs")
88 if size == 0:
89 # it means to minimalize it
90 return runner.show([resize2fs, '-M', fs])
91 else:
92 return runner.show([resize2fs, fs, "%sK" % (size / 1024,)])
93
94def my_fuser(fp):
95 fuser = find_binary_path("fuser")
96 if not os.path.exists(fp):
97 return False
98
99 rc = runner.quiet([fuser, "-s", fp])
100 if rc == 0:
101 for pid in runner.outs([fuser, fp]).split():
102 fd = open("/proc/%s/cmdline" % pid, "r")
103 cmdline = fd.read()
104 fd.close()
105 if cmdline[:-1] == "/bin/bash":
106 return True
107
108 # not found
109 return False
110
111class BindChrootMount:
112 """Represents a bind mount of a directory into a chroot."""
113 def __init__(self, src, chroot, dest = None, option = None):
114 self.root = os.path.abspath(os.path.expanduser(chroot))
115 self.option = option
116
117 self.orig_src = self.src = src
118 if os.path.islink(src):
119 self.src = os.readlink(src)
120 if not self.src.startswith('/'):
121 self.src = os.path.abspath(os.path.join(os.path.dirname(src),
122 self.src))
123
124 if not dest:
125 dest = self.src
126 self.dest = os.path.join(self.root, dest.lstrip('/'))
127
128 self.mounted = False
129 self.mountcmd = find_binary_path("mount")
130 self.umountcmd = find_binary_path("umount")
131
132 def ismounted(self):
133 with open('/proc/mounts') as f:
134 for line in f:
135 if line.split()[1] == os.path.abspath(self.dest):
136 return True
137
138 return False
139
140 def has_chroot_instance(self):
141 lock = os.path.join(self.root, ".chroot.lock")
142 return my_fuser(lock)
143
144 def mount(self):
145 if self.mounted or self.ismounted():
146 return
147
148 makedirs(self.dest)
149 rc = runner.show([self.mountcmd, "--bind", self.src, self.dest])
150 if rc != 0:
151 raise MountError("Bind-mounting '%s' to '%s' failed" %
152 (self.src, self.dest))
153 if self.option:
154 rc = runner.show([self.mountcmd, "--bind", "-o", "remount,%s" % self.option, self.dest])
155 if rc != 0:
156 raise MountError("Bind-remounting '%s' failed" % self.dest)
157
158 self.mounted = True
159 if os.path.islink(self.orig_src):
160 dest = os.path.join(self.root, self.orig_src.lstrip('/'))
161 if not os.path.exists(dest):
162 os.symlink(self.src, dest)
163
164 def unmount(self):
165 if self.has_chroot_instance():
166 return
167
168 if self.ismounted():
169 runner.show([self.umountcmd, "-l", self.dest])
170 self.mounted = False
171
172class LoopbackMount:
173 """LoopbackMount compatibility layer for old API"""
174 def __init__(self, lofile, mountdir, fstype = None):
175 self.diskmount = DiskMount(LoopbackDisk(lofile,size = 0),mountdir,fstype,rmmountdir = True)
176 self.losetup = False
177 self.losetupcmd = find_binary_path("losetup")
178
179 def cleanup(self):
180 self.diskmount.cleanup()
181
182 def unmount(self):
183 self.diskmount.unmount()
184
185 def lounsetup(self):
186 if self.losetup:
187 runner.show([self.losetupcmd, "-d", self.loopdev])
188 self.losetup = False
189 self.loopdev = None
190
191 def loopsetup(self):
192 if self.losetup:
193 return
194
195 self.loopdev = get_loop_device(self.losetupcmd, self.lofile)
196 self.losetup = True
197
198 def mount(self):
199 self.diskmount.mount()
200
201class SparseLoopbackMount(LoopbackMount):
202 """SparseLoopbackMount compatibility layer for old API"""
203 def __init__(self, lofile, mountdir, size, fstype = None):
204 self.diskmount = DiskMount(SparseLoopbackDisk(lofile,size),mountdir,fstype,rmmountdir = True)
205
206 def expand(self, create = False, size = None):
207 self.diskmount.disk.expand(create, size)
208
209 def truncate(self, size = None):
210 self.diskmount.disk.truncate(size)
211
212 def create(self):
213 self.diskmount.disk.create()
214
215class SparseExtLoopbackMount(SparseLoopbackMount):
216 """SparseExtLoopbackMount compatibility layer for old API"""
217 def __init__(self, lofile, mountdir, size, fstype, blocksize, fslabel):
218 self.diskmount = ExtDiskMount(SparseLoopbackDisk(lofile,size), mountdir, fstype, blocksize, fslabel, rmmountdir = True)
219
220
221 def __format_filesystem(self):
222 self.diskmount.__format_filesystem()
223
224 def create(self):
225 self.diskmount.disk.create()
226
227 def resize(self, size = None):
228 return self.diskmount.__resize_filesystem(size)
229
230 def mount(self):
231 self.diskmount.mount()
232
233 def __fsck(self):
234 self.extdiskmount.__fsck()
235
236 def __get_size_from_filesystem(self):
237 return self.diskmount.__get_size_from_filesystem()
238
239 def __resize_to_minimal(self):
240 return self.diskmount.__resize_to_minimal()
241
242 def resparse(self, size = None):
243 return self.diskmount.resparse(size)
244
245class Disk: 62class Disk:
246 """Generic base object for a disk 63 """Generic base object for a disk
247 64
@@ -270,20 +87,6 @@ class Disk:
270 size = property(get_size) 87 size = property(get_size)
271 88
272 89
273class RawDisk(Disk):
274 """A Disk backed by a block device.
275 Note that create() is a no-op.
276 """
277 def __init__(self, size, device):
278 Disk.__init__(self, size, device)
279
280 def fixed(self):
281 return True
282
283 def exists(self):
284 return True
285
286
287class DiskImage(Disk): 90class DiskImage(Disk):
288 """ 91 """
289 A Disk backed by a file. 92 A Disk backed by a file.
@@ -311,76 +114,6 @@ class DiskImage(Disk):
311 self.device = self.image_file 114 self.device = self.image_file
312 115
313 116
314class LoopbackDisk(Disk):
315 """A Disk backed by a file via the loop module."""
316 def __init__(self, lofile, size):
317 Disk.__init__(self, size)
318 self.lofile = lofile
319 self.losetupcmd = find_binary_path("losetup")
320
321 def fixed(self):
322 return False
323
324 def exists(self):
325 return os.path.exists(self.lofile)
326
327 def create(self):
328 if self.device is not None:
329 return
330
331 self.device = get_loop_device(self.losetupcmd, self.lofile)
332
333 def cleanup(self):
334 if self.device is None:
335 return
336 msger.debug("Losetup remove %s" % self.device)
337 rc = runner.show([self.losetupcmd, "-d", self.device])
338 self.device = None
339
340class SparseLoopbackDisk(LoopbackDisk):
341 """A Disk backed by a sparse file via the loop module."""
342 def __init__(self, lofile, size):
343 LoopbackDisk.__init__(self, lofile, size)
344
345 def expand(self, create = False, size = None):
346 flags = os.O_WRONLY
347 if create:
348 flags |= os.O_CREAT
349 if not os.path.exists(self.lofile):
350 makedirs(os.path.dirname(self.lofile))
351
352 if size is None:
353 size = self.size
354
355 msger.debug("Extending sparse file %s to %d" % (self.lofile, size))
356 if create:
357 fd = os.open(self.lofile, flags, 0644)
358 else:
359 fd = os.open(self.lofile, flags)
360
361 if size <= 0:
362 size = 1
363 try:
364 os.ftruncate(fd, size)
365 except:
366 # may be limited by 2G in 32bit env
367 os.ftruncate(fd, 2**31L)
368
369 os.close(fd)
370
371 def truncate(self, size = None):
372 if size is None:
373 size = self.size
374
375 msger.debug("Truncating sparse file %s to %d" % (self.lofile, size))
376 fd = os.open(self.lofile, os.O_WRONLY)
377 os.ftruncate(fd, size)
378 os.close(fd)
379
380 def create(self):
381 self.expand(create = True)
382 LoopbackDisk.create(self)
383
384class Mount: 117class Mount:
385 """A generic base class to deal with mounting things.""" 118 """A generic base class to deal with mounting things."""
386 def __init__(self, mountdir): 119 def __init__(self, mountdir):
@@ -395,666 +128,3 @@ class Mount:
395 def unmount(self): 128 def unmount(self):
396 pass 129 pass
397 130
398class DiskMount(Mount):
399 """A Mount object that handles mounting of a Disk."""
400 def __init__(self, disk, mountdir, fstype = None, rmmountdir = True):
401 Mount.__init__(self, mountdir)
402
403 self.disk = disk
404 self.fstype = fstype
405 self.rmmountdir = rmmountdir
406
407 self.mounted = False
408 self.rmdir = False
409 if fstype:
410 self.mkfscmd = find_binary_path("mkfs." + self.fstype)
411 else:
412 self.mkfscmd = None
413 self.mountcmd = find_binary_path("mount")
414 self.umountcmd = find_binary_path("umount")
415
416 def cleanup(self):
417 Mount.cleanup(self)
418 self.disk.cleanup()
419
420 def unmount(self):
421 if self.mounted:
422 msger.debug("Unmounting directory %s" % self.mountdir)
423 runner.quiet('sync') # sync the data on this mount point
424 rc = runner.show([self.umountcmd, "-l", self.mountdir])
425 if rc == 0:
426 self.mounted = False
427 else:
428 raise MountError("Failed to umount %s" % self.mountdir)
429 if self.rmdir and not self.mounted:
430 try:
431 os.rmdir(self.mountdir)
432 except OSError, e:
433 pass
434 self.rmdir = False
435
436
437 def __create(self):
438 self.disk.create()
439
440
441 def mount(self, options = None):
442 if self.mounted:
443 return
444
445 if not os.path.isdir(self.mountdir):
446 msger.debug("Creating mount point %s" % self.mountdir)
447 os.makedirs(self.mountdir)
448 self.rmdir = self.rmmountdir
449
450 self.__create()
451
452 msger.debug("Mounting %s at %s" % (self.disk.device, self.mountdir))
453 if options:
454 args = [ self.mountcmd, "-o", options, self.disk.device, self.mountdir ]
455 else:
456 args = [ self.mountcmd, self.disk.device, self.mountdir ]
457 if self.fstype:
458 args.extend(["-t", self.fstype])
459
460 rc = runner.show(args)
461 if rc != 0:
462 raise MountError("Failed to mount '%s' to '%s' with command '%s'. Retval: %s" %
463 (self.disk.device, self.mountdir, " ".join(args), rc))
464
465 self.mounted = True
466
467class ExtDiskMount(DiskMount):
468 """A DiskMount object that is able to format/resize ext[23] filesystems."""
469 def __init__(self, disk, mountdir, fstype, blocksize, fslabel, rmmountdir=True, skipformat = False, fsopts = None):
470 DiskMount.__init__(self, disk, mountdir, fstype, rmmountdir)
471 self.blocksize = blocksize
472 self.fslabel = fslabel.replace("/", "")
473 self.uuid = str(uuid.uuid4())
474 self.skipformat = skipformat
475 self.fsopts = fsopts
476 self.extopts = None
477 self.dumpe2fs = find_binary_path("dumpe2fs")
478 self.tune2fs = find_binary_path("tune2fs")
479
480 def __parse_field(self, output, field):
481 for line in output.split("\n"):
482 if line.startswith(field + ":"):
483 return line[len(field) + 1:].strip()
484
485 raise KeyError("Failed to find field '%s' in output" % field)
486
487 def __format_filesystem(self):
488 if self.skipformat:
489 msger.debug("Skip filesystem format.")
490 return
491
492 msger.verbose("Formating %s filesystem on %s" % (self.fstype, self.disk.device))
493 cmdlist = [self.mkfscmd, "-F", "-L", self.fslabel, "-m", "1", "-b",
494 str(self.blocksize), "-U", self.uuid]
495 if self.extopts:
496 cmdlist.extend(self.extopts.split())
497 cmdlist.extend([self.disk.device])
498
499 rc, errout = runner.runtool(cmdlist, catch=2)
500 if rc != 0:
501 raise MountError("Error creating %s filesystem on disk %s:\n%s" %
502 (self.fstype, self.disk.device, errout))
503
504 if not self.extopts:
505 msger.debug("Tuning filesystem on %s" % self.disk.device)
506 runner.show([self.tune2fs, "-c0", "-i0", "-Odir_index", "-ouser_xattr,acl", self.disk.device])
507
508 def __resize_filesystem(self, size = None):
509 current_size = os.stat(self.disk.lofile)[stat.ST_SIZE]
510
511 if size is None:
512 size = self.disk.size
513
514 if size == current_size:
515 return
516
517 if size > current_size:
518 self.disk.expand(size)
519
520 self.__fsck()
521
522 resize2fs(self.disk.lofile, size)
523 return size
524
525 def __create(self):
526 resize = False
527 if not self.disk.fixed() and self.disk.exists():
528 resize = True
529
530 self.disk.create()
531
532 if resize:
533 self.__resize_filesystem()
534 else:
535 self.__format_filesystem()
536
537 def mount(self, options = None):
538 self.__create()
539 DiskMount.mount(self, options)
540
541 def __fsck(self):
542 msger.info("Checking filesystem %s" % self.disk.lofile)
543 runner.quiet(["/sbin/e2fsck", "-f", "-y", self.disk.lofile])
544
545 def __get_size_from_filesystem(self):
546 return int(self.__parse_field(runner.outs([self.dumpe2fs, '-h', self.disk.lofile]),
547 "Block count")) * self.blocksize
548
549 def __resize_to_minimal(self):
550 self.__fsck()
551
552 #
553 # Use a binary search to find the minimal size
554 # we can resize the image to
555 #
556 bot = 0
557 top = self.__get_size_from_filesystem()
558 while top != (bot + 1):
559 t = bot + ((top - bot) / 2)
560
561 if not resize2fs(self.disk.lofile, t):
562 top = t
563 else:
564 bot = t
565 return top
566
567 def resparse(self, size = None):
568 self.cleanup()
569 if size == 0:
570 minsize = 0
571 else:
572 minsize = self.__resize_to_minimal()
573 self.disk.truncate(minsize)
574
575 self.__resize_filesystem(size)
576 return minsize
577
578class VfatDiskMount(DiskMount):
579 """A DiskMount object that is able to format vfat/msdos filesystems."""
580 def __init__(self, disk, mountdir, fstype, blocksize, fslabel, rmmountdir=True, skipformat = False, fsopts = None):
581 DiskMount.__init__(self, disk, mountdir, fstype, rmmountdir)
582 self.blocksize = blocksize
583 self.fslabel = fslabel.replace("/", "")
584 rand1 = random.randint(0, 2**16 - 1)
585 rand2 = random.randint(0, 2**16 - 1)
586 self.uuid = "%04X-%04X" % (rand1, rand2)
587 self.skipformat = skipformat
588 self.fsopts = fsopts
589 self.fsckcmd = find_binary_path("fsck." + self.fstype)
590
591 def __format_filesystem(self):
592 if self.skipformat:
593 msger.debug("Skip filesystem format.")
594 return
595
596 msger.verbose("Formating %s filesystem on %s" % (self.fstype, self.disk.device))
597 rc = runner.show([self.mkfscmd, "-n", self.fslabel,
598 "-i", self.uuid.replace("-", ""), self.disk.device])
599 if rc != 0:
600 raise MountError("Error creating %s filesystem on disk %s" % (self.fstype,self.disk.device))
601
602 msger.verbose("Tuning filesystem on %s" % self.disk.device)
603
604 def __resize_filesystem(self, size = None):
605 current_size = os.stat(self.disk.lofile)[stat.ST_SIZE]
606
607 if size is None:
608 size = self.disk.size
609
610 if size == current_size:
611 return
612
613 if size > current_size:
614 self.disk.expand(size)
615
616 self.__fsck()
617
618 #resize2fs(self.disk.lofile, size)
619 return size
620
621 def __create(self):
622 resize = False
623 if not self.disk.fixed() and self.disk.exists():
624 resize = True
625
626 self.disk.create()
627
628 if resize:
629 self.__resize_filesystem()
630 else:
631 self.__format_filesystem()
632
633 def mount(self, options = None):
634 self.__create()
635 DiskMount.mount(self, options)
636
637 def __fsck(self):
638 msger.debug("Checking filesystem %s" % self.disk.lofile)
639 runner.show([self.fsckcmd, "-y", self.disk.lofile])
640
641 def __get_size_from_filesystem(self):
642 return self.disk.size
643
644 def __resize_to_minimal(self):
645 self.__fsck()
646
647 #
648 # Use a binary search to find the minimal size
649 # we can resize the image to
650 #
651 bot = 0
652 top = self.__get_size_from_filesystem()
653 return top
654
655 def resparse(self, size = None):
656 self.cleanup()
657 minsize = self.__resize_to_minimal()
658 self.disk.truncate(minsize)
659 self.__resize_filesystem(size)
660 return minsize
661
662class BtrfsDiskMount(DiskMount):
663 """A DiskMount object that is able to format/resize btrfs filesystems."""
664 def __init__(self, disk, mountdir, fstype, blocksize, fslabel, rmmountdir=True, skipformat = False, fsopts = None):
665 self.__check_btrfs()
666 DiskMount.__init__(self, disk, mountdir, fstype, rmmountdir)
667 self.blocksize = blocksize
668 self.fslabel = fslabel.replace("/", "")
669 self.uuid = None
670 self.skipformat = skipformat
671 self.fsopts = fsopts
672 self.blkidcmd = find_binary_path("blkid")
673 self.btrfsckcmd = find_binary_path("btrfsck")
674
675 def __check_btrfs(self):
676 found = False
677 """ Need to load btrfs module to mount it """
678 load_module("btrfs")
679 for line in open("/proc/filesystems").xreadlines():
680 if line.find("btrfs") > -1:
681 found = True
682 break
683 if not found:
684 raise MountError("Your system can't mount btrfs filesystem, please make sure your kernel has btrfs support and the module btrfs.ko has been loaded.")
685
686 # disable selinux, selinux will block write
687 if os.path.exists("/usr/sbin/setenforce"):
688 runner.show(["/usr/sbin/setenforce", "0"])
689
690 def __parse_field(self, output, field):
691 for line in output.split(" "):
692 if line.startswith(field + "="):
693 return line[len(field) + 1:].strip().replace("\"", "")
694
695 raise KeyError("Failed to find field '%s' in output" % field)
696
697 def __format_filesystem(self):
698 if self.skipformat:
699 msger.debug("Skip filesystem format.")
700 return
701
702 msger.verbose("Formating %s filesystem on %s" % (self.fstype, self.disk.device))
703 rc = runner.show([self.mkfscmd, "-L", self.fslabel, self.disk.device])
704 if rc != 0:
705 raise MountError("Error creating %s filesystem on disk %s" % (self.fstype,self.disk.device))
706
707 self.uuid = self.__parse_field(runner.outs([self.blkidcmd, self.disk.device]), "UUID")
708
709 def __resize_filesystem(self, size = None):
710 current_size = os.stat(self.disk.lofile)[stat.ST_SIZE]
711
712 if size is None:
713 size = self.disk.size
714
715 if size == current_size:
716 return
717
718 if size > current_size:
719 self.disk.expand(size)
720
721 self.__fsck()
722 return size
723
724 def __create(self):
725 resize = False
726 if not self.disk.fixed() and self.disk.exists():
727 resize = True
728
729 self.disk.create()
730
731 if resize:
732 self.__resize_filesystem()
733 else:
734 self.__format_filesystem()
735
736 def mount(self, options = None):
737 self.__create()
738 DiskMount.mount(self, options)
739
740 def __fsck(self):
741 msger.debug("Checking filesystem %s" % self.disk.lofile)
742 runner.quiet([self.btrfsckcmd, self.disk.lofile])
743
744 def __get_size_from_filesystem(self):
745 return self.disk.size
746
747 def __resize_to_minimal(self):
748 self.__fsck()
749
750 return self.__get_size_from_filesystem()
751
752 def resparse(self, size = None):
753 self.cleanup()
754 minsize = self.__resize_to_minimal()
755 self.disk.truncate(minsize)
756 self.__resize_filesystem(size)
757 return minsize
758
759class DeviceMapperSnapshot(object):
760 def __init__(self, imgloop, cowloop):
761 self.imgloop = imgloop
762 self.cowloop = cowloop
763
764 self.__created = False
765 self.__name = None
766 self.dmsetupcmd = find_binary_path("dmsetup")
767
768 """Load dm_snapshot if it isn't loaded"""
769 load_module("dm_snapshot")
770
771 def get_path(self):
772 if self.__name is None:
773 return None
774 return os.path.join("/dev/mapper", self.__name)
775 path = property(get_path)
776
777 def create(self):
778 if self.__created:
779 return
780
781 self.imgloop.create()
782 self.cowloop.create()
783
784 self.__name = "imgcreate-%d-%d" % (os.getpid(),
785 random.randint(0, 2**16))
786
787 size = os.stat(self.imgloop.lofile)[stat.ST_SIZE]
788
789 table = "0 %d snapshot %s %s p 8" % (size / 512,
790 self.imgloop.device,
791 self.cowloop.device)
792
793 args = [self.dmsetupcmd, "create", self.__name, "--table", table]
794 if runner.show(args) != 0:
795 self.cowloop.cleanup()
796 self.imgloop.cleanup()
797 raise SnapshotError("Could not create snapshot device using: " + ' '.join(args))
798
799 self.__created = True
800
801 def remove(self, ignore_errors = False):
802 if not self.__created:
803 return
804
805 time.sleep(2)
806 rc = runner.show([self.dmsetupcmd, "remove", self.__name])
807 if not ignore_errors and rc != 0:
808 raise SnapshotError("Could not remove snapshot device")
809
810 self.__name = None
811 self.__created = False
812
813 self.cowloop.cleanup()
814 self.imgloop.cleanup()
815
816 def get_cow_used(self):
817 if not self.__created:
818 return 0
819
820 #
821 # dmsetup status on a snapshot returns e.g.
822 # "0 8388608 snapshot 416/1048576"
823 # or, more generally:
824 # "A B snapshot C/D"
825 # where C is the number of 512 byte sectors in use
826 #
827 out = runner.outs([self.dmsetupcmd, "status", self.__name])
828 try:
829 return int((out.split()[3]).split('/')[0]) * 512
830 except ValueError:
831 raise SnapshotError("Failed to parse dmsetup status: " + out)
832
833def create_image_minimizer(path, image, minimal_size):
834 """
835 Builds a copy-on-write image which can be used to
836 create a device-mapper snapshot of an image where
837 the image's filesystem is as small as possible
838
839 The steps taken are:
840 1) Create a sparse COW
841 2) Loopback mount the image and the COW
842 3) Create a device-mapper snapshot of the image
843 using the COW
844 4) Resize the filesystem to the minimal size
845 5) Determine the amount of space used in the COW
846 6) Restroy the device-mapper snapshot
847 7) Truncate the COW, removing unused space
848 8) Create a squashfs of the COW
849 """
850 imgloop = LoopbackDisk(image, None) # Passing bogus size - doesn't matter
851
852 cowloop = SparseLoopbackDisk(os.path.join(os.path.dirname(path), "osmin"),
853 64L * 1024L * 1024L)
854
855 snapshot = DeviceMapperSnapshot(imgloop, cowloop)
856
857 try:
858 snapshot.create()
859
860 resize2fs(snapshot.path, minimal_size)
861
862 cow_used = snapshot.get_cow_used()
863 finally:
864 snapshot.remove(ignore_errors = (not sys.exc_info()[0] is None))
865
866 cowloop.truncate(cow_used)
867
868 mksquashfs(cowloop.lofile, path)
869
870 os.unlink(cowloop.lofile)
871
872def load_module(module):
873 found = False
874 for line in open('/proc/modules').xreadlines():
875 if line.startswith("%s " % module):
876 found = True
877 break
878 if not found:
879 msger.info("Loading %s..." % module)
880 runner.quiet(['modprobe', module])
881
882class LoopDevice(object):
883 def __init__(self, loopid=None):
884 self.device = None
885 self.loopid = loopid
886 self.created = False
887 self.kpartxcmd = find_binary_path("kpartx")
888 self.losetupcmd = find_binary_path("losetup")
889
890 def register(self, device):
891 self.device = device
892 self.loopid = None
893 self.created = True
894
895 def reg_atexit(self):
896 import atexit
897 atexit.register(self.close)
898
899 def _genloopid(self):
900 import glob
901 if not glob.glob("/dev/loop[0-9]*"):
902 return 10
903
904 fint = lambda x: x[9:].isdigit() and int(x[9:]) or 0
905 maxid = 1 + max(filter(lambda x: x<100,
906 map(fint, glob.glob("/dev/loop[0-9]*"))))
907 if maxid < 10: maxid = 10
908 if maxid >= 100: raise
909 return maxid
910
911 def _kpseek(self, device):
912 rc, out = runner.runtool([self.kpartxcmd, '-l', '-v', device])
913 if rc != 0:
914 raise MountError("Can't query dm snapshot on %s" % device)
915 for line in out.splitlines():
916 if line and line.startswith("loop"):
917 return True
918 return False
919
920 def _loseek(self, device):
921 import re
922 rc, out = runner.runtool([self.losetupcmd, '-a'])
923 if rc != 0:
924 raise MountError("Failed to run 'losetup -a'")
925 for line in out.splitlines():
926 m = re.match("([^:]+): .*", line)
927 if m and m.group(1) == device:
928 return True
929 return False
930
931 def create(self):
932 if not self.created:
933 if not self.loopid:
934 self.loopid = self._genloopid()
935 self.device = "/dev/loop%d" % self.loopid
936 if os.path.exists(self.device):
937 if self._loseek(self.device):
938 raise MountError("Device busy: %s" % self.device)
939 else:
940 self.created = True
941 return
942
943 mknod = find_binary_path('mknod')
944 rc = runner.show([mknod, '-m664', self.device, 'b', '7', str(self.loopid)])
945 if rc != 0:
946 raise MountError("Failed to create device %s" % self.device)
947 else:
948 self.created = True
949
950 def close(self):
951 if self.created:
952 try:
953 self.cleanup()
954 self.device = None
955 except MountError, e:
956 msger.error("%s" % e)
957
958 def cleanup(self):
959
960 if self.device is None:
961 return
962
963
964 if self._kpseek(self.device):
965 if self.created:
966 for i in range(3, os.sysconf("SC_OPEN_MAX")):
967 try:
968 os.close(i)
969 except:
970 pass
971 runner.quiet([self.kpartxcmd, "-d", self.device])
972 if self._loseek(self.device):
973 runner.quiet([self.losetupcmd, "-d", self.device])
974 # FIXME: should sleep a while between two loseek
975 if self._loseek(self.device):
976 msger.warning("Can't cleanup loop device %s" % self.device)
977 elif self.loopid:
978 os.unlink(self.device)
979
980DEVICE_PIDFILE_DIR = "/var/tmp/mic/device"
981DEVICE_LOCKFILE = "/var/lock/__mic_loopdev.lock"
982
983def get_loop_device(losetupcmd, lofile):
984 global DEVICE_PIDFILE_DIR
985 global DEVICE_LOCKFILE
986
987 import fcntl
988 makedirs(os.path.dirname(DEVICE_LOCKFILE))
989 fp = open(DEVICE_LOCKFILE, 'w')
990 fcntl.flock(fp, fcntl.LOCK_EX)
991 try:
992 loopdev = None
993 devinst = LoopDevice()
994
995 # clean up left loop device first
996 clean_loop_devices()
997
998 # provide an avaible loop device
999 rc, out = runner.runtool([losetupcmd, "--find"])
1000 if rc == 0:
1001 loopdev = out.split()[0]
1002 devinst.register(loopdev)
1003 if not loopdev or not os.path.exists(loopdev):
1004 devinst.create()
1005 loopdev = devinst.device
1006
1007 # setup a loop device for image file
1008 rc = runner.show([losetupcmd, loopdev, lofile])
1009 if rc != 0:
1010 raise MountError("Failed to setup loop device for '%s'" % lofile)
1011
1012 devinst.reg_atexit()
1013
1014 # try to save device and pid
1015 makedirs(DEVICE_PIDFILE_DIR)
1016 pidfile = os.path.join(DEVICE_PIDFILE_DIR, os.path.basename(loopdev))
1017 if os.path.exists(pidfile):
1018 os.unlink(pidfile)
1019 with open(pidfile, 'w') as wf:
1020 wf.write(str(os.getpid()))
1021
1022 except MountError, err:
1023 raise CreatorError("%s" % str(err))
1024 except:
1025 raise
1026 finally:
1027 try:
1028 fcntl.flock(fp, fcntl.LOCK_UN)
1029 fp.close()
1030 os.unlink(DEVICE_LOCKFILE)
1031 except:
1032 pass
1033
1034 return loopdev
1035
1036def clean_loop_devices(piddir=DEVICE_PIDFILE_DIR):
1037 if not os.path.exists(piddir) or not os.path.isdir(piddir):
1038 return
1039
1040 for loopdev in os.listdir(piddir):
1041 pidfile = os.path.join(piddir, loopdev)
1042 try:
1043 with open(pidfile, 'r') as rf:
1044 devpid = int(rf.read())
1045 except:
1046 devpid = None
1047
1048 # if the process using this device is alive, skip it
1049 if not devpid or os.path.exists(os.path.join('/proc', str(devpid))):
1050 continue
1051
1052 # try to clean it up
1053 try:
1054 devinst = LoopDevice()
1055 devinst.register(os.path.join('/dev', loopdev))
1056 devinst.cleanup()
1057 os.unlink(pidfile)
1058 except:
1059 pass
1060