Statistics
| Branch: | Tag: | Revision:

root / image_creator / bundle_volume.py @ e6f134b3

History | View | Annotate | Download (13.4 kB)

1 9e4b4de2 Nikos Skalkotos
# Copyright 2012 GRNET S.A. All rights reserved.
2 9e4b4de2 Nikos Skalkotos
#
3 9e4b4de2 Nikos Skalkotos
# Redistribution and use in source and binary forms, with or
4 9e4b4de2 Nikos Skalkotos
# without modification, are permitted provided that the following
5 9e4b4de2 Nikos Skalkotos
# conditions are met:
6 9e4b4de2 Nikos Skalkotos
#
7 9e4b4de2 Nikos Skalkotos
#   1. Redistributions of source code must retain the above
8 9e4b4de2 Nikos Skalkotos
#      copyright notice, this list of conditions and the following
9 9e4b4de2 Nikos Skalkotos
#      disclaimer.
10 9e4b4de2 Nikos Skalkotos
#
11 9e4b4de2 Nikos Skalkotos
#   2. Redistributions in binary form must reproduce the above
12 9e4b4de2 Nikos Skalkotos
#      copyright notice, this list of conditions and the following
13 9e4b4de2 Nikos Skalkotos
#      disclaimer in the documentation and/or other materials
14 9e4b4de2 Nikos Skalkotos
#      provided with the distribution.
15 9e4b4de2 Nikos Skalkotos
#
16 9e4b4de2 Nikos Skalkotos
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 9e4b4de2 Nikos Skalkotos
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 9e4b4de2 Nikos Skalkotos
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 9e4b4de2 Nikos Skalkotos
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 9e4b4de2 Nikos Skalkotos
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 9e4b4de2 Nikos Skalkotos
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 9e4b4de2 Nikos Skalkotos
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 9e4b4de2 Nikos Skalkotos
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 9e4b4de2 Nikos Skalkotos
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 9e4b4de2 Nikos Skalkotos
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 9e4b4de2 Nikos Skalkotos
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 9e4b4de2 Nikos Skalkotos
# POSSIBILITY OF SUCH DAMAGE.
28 9e4b4de2 Nikos Skalkotos
#
29 9e4b4de2 Nikos Skalkotos
# The views and conclusions contained in the software and
30 9e4b4de2 Nikos Skalkotos
# documentation are those of the authors and should not be
31 9e4b4de2 Nikos Skalkotos
# interpreted as representing official policies, either expressed
32 9e4b4de2 Nikos Skalkotos
# or implied, of GRNET S.A.
33 9e4b4de2 Nikos Skalkotos
34 27a4229d Nikos Skalkotos
import os
35 27a4229d Nikos Skalkotos
import re
36 27a4229d Nikos Skalkotos
import uuid
37 8eea5572 Nikos Skalkotos
import tempfile
38 25b4d858 Nikos Skalkotos
import time
39 9517bf29 Nikos Skalkotos
from collections import namedtuple
40 27a4229d Nikos Skalkotos
41 8eea5572 Nikos Skalkotos
import parted
42 8eea5572 Nikos Skalkotos
43 74149d07 Nikos Skalkotos
from image_creator.rsync import Rsync
44 27a4229d Nikos Skalkotos
from image_creator.util import get_command
45 9e4b4de2 Nikos Skalkotos
from image_creator.util import FatalError
46 9e4b4de2 Nikos Skalkotos
47 27a4229d Nikos Skalkotos
findfs = get_command('findfs')
48 27a4229d Nikos Skalkotos
truncate = get_command('truncate')
49 27a4229d Nikos Skalkotos
dd = get_command('dd')
50 8eea5572 Nikos Skalkotos
dmsetup = get_command('dmsetup')
51 25b4d858 Nikos Skalkotos
losetup = get_command('losetup')
52 25b4d858 Nikos Skalkotos
mount = get_command('mount')
53 25b4d858 Nikos Skalkotos
umount = get_command('umount')
54 25b4d858 Nikos Skalkotos
55 25b4d858 Nikos Skalkotos
MKFS_OPTS = {
56 25b4d858 Nikos Skalkotos
    'ext2': ['-F'],
57 25b4d858 Nikos Skalkotos
    'ext3': ['-F'],
58 25b4d858 Nikos Skalkotos
    'ext4': ['-F'],
59 25b4d858 Nikos Skalkotos
    'reiserfs': ['-ff'],
60 25b4d858 Nikos Skalkotos
    'btrfs': [],
61 25b4d858 Nikos Skalkotos
    'minix': [],
62 25b4d858 Nikos Skalkotos
    'xfs': ['-f'],
63 25b4d858 Nikos Skalkotos
    'jfs': ['-f'],
64 25b4d858 Nikos Skalkotos
    'ntfs': ['-F'],
65 25b4d858 Nikos Skalkotos
    'msdos': [],
66 25b4d858 Nikos Skalkotos
    'vfat': []
67 25b4d858 Nikos Skalkotos
    }
68 9517bf29 Nikos Skalkotos
69 9517bf29 Nikos Skalkotos
70 8eea5572 Nikos Skalkotos
class BundleVolume():
71 27a4229d Nikos Skalkotos
72 8eea5572 Nikos Skalkotos
    def __init__(self, out, meta):
73 8eea5572 Nikos Skalkotos
        self.out = out
74 8eea5572 Nikos Skalkotos
        self.meta = meta
75 27a4229d Nikos Skalkotos
76 a939e3b8 Nikos Skalkotos
        self.out.output('Searching for root device ...', False)
77 8eea5572 Nikos Skalkotos
        root = self._get_root_partition()
78 27a4229d Nikos Skalkotos
79 8eea5572 Nikos Skalkotos
        if root.startswith("UUID=") or root.startswith("LABEL="):
80 25b4d858 Nikos Skalkotos
            root = findfs(root).stdout.strip()
81 27a4229d Nikos Skalkotos
82 25b4d858 Nikos Skalkotos
        if not re.match('/dev/[hsv]d[a-z][1-9]*$', root):
83 25b4d858 Nikos Skalkotos
            raise FatalError("Don't know how to handle root device: %s" % root)
84 9517bf29 Nikos Skalkotos
85 25b4d858 Nikos Skalkotos
        out.success(root)
86 9517bf29 Nikos Skalkotos
87 25b4d858 Nikos Skalkotos
        disk_file = re.split('[0-9]', root)[0]
88 25b4d858 Nikos Skalkotos
        device = parted.Device(disk_file)
89 25b4d858 Nikos Skalkotos
        self.disk = parted.Disk(device)
90 9517bf29 Nikos Skalkotos
91 a939e3b8 Nikos Skalkotos
    def _read_fstable(self, f):
92 25b4d858 Nikos Skalkotos
93 8eea5572 Nikos Skalkotos
        if not os.path.isfile(f):
94 8eea5572 Nikos Skalkotos
            raise FatalError("Unable to open: `%s'. File is missing." % f)
95 27a4229d Nikos Skalkotos
96 74149d07 Nikos Skalkotos
        FileSystemTableEntry = namedtuple('FileSystemTableEntry',
97 25b4d858 Nikos Skalkotos
                                     'dev mpoint fs opts freq passno')
98 8eea5572 Nikos Skalkotos
        with open(f) as table:
99 8eea5572 Nikos Skalkotos
            for line in iter(table):
100 8eea5572 Nikos Skalkotos
                entry = line.split('#')[0].strip().split()
101 8eea5572 Nikos Skalkotos
                if len(entry) != 6:
102 8eea5572 Nikos Skalkotos
                    continue
103 74149d07 Nikos Skalkotos
                yield FileSystemTableEntry(*entry)
104 27a4229d Nikos Skalkotos
105 a939e3b8 Nikos Skalkotos
    def _get_root_partition(self):
106 8eea5572 Nikos Skalkotos
        for entry in self._read_fstable('/etc/fstab'):
107 8eea5572 Nikos Skalkotos
            if entry.mpoint == '/':
108 8eea5572 Nikos Skalkotos
                return entry.dev
109 27a4229d Nikos Skalkotos
110 8eea5572 Nikos Skalkotos
        raise FatalError("Unable to find root device in /etc/fstab")
111 27a4229d Nikos Skalkotos
112 a939e3b8 Nikos Skalkotos
    def _is_mpoint(self, path):
113 a939e3b8 Nikos Skalkotos
        for entry in self._read_fstable('/proc/mounts'):
114 8eea5572 Nikos Skalkotos
            if entry.mpoint == path:
115 8eea5572 Nikos Skalkotos
                return True
116 8eea5572 Nikos Skalkotos
        return False
117 27a4229d Nikos Skalkotos
118 25b4d858 Nikos Skalkotos
    def _get_mount_options(self, device):
119 a939e3b8 Nikos Skalkotos
        for entry in self._read_fstable('/proc/mounts'):
120 8eea5572 Nikos Skalkotos
            if not entry.dev.startswith('/'):
121 8eea5572 Nikos Skalkotos
                continue
122 9517bf29 Nikos Skalkotos
123 8eea5572 Nikos Skalkotos
            if os.path.realpath(entry.dev) == os.path.realpath(device):
124 8eea5572 Nikos Skalkotos
                return entry
125 9517bf29 Nikos Skalkotos
126 25b4d858 Nikos Skalkotos
        return None
127 9517bf29 Nikos Skalkotos
128 25b4d858 Nikos Skalkotos
    def _create_partition_table(self, image):
129 8eea5572 Nikos Skalkotos
130 25b4d858 Nikos Skalkotos
        if self.disk.type != 'msdos':
131 8eea5572 Nikos Skalkotos
            raise FatalError('Only msdos partition tables are supported')
132 8eea5572 Nikos Skalkotos
133 8eea5572 Nikos Skalkotos
        # Copy the MBR and the space between the MBR and the first partition.
134 8eea5572 Nikos Skalkotos
        # In Grub version 1 Stage 1.5 is located there.
135 25b4d858 Nikos Skalkotos
        first_sector = self.disk.getPrimaryPartitions()[0].geometry.start
136 8eea5572 Nikos Skalkotos
137 25b4d858 Nikos Skalkotos
        dd('if=%s' % self.disk.device.path, 'of=%s' % image,
138 25b4d858 Nikos Skalkotos
           'bs=%d' % self.disk.device.sectorSize,
139 8eea5572 Nikos Skalkotos
           'count=%d' % first_sector, 'conv=notrunc')
140 8eea5572 Nikos Skalkotos
141 8eea5572 Nikos Skalkotos
        # Create the Extended boot records (EBRs) in the image
142 25b4d858 Nikos Skalkotos
        extended = self.disk.getExtendedPartition()
143 8eea5572 Nikos Skalkotos
        if not extended:
144 8eea5572 Nikos Skalkotos
            return
145 8eea5572 Nikos Skalkotos
146 8eea5572 Nikos Skalkotos
        # Extended boot records precede the logical partitions they describe
147 25b4d858 Nikos Skalkotos
        logical = self.disk.getLogicalPartitions()
148 8eea5572 Nikos Skalkotos
        start = extended.geometry.start
149 8eea5572 Nikos Skalkotos
        for i in range(len(logical)):
150 8eea5572 Nikos Skalkotos
            end = logical[i].geometry.start - 1
151 25b4d858 Nikos Skalkotos
            dd('if=%s' % self.disk.device.path, 'of=%s' % image,
152 8eea5572 Nikos Skalkotos
               'count=%d' % (end - start + 1), 'conv=notrunc',
153 8eea5572 Nikos Skalkotos
               'seek=%d' % start, 'skip=%d' % start)
154 8eea5572 Nikos Skalkotos
            start = logical[i].geometry.end + 1
155 8eea5572 Nikos Skalkotos
156 25b4d858 Nikos Skalkotos
    def _get_partitions(self, disk):
157 25b4d858 Nikos Skalkotos
        Partition = namedtuple('Partition', 'num start end type fs')
158 8eea5572 Nikos Skalkotos
159 8eea5572 Nikos Skalkotos
        partitions = []
160 25b4d858 Nikos Skalkotos
        for p in disk.partitions:
161 25b4d858 Nikos Skalkotos
            num = p.number
162 25b4d858 Nikos Skalkotos
            start = p.geometry.start
163 25b4d858 Nikos Skalkotos
            end = p.geometry.end
164 25b4d858 Nikos Skalkotos
            ptype = p.type
165 25b4d858 Nikos Skalkotos
            fs = p.fileSystem.type if p.fileSystem is not None else ''
166 25b4d858 Nikos Skalkotos
            partitions.append(Partition(num, start, end, ptype, fs))
167 25b4d858 Nikos Skalkotos
168 25b4d858 Nikos Skalkotos
        return partitions
169 25b4d858 Nikos Skalkotos
170 25b4d858 Nikos Skalkotos
    def _shrink_partitions(self, image):
171 8eea5572 Nikos Skalkotos
172 25b4d858 Nikos Skalkotos
        new_end = self.disk.device.getLength()
173 25b4d858 Nikos Skalkotos
174 25b4d858 Nikos Skalkotos
        image_dev = parted.Device(image)
175 a939e3b8 Nikos Skalkotos
        image_disk = parted.Disk(image_dev)
176 a939e3b8 Nikos Skalkotos
177 a939e3b8 Nikos Skalkotos
        is_extended = lambda p: p.type == parted.PARTITION_EXTENDED
178 a939e3b8 Nikos Skalkotos
        is_logical = lambda p: p.type == parted.PARTITION_LOGICAL
179 a939e3b8 Nikos Skalkotos
180 25b4d858 Nikos Skalkotos
        partitions = self._get_partitions(self.disk)
181 a939e3b8 Nikos Skalkotos
182 a939e3b8 Nikos Skalkotos
        last = partitions[-1]
183 a939e3b8 Nikos Skalkotos
        if last.fs == 'linux-swap(v1)':
184 a939e3b8 Nikos Skalkotos
            MB = 2 ** 20
185 25b4d858 Nikos Skalkotos
            size = (last.end - last.start + 1) * self.disk.device.sectorSize
186 a939e3b8 Nikos Skalkotos
            self.meta['SWAP'] = "%d:%s" % (last.num, (size + MB - 1) // MB)
187 a939e3b8 Nikos Skalkotos
188 a939e3b8 Nikos Skalkotos
            image_disk.deletePartition(
189 a939e3b8 Nikos Skalkotos
                image_disk.getPartitionBySector(last.start))
190 a939e3b8 Nikos Skalkotos
            image_disk.commit()
191 a939e3b8 Nikos Skalkotos
192 a939e3b8 Nikos Skalkotos
            if is_logical(last) and last.num == 5:
193 a939e3b8 Nikos Skalkotos
                extended = image_disk.getExtendedPartition()
194 a939e3b8 Nikos Skalkotos
                image_disk.deletePartition(extended)
195 a939e3b8 Nikos Skalkotos
                image_disk.commit()
196 a939e3b8 Nikos Skalkotos
                partitions.remove(filter(is_extended, partitions)[0])
197 a939e3b8 Nikos Skalkotos
198 a939e3b8 Nikos Skalkotos
            partitions.remove(last)
199 a939e3b8 Nikos Skalkotos
            last = partitions[-1]
200 a939e3b8 Nikos Skalkotos
201 a939e3b8 Nikos Skalkotos
            # Leave 2048 blocks at the end
202 a939e3b8 Nikos Skalkotos
            new_end = last.end + 2048
203 a939e3b8 Nikos Skalkotos
204 25b4d858 Nikos Skalkotos
        mount_options = self._get_mount_options(
205 25b4d858 Nikos Skalkotos
                self.disk.getPartitionBySector(last.start).path)
206 74149d07 Nikos Skalkotos
        if mount_options is not None:
207 25b4d858 Nikos Skalkotos
            stat = os.statvfs(mount_options.mpoint)
208 25b4d858 Nikos Skalkotos
            # Shrink the last partition. The new size should be the size of the
209 25b4d858 Nikos Skalkotos
            # occupied blocks
210 a939e3b8 Nikos Skalkotos
            blcks = stat.f_blocks - stat.f_bavail
211 25b4d858 Nikos Skalkotos
            new_size = (blcks * stat.f_frsize) // self.disk.device.sectorSize
212 a939e3b8 Nikos Skalkotos
213 a939e3b8 Nikos Skalkotos
            # Add 10% just to be on the safe side
214 a939e3b8 Nikos Skalkotos
            part_end = last.start + (new_size * 11) // 10
215 a939e3b8 Nikos Skalkotos
            # Alighn to 2048
216 a939e3b8 Nikos Skalkotos
            part_end = ((part_end + 2047) // 2048) * 2048
217 a939e3b8 Nikos Skalkotos
218 a939e3b8 Nikos Skalkotos
            image_disk.setPartitionGeometry(
219 a939e3b8 Nikos Skalkotos
                image_disk.getPartitionBySector(last.start),
220 a939e3b8 Nikos Skalkotos
                parted.Constraint(device=image_disk.device),
221 a939e3b8 Nikos Skalkotos
                start=last.start, end=last.end)
222 a939e3b8 Nikos Skalkotos
            image_disk.commit()
223 a939e3b8 Nikos Skalkotos
224 25b4d858 Nikos Skalkotos
            # Parted may have changed this for better alignment
225 25b4d858 Nikos Skalkotos
            part_end = image_disk.getPartitionBySector(last.start).geometry.end
226 25b4d858 Nikos Skalkotos
            last = last._replace(end=part_end)
227 25b4d858 Nikos Skalkotos
            partitions[-1] = last
228 25b4d858 Nikos Skalkotos
229 25b4d858 Nikos Skalkotos
            # Leave 2048 blocks at the end.
230 25b4d858 Nikos Skalkotos
            new_end = new_size + 2048
231 25b4d858 Nikos Skalkotos
232 a939e3b8 Nikos Skalkotos
            if last.type == parted.PARTITION_LOGICAL:
233 a939e3b8 Nikos Skalkotos
                # Fix the extended partition
234 a939e3b8 Nikos Skalkotos
                extended = disk.getExtendedPartition()
235 a939e3b8 Nikos Skalkotos
236 a939e3b8 Nikos Skalkotos
                image_disk.setPartitionGeometry(extended,
237 a939e3b8 Nikos Skalkotos
                    parted.Constraint(device=img_dev),
238 a939e3b8 Nikos Skalkotos
                    ext.geometry.start, end=last.end)
239 a939e3b8 Nikos Skalkotos
                image_disk.commit()
240 8eea5572 Nikos Skalkotos
241 25b4d858 Nikos Skalkotos
        return new_end
242 8eea5572 Nikos Skalkotos
243 25b4d858 Nikos Skalkotos
    def _map_partition(self, dev, num, start, end):
244 25b4d858 Nikos Skalkotos
        name = os.path.basename(dev)
245 25b4d858 Nikos Skalkotos
        tablefd, table = tempfile.mkstemp()
246 25b4d858 Nikos Skalkotos
        try:
247 25b4d858 Nikos Skalkotos
            size = end - start + 1
248 25b4d858 Nikos Skalkotos
            os.write(tablefd, "0 %d linear %s %d" % (size, dev, start))
249 25b4d858 Nikos Skalkotos
            dmsetup('create', "%sp%d" % (name, num), table)
250 25b4d858 Nikos Skalkotos
        finally:
251 25b4d858 Nikos Skalkotos
            os.unlink(table)
252 8eea5572 Nikos Skalkotos
253 25b4d858 Nikos Skalkotos
        return "/dev/mapper/%sp%d" % (name, num)
254 8eea5572 Nikos Skalkotos
255 25b4d858 Nikos Skalkotos
    def _unmap_partition(self, dev):
256 25b4d858 Nikos Skalkotos
        if not os.path.exists(dev):
257 25b4d858 Nikos Skalkotos
            return
258 8eea5572 Nikos Skalkotos
259 25b4d858 Nikos Skalkotos
        dmsetup('remove', dev.split('/dev/mapper/')[1])
260 25b4d858 Nikos Skalkotos
        time.sleep(0.1)
261 8eea5572 Nikos Skalkotos
262 25b4d858 Nikos Skalkotos
    def _mount(self, target, devs):
263 25b4d858 Nikos Skalkotos
264 25b4d858 Nikos Skalkotos
        devs.sort(key=lambda d: d[1])
265 25b4d858 Nikos Skalkotos
        for dev, mpoint in devs:
266 25b4d858 Nikos Skalkotos
            absmpoint = os.path.abspath(target + mpoint)
267 25b4d858 Nikos Skalkotos
            if not os.path.exists(absmpoint):
268 25b4d858 Nikos Skalkotos
                os.makedirs(absmpoint)
269 25b4d858 Nikos Skalkotos
            mount(dev, absmpoint)
270 25b4d858 Nikos Skalkotos
271 25b4d858 Nikos Skalkotos
    def _umount_all(self, target):
272 25b4d858 Nikos Skalkotos
        mpoints = []
273 25b4d858 Nikos Skalkotos
        for entry in self._read_fstable('/proc/mounts'):
274 25b4d858 Nikos Skalkotos
            if entry.mpoint.startswith(os.path.abspath(target)):
275 25b4d858 Nikos Skalkotos
                    mpoints.append(entry.mpoint)
276 74149d07 Nikos Skalkotos
277 25b4d858 Nikos Skalkotos
        mpoints.sort()
278 25b4d858 Nikos Skalkotos
        for mpoint in reversed(mpoints):
279 25b4d858 Nikos Skalkotos
            umount(mpoint)
280 25b4d858 Nikos Skalkotos
281 74149d07 Nikos Skalkotos
    def _to_exclude(self):
282 74149d07 Nikos Skalkotos
        excluded = ['/tmp']
283 74149d07 Nikos Skalkotos
        local_filesystems = MKFS_OPTS.keys() + ['rootfs']
284 74149d07 Nikos Skalkotos
        for entry in self._read_fstable('/proc/mounts'):
285 74149d07 Nikos Skalkotos
            if entry.fs in local_filesystems:
286 74149d07 Nikos Skalkotos
                continue
287 74149d07 Nikos Skalkotos
288 74149d07 Nikos Skalkotos
            mpoint = entry.mpoint
289 74149d07 Nikos Skalkotos
            if mpoint in excluded:
290 74149d07 Nikos Skalkotos
                continue
291 74149d07 Nikos Skalkotos
292 74149d07 Nikos Skalkotos
            descendants = filter(lambda p: p.startswith(mpoint + '/'),
293 74149d07 Nikos Skalkotos
                    excluded)
294 74149d07 Nikos Skalkotos
            if len(descendants):
295 74149d07 Nikos Skalkotos
                for d in descendants:
296 74149d07 Nikos Skalkotos
                    excluded.remove(d)
297 74149d07 Nikos Skalkotos
                excluded.append(mpoint)
298 74149d07 Nikos Skalkotos
                continue
299 74149d07 Nikos Skalkotos
300 74149d07 Nikos Skalkotos
            dirname = mpoint
301 74149d07 Nikos Skalkotos
            basename = ''
302 74149d07 Nikos Skalkotos
            found_ancestor = False
303 74149d07 Nikos Skalkotos
            while dirname != '/':
304 74149d07 Nikos Skalkotos
                (dirname, basename) = os.path.split(dirname)
305 74149d07 Nikos Skalkotos
                if dirname in excluded:
306 74149d07 Nikos Skalkotos
                    found_ancestor = True
307 74149d07 Nikos Skalkotos
                    break
308 74149d07 Nikos Skalkotos
309 74149d07 Nikos Skalkotos
            if not found_ancestor:
310 74149d07 Nikos Skalkotos
                excluded.append(mpoint)
311 74149d07 Nikos Skalkotos
312 74149d07 Nikos Skalkotos
        return map(lambda d: d + "/*", excluded)
313 74149d07 Nikos Skalkotos
314 25b4d858 Nikos Skalkotos
    def _create_filesystems(self, image):
315 74149d07 Nikos Skalkotos
316 25b4d858 Nikos Skalkotos
        partitions = self._get_partitions(parted.Disk(parted.Device(image)))
317 25b4d858 Nikos Skalkotos
        filesystems = {}
318 25b4d858 Nikos Skalkotos
        for p in self.disk.partitions:
319 25b4d858 Nikos Skalkotos
            filesystems[p.number] = self._get_mount_options(p.path)
320 25b4d858 Nikos Skalkotos
321 25b4d858 Nikos Skalkotos
        unmounted = filter(lambda p: filesystems[p.num] is None, partitions)
322 25b4d858 Nikos Skalkotos
        mounted = filter(lambda p: filesystems[p.num] is not None, partitions)
323 25b4d858 Nikos Skalkotos
324 25b4d858 Nikos Skalkotos
        # For partitions that are not mounted right now, we can simply dd them
325 25b4d858 Nikos Skalkotos
        # into the image.
326 25b4d858 Nikos Skalkotos
        for p in unmounted:
327 25b4d858 Nikos Skalkotos
            dd('if=%s' % self.disk.device.path, 'of=%s' % image,
328 25b4d858 Nikos Skalkotos
               'count=%d' % (p.end - p.start + 1), 'conv=notrunc',
329 25b4d858 Nikos Skalkotos
               'seek=%d' % p.start, 'skip=%d' % p.start)
330 25b4d858 Nikos Skalkotos
331 25b4d858 Nikos Skalkotos
        loop = str(losetup('-f', '--show', image)).strip()
332 25b4d858 Nikos Skalkotos
        mapped = {}
333 25b4d858 Nikos Skalkotos
        try:
334 25b4d858 Nikos Skalkotos
            for p in mounted:
335 74149d07 Nikos Skalkotos
                i = p.num
336 25b4d858 Nikos Skalkotos
                mapped[i] = self._map_partition(loop, i, p.start, p.end)
337 25b4d858 Nikos Skalkotos
338 25b4d858 Nikos Skalkotos
            # Create the file systems
339 25b4d858 Nikos Skalkotos
            for i, dev in mapped.iteritems():
340 25b4d858 Nikos Skalkotos
                fs = filesystems[i].fs
341 25b4d858 Nikos Skalkotos
                self.out.output('Creating %s filesystem on partition %d ... ' %
342 25b4d858 Nikos Skalkotos
                    (fs, i), False)
343 25b4d858 Nikos Skalkotos
                get_command('mkfs.%s' % fs)(*(MKFS_OPTS[fs] + [dev]))
344 25b4d858 Nikos Skalkotos
                self.out.success('done')
345 25b4d858 Nikos Skalkotos
346 25b4d858 Nikos Skalkotos
            target = tempfile.mkdtemp()
347 25b4d858 Nikos Skalkotos
            try:
348 25b4d858 Nikos Skalkotos
                absmpoints = self._mount(target,
349 25b4d858 Nikos Skalkotos
                    [(mapped[i], filesystems[i].mpoint) for i in mapped.keys()]
350 25b4d858 Nikos Skalkotos
                )
351 74149d07 Nikos Skalkotos
                exclude = self._to_exclude() + [image]
352 e6f134b3 Nikos Skalkotos
                rsync = Rsync('/', target,
353 e6f134b3 Nikos Skalkotos
                              map(lambda p: os.path.relpath(p, '/'), exclude))
354 74149d07 Nikos Skalkotos
                msg = "Copying host files into the image"
355 74149d07 Nikos Skalkotos
                rsync.archive().run(self.out, msg)
356 25b4d858 Nikos Skalkotos
357 25b4d858 Nikos Skalkotos
            finally:
358 25b4d858 Nikos Skalkotos
                self._umount_all(target)
359 25b4d858 Nikos Skalkotos
                os.rmdir(target)
360 25b4d858 Nikos Skalkotos
        finally:
361 25b4d858 Nikos Skalkotos
            for dev in mapped.values():
362 25b4d858 Nikos Skalkotos
                self._unmap_partition(dev)
363 25b4d858 Nikos Skalkotos
            losetup('-d', loop)
364 25b4d858 Nikos Skalkotos
365 25b4d858 Nikos Skalkotos
    def create_image(self):
366 a939e3b8 Nikos Skalkotos
367 25b4d858 Nikos Skalkotos
        image = '/mnt/%s.diskdump' % uuid.uuid4().hex
368 25b4d858 Nikos Skalkotos
369 25b4d858 Nikos Skalkotos
        disk_size = self.disk.device.getLength() * self.disk.device.sectorSize
370 a939e3b8 Nikos Skalkotos
371 a939e3b8 Nikos Skalkotos
        # Create sparse file to host the image
372 25b4d858 Nikos Skalkotos
        truncate("-s", "%d" % disk_size, image)
373 25b4d858 Nikos Skalkotos
374 25b4d858 Nikos Skalkotos
        self._create_partition_table(image)
375 25b4d858 Nikos Skalkotos
        end_sector = self._shrink_partitions(image)
376 a939e3b8 Nikos Skalkotos
377 25b4d858 Nikos Skalkotos
        # Check if the available space is enough to host the image
378 25b4d858 Nikos Skalkotos
        dirname = os.path.dirname(image)
379 25b4d858 Nikos Skalkotos
        size = (end_sector + 1) * self.disk.device.sectorSize
380 25b4d858 Nikos Skalkotos
        self.out.output("Examining available space in %s ..." % dirname, False)
381 25b4d858 Nikos Skalkotos
        stat = os.statvfs(dirname)
382 25b4d858 Nikos Skalkotos
        available = stat.f_bavail * stat.f_frsize
383 25b4d858 Nikos Skalkotos
        if available <= size:
384 25b4d858 Nikos Skalkotos
            raise FatalError('Not enough space in %s to host the image' %
385 25b4d858 Nikos Skalkotos
                             dirname)
386 25b4d858 Nikos Skalkotos
        self.out.success("sufficient")
387 8eea5572 Nikos Skalkotos
388 25b4d858 Nikos Skalkotos
        self._create_filesystems(image)
389 8eea5572 Nikos Skalkotos
390 25b4d858 Nikos Skalkotos
        return image
391 9e4b4de2 Nikos Skalkotos
392 9e4b4de2 Nikos Skalkotos
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :