9 from pbs import dmsetup
10 from pbs import blockdev
12 from pbs import kpartx
14 from pbs import umount
18 def __init__(self, source):
19 self._cleanup_jobs = []
23 def _add_cleanup(self, job, *args):
24 self._cleanup_jobs.append((job, args))
26 def _losetup(self, fname):
27 loop = losetup.find_unused_loop_device()
29 self._add_cleanup(loop.unmount)
32 def _dir_to_disk(self):
33 raise NotImplementedError
36 while len(self._cleanup_jobs):
37 job, args = self._cleanup_jobs.pop()
41 sourcedev = self.source
42 mode = os.stat(self.source).st_mode
43 if stat.S_ISDIR(mode):
44 return self._losetup(self._dir_to_disk())
45 elif stat.S_ISREG(mode):
46 sourcedev = self._losetup(self.source)
47 elif not stat.S_ISBLK(mode):
48 raise ValueError("Value for self.source is invalid")
50 # Take a snapshot and return it to the user
51 size = blockdev('--getsize', sourcedev)
52 cowfd, cow = tempfile.mkstemp()
53 self._add_cleanup(os.unlink, cow)
55 dd('if=/dev/null', 'of=%s' % cow, 'bs=1k' ,'seek=%d' % (1024*1024))
56 cowdev = self._losetup(cow)
58 snapshot = uuid.uuid4().hex
59 tablefd, table = tempfile.mkstemp()
61 os.write(tablefd, "0 %d snapshot %s %s n 8" % \
62 (int(size), sourcedev, cowdev))
63 dmsetup('create', snapshot, table)
64 self._add_cleanup(dmsetup, 'remove', snapshot)
68 new_device = DiskDevice(self, "/dev/mapper/%s" % snapshot)
69 self._devices.append(new_device)
72 class DiskDevice(object):
74 def __init__(self, disk, device, bootable = True):
77 self.is_bootable = bootable
78 self.partitions_mapped = False
79 self.magic_number = uuid.uuid4().hex
81 def list_partitions(self):
82 if not self.partitions_mapped:
83 kpartx("-a", "-p", self.magic_number, self.dev)
84 self.disk._cleanup_jobs.append(kpartx, "-d", "-p",
85 self.magic_number, self.dev)
86 self.partitions_mapped = True
88 output = kpartx("-l", "-p", self.magic_number, self.dev)
89 return [ "/dev/mapper/%s" % x for x in
90 re.findall('^\S+', str(output), flags=re.MULTILINE)]
92 def mount(self, partition):
93 if not self.partitions_mapped:
94 self.list_partitions()
95 kpartx("-a", "-p", self.magic_number, self.dev)
96 self.disk._cleanup_jobs.append(kpartx, "-d", "-p",
97 self.magic_number, self.dev)
98 self.partitions_mapped = True
100 targetfd, target = tempfile.mkdtemp()
102 mount(dev, partition)
108 def unmount(self, partition):
111 mode = os.stat(self.source).st_mode
112 if stat.S_ISDIR(mode):
115 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :