Statistics
| Branch: | Tag: | Revision:

root / image_creator / image.py @ 5e18a927

History | View | Annotate | Download (12.8 kB)

1 121f3bc0 Nikos Skalkotos
# -*- coding: utf-8 -*-
2 121f3bc0 Nikos Skalkotos
#
3 f5174d2c Nikos Skalkotos
# Copyright 2013 GRNET S.A. All rights reserved.
4 f5174d2c Nikos Skalkotos
#
5 f5174d2c Nikos Skalkotos
# Redistribution and use in source and binary forms, with or
6 f5174d2c Nikos Skalkotos
# without modification, are permitted provided that the following
7 f5174d2c Nikos Skalkotos
# conditions are met:
8 f5174d2c Nikos Skalkotos
#
9 f5174d2c Nikos Skalkotos
#   1. Redistributions of source code must retain the above
10 f5174d2c Nikos Skalkotos
#      copyright notice, this list of conditions and the following
11 f5174d2c Nikos Skalkotos
#      disclaimer.
12 f5174d2c Nikos Skalkotos
#
13 f5174d2c Nikos Skalkotos
#   2. Redistributions in binary form must reproduce the above
14 f5174d2c Nikos Skalkotos
#      copyright notice, this list of conditions and the following
15 f5174d2c Nikos Skalkotos
#      disclaimer in the documentation and/or other materials
16 f5174d2c Nikos Skalkotos
#      provided with the distribution.
17 f5174d2c Nikos Skalkotos
#
18 f5174d2c Nikos Skalkotos
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
19 f5174d2c Nikos Skalkotos
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 f5174d2c Nikos Skalkotos
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 f5174d2c Nikos Skalkotos
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
22 f5174d2c Nikos Skalkotos
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 f5174d2c Nikos Skalkotos
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 f5174d2c Nikos Skalkotos
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
25 f5174d2c Nikos Skalkotos
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 f5174d2c Nikos Skalkotos
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 f5174d2c Nikos Skalkotos
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 f5174d2c Nikos Skalkotos
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 f5174d2c Nikos Skalkotos
# POSSIBILITY OF SUCH DAMAGE.
30 f5174d2c Nikos Skalkotos
#
31 f5174d2c Nikos Skalkotos
# The views and conclusions contained in the software and
32 f5174d2c Nikos Skalkotos
# documentation are those of the authors and should not be
33 f5174d2c Nikos Skalkotos
# interpreted as representing official policies, either expressed
34 f5174d2c Nikos Skalkotos
# or implied, of GRNET S.A.
35 f5174d2c Nikos Skalkotos
36 f5174d2c Nikos Skalkotos
from image_creator.util import FatalError
37 f5174d2c Nikos Skalkotos
from image_creator.gpt import GPTPartitionTable
38 f5174d2c Nikos Skalkotos
from image_creator.os_type import os_cls
39 f5174d2c Nikos Skalkotos
40 f5174d2c Nikos Skalkotos
import re
41 f5174d2c Nikos Skalkotos
import guestfs
42 f5174d2c Nikos Skalkotos
from sendfile import sendfile
43 f5174d2c Nikos Skalkotos
44 f5174d2c Nikos Skalkotos
45 f5174d2c Nikos Skalkotos
class Image(object):
46 f5174d2c Nikos Skalkotos
    """The instances of this class can create images out of block devices."""
47 f5174d2c Nikos Skalkotos
48 f5174d2c Nikos Skalkotos
    def __init__(self, device, output, bootable=True, meta={}):
49 88f83027 Nikos Skalkotos
        """Create a new Image instance"""
50 f5174d2c Nikos Skalkotos
51 f5174d2c Nikos Skalkotos
        self.device = device
52 f5174d2c Nikos Skalkotos
        self.out = output
53 f5174d2c Nikos Skalkotos
        self.bootable = bootable
54 f5174d2c Nikos Skalkotos
        self.meta = meta
55 f5174d2c Nikos Skalkotos
        self.progress_bar = None
56 f5174d2c Nikos Skalkotos
        self.guestfs_device = None
57 f5174d2c Nikos Skalkotos
        self.size = 0
58 f5174d2c Nikos Skalkotos
59 f5174d2c Nikos Skalkotos
        self.g = guestfs.GuestFS()
60 f5174d2c Nikos Skalkotos
        self.g.add_drive_opts(self.device, readonly=0, format="raw")
61 f5174d2c Nikos Skalkotos
62 f5174d2c Nikos Skalkotos
        # Before version 1.17.14 the recovery process, which is a fork of the
63 f5174d2c Nikos Skalkotos
        # original process that called libguestfs, did not close its inherited
64 f5174d2c Nikos Skalkotos
        # file descriptors. This can cause problems especially if the parent
65 f5174d2c Nikos Skalkotos
        # process has opened pipes. Since the recovery process is an optional
66 f5174d2c Nikos Skalkotos
        # feature of libguestfs, it's better to disable it.
67 f5174d2c Nikos Skalkotos
        self.g.set_recovery_proc(0)
68 f5174d2c Nikos Skalkotos
        version = self.g.version()
69 f5174d2c Nikos Skalkotos
        if version['major'] > 1 or \
70 f5174d2c Nikos Skalkotos
            (version['major'] == 1 and (version['minor'] >= 18 or
71 f5174d2c Nikos Skalkotos
                                        (version['minor'] == 17 and
72 f5174d2c Nikos Skalkotos
                                         version['release'] >= 14))):
73 f5174d2c Nikos Skalkotos
            self.g.set_recovery_proc(1)
74 f5174d2c Nikos Skalkotos
            self.out.output("Enabling recovery proc")
75 f5174d2c Nikos Skalkotos
76 f5174d2c Nikos Skalkotos
        #self.g.set_trace(1)
77 f5174d2c Nikos Skalkotos
        #self.g.set_verbose(1)
78 f5174d2c Nikos Skalkotos
79 f5174d2c Nikos Skalkotos
        self.guestfs_enabled = False
80 f5174d2c Nikos Skalkotos
81 f5174d2c Nikos Skalkotos
    def enable(self):
82 88f83027 Nikos Skalkotos
        """Enable a newly created Image instance"""
83 f5174d2c Nikos Skalkotos
84 f5174d2c Nikos Skalkotos
        self.out.output('Launching helper VM (may take a while) ...', False)
85 f5174d2c Nikos Skalkotos
        # self.progressbar = self.out.Progress(100, "Launching helper VM",
86 f5174d2c Nikos Skalkotos
        #                                     "percent")
87 f5174d2c Nikos Skalkotos
        # eh = self.g.set_event_callback(self.progress_callback,
88 f5174d2c Nikos Skalkotos
        #                               guestfs.EVENT_PROGRESS)
89 f5174d2c Nikos Skalkotos
        self.g.launch()
90 f5174d2c Nikos Skalkotos
        self.guestfs_enabled = True
91 f5174d2c Nikos Skalkotos
        # self.g.delete_event_callback(eh)
92 f5174d2c Nikos Skalkotos
        # self.progressbar.success('done')
93 f5174d2c Nikos Skalkotos
        # self.progressbar = None
94 f5174d2c Nikos Skalkotos
        self.out.success('done')
95 f5174d2c Nikos Skalkotos
96 f5174d2c Nikos Skalkotos
        self.out.output('Inspecting Operating System ...', False)
97 f5174d2c Nikos Skalkotos
        roots = self.g.inspect_os()
98 f5174d2c Nikos Skalkotos
        if len(roots) == 0:
99 f5174d2c Nikos Skalkotos
            raise FatalError("No operating system found")
100 f5174d2c Nikos Skalkotos
        if len(roots) > 1:
101 f5174d2c Nikos Skalkotos
            raise FatalError("Multiple operating systems found."
102 f5174d2c Nikos Skalkotos
                             "We only support images with one OS.")
103 f5174d2c Nikos Skalkotos
        self.root = roots[0]
104 f5174d2c Nikos Skalkotos
        self.guestfs_device = self.g.part_to_dev(self.root)
105 f5174d2c Nikos Skalkotos
        self.size = self.g.blockdev_getsize64(self.guestfs_device)
106 f5174d2c Nikos Skalkotos
        self.meta['PARTITION_TABLE'] = \
107 f5174d2c Nikos Skalkotos
            self.g.part_get_parttype(self.guestfs_device)
108 f5174d2c Nikos Skalkotos
109 f5174d2c Nikos Skalkotos
        self.ostype = self.g.inspect_get_type(self.root)
110 f5174d2c Nikos Skalkotos
        self.distro = self.g.inspect_get_distro(self.root)
111 80411610 Nikos Skalkotos
        self.out.success(
112 80411610 Nikos Skalkotos
            'found a(n) %s system' %
113 80411610 Nikos Skalkotos
            self.ostype if self.distro == "unknown" else self.distro)
114 f5174d2c Nikos Skalkotos
115 f5174d2c Nikos Skalkotos
    def _get_os(self):
116 88f83027 Nikos Skalkotos
        """Return an OS class instance for this image"""
117 f5174d2c Nikos Skalkotos
        if hasattr(self, "_os"):
118 f5174d2c Nikos Skalkotos
            return self._os
119 f5174d2c Nikos Skalkotos
120 f5174d2c Nikos Skalkotos
        if not self.guestfs_enabled:
121 f5174d2c Nikos Skalkotos
            self.enable()
122 f5174d2c Nikos Skalkotos
123 71b0ab28 Nikos Skalkotos
        cls = os_cls(self.distro, self.ostype)
124 71b0ab28 Nikos Skalkotos
        self._os = cls(self.root, self.g, self.out)
125 f5174d2c Nikos Skalkotos
126 71b0ab28 Nikos Skalkotos
        self._os.collect_metadata()
127 f5174d2c Nikos Skalkotos
128 f5174d2c Nikos Skalkotos
        return self._os
129 f5174d2c Nikos Skalkotos
130 f5174d2c Nikos Skalkotos
    os = property(_get_os)
131 f5174d2c Nikos Skalkotos
132 f5174d2c Nikos Skalkotos
    def destroy(self):
133 88f83027 Nikos Skalkotos
        """Destroy this Image instance."""
134 f5174d2c Nikos Skalkotos
135 f5174d2c Nikos Skalkotos
        # In new guestfs versions, there is a handy shutdown method for this
136 f5174d2c Nikos Skalkotos
        try:
137 f5174d2c Nikos Skalkotos
            if self.guestfs_enabled:
138 f5174d2c Nikos Skalkotos
                self.g.umount_all()
139 f5174d2c Nikos Skalkotos
                self.g.sync()
140 f5174d2c Nikos Skalkotos
        finally:
141 f5174d2c Nikos Skalkotos
            # Close the guestfs handler if open
142 f5174d2c Nikos Skalkotos
            self.g.close()
143 f5174d2c Nikos Skalkotos
144 f5174d2c Nikos Skalkotos
#    def progress_callback(self, ev, eh, buf, array):
145 f5174d2c Nikos Skalkotos
#        position = array[2]
146 f5174d2c Nikos Skalkotos
#        total = array[3]
147 f5174d2c Nikos Skalkotos
#
148 f5174d2c Nikos Skalkotos
#        self.progressbar.goto((position * 100) // total)
149 f5174d2c Nikos Skalkotos
150 f5174d2c Nikos Skalkotos
    def _last_partition(self):
151 88f83027 Nikos Skalkotos
        """Return the last partition of the image disk"""
152 f5174d2c Nikos Skalkotos
        if self.meta['PARTITION_TABLE'] not in 'msdos' 'gpt':
153 f5174d2c Nikos Skalkotos
            msg = "Unsupported partition table: %s. Only msdos and gpt " \
154 f5174d2c Nikos Skalkotos
                "partition tables are supported" % self.meta['PARTITION_TABLE']
155 f5174d2c Nikos Skalkotos
            raise FatalError(msg)
156 f5174d2c Nikos Skalkotos
157 f5174d2c Nikos Skalkotos
        is_extended = lambda p: \
158 f5174d2c Nikos Skalkotos
            self.g.part_get_mbr_id(self.guestfs_device, p['part_num']) \
159 f5174d2c Nikos Skalkotos
            in (0x5, 0xf)
160 f5174d2c Nikos Skalkotos
        is_logical = lambda p: \
161 f5174d2c Nikos Skalkotos
            self.meta['PARTITION_TABLE'] == 'msdos' and p['part_num'] > 4
162 f5174d2c Nikos Skalkotos
163 f5174d2c Nikos Skalkotos
        partitions = self.g.part_list(self.guestfs_device)
164 f5174d2c Nikos Skalkotos
        last_partition = partitions[-1]
165 f5174d2c Nikos Skalkotos
166 f5174d2c Nikos Skalkotos
        if is_logical(last_partition):
167 f5174d2c Nikos Skalkotos
            # The disk contains extended and logical partitions....
168 f5174d2c Nikos Skalkotos
            extended = filter(is_extended, partitions)[0]
169 f5174d2c Nikos Skalkotos
            last_primary = [p for p in partitions if p['part_num'] <= 4][-1]
170 f5174d2c Nikos Skalkotos
171 f5174d2c Nikos Skalkotos
            # check if extended is the last primary partition
172 f5174d2c Nikos Skalkotos
            if last_primary['part_num'] > extended['part_num']:
173 f5174d2c Nikos Skalkotos
                last_partition = last_primary
174 f5174d2c Nikos Skalkotos
175 f5174d2c Nikos Skalkotos
        return last_partition
176 f5174d2c Nikos Skalkotos
177 f5174d2c Nikos Skalkotos
    def shrink(self):
178 88f83027 Nikos Skalkotos
        """Shrink the image.
179 f5174d2c Nikos Skalkotos

180 88f83027 Nikos Skalkotos
        This is accomplished by shrinking the last file system of the
181 88f83027 Nikos Skalkotos
        image and then updating the partition table. The new disk size
182 f5174d2c Nikos Skalkotos
        (in bytes) is returned.
183 f5174d2c Nikos Skalkotos

184 f5174d2c Nikos Skalkotos
        ATTENTION: make sure unmount is called before shrink
185 f5174d2c Nikos Skalkotos
        """
186 f5174d2c Nikos Skalkotos
        get_fstype = lambda p: \
187 f5174d2c Nikos Skalkotos
            self.g.vfs_type("%s%d" % (self.guestfs_device, p['part_num']))
188 f5174d2c Nikos Skalkotos
        is_logical = lambda p: \
189 f5174d2c Nikos Skalkotos
            self.meta['PARTITION_TABLE'] == 'msdos' and p['part_num'] > 4
190 f5174d2c Nikos Skalkotos
        is_extended = lambda p: \
191 f5174d2c Nikos Skalkotos
            self.meta['PARTITION_TABLE'] == 'msdos' and \
192 f5174d2c Nikos Skalkotos
            self.g.part_get_mbr_id(self.guestfs_device, p['part_num']) \
193 f5174d2c Nikos Skalkotos
            in (0x5, 0xf)
194 f5174d2c Nikos Skalkotos
195 f5174d2c Nikos Skalkotos
        part_add = lambda ptype, start, stop: \
196 f5174d2c Nikos Skalkotos
            self.g.part_add(self.guestfs_device, ptype, start, stop)
197 f5174d2c Nikos Skalkotos
        part_del = lambda p: self.g.part_del(self.guestfs_device, p)
198 f5174d2c Nikos Skalkotos
        part_get_id = lambda p: self.g.part_get_mbr_id(self.guestfs_device, p)
199 f5174d2c Nikos Skalkotos
        part_set_id = lambda p, id: \
200 f5174d2c Nikos Skalkotos
            self.g.part_set_mbr_id(self.guestfs_device, p, id)
201 f5174d2c Nikos Skalkotos
        part_get_bootable = lambda p: \
202 f5174d2c Nikos Skalkotos
            self.g.part_get_bootable(self.guestfs_device, p)
203 f5174d2c Nikos Skalkotos
        part_set_bootable = lambda p, bootable: \
204 f5174d2c Nikos Skalkotos
            self.g.part_set_bootable(self.guestfs_device, p, bootable)
205 f5174d2c Nikos Skalkotos
206 f5174d2c Nikos Skalkotos
        MB = 2 ** 20
207 f5174d2c Nikos Skalkotos
208 f5174d2c Nikos Skalkotos
        self.out.output("Shrinking image (this may take a while) ...", False)
209 f5174d2c Nikos Skalkotos
210 f5174d2c Nikos Skalkotos
        sector_size = self.g.blockdev_getss(self.guestfs_device)
211 f5174d2c Nikos Skalkotos
212 f5174d2c Nikos Skalkotos
        last_part = None
213 f5174d2c Nikos Skalkotos
        fstype = None
214 f5174d2c Nikos Skalkotos
        while True:
215 f5174d2c Nikos Skalkotos
            last_part = self._last_partition()
216 f5174d2c Nikos Skalkotos
            fstype = get_fstype(last_part)
217 f5174d2c Nikos Skalkotos
218 f5174d2c Nikos Skalkotos
            if fstype == 'swap':
219 f5174d2c Nikos Skalkotos
                self.meta['SWAP'] = "%d:%s" % \
220 f5174d2c Nikos Skalkotos
                    (last_part['part_num'],
221 f5174d2c Nikos Skalkotos
                     (last_part['part_size'] + MB - 1) // MB)
222 f5174d2c Nikos Skalkotos
                part_del(last_part['part_num'])
223 f5174d2c Nikos Skalkotos
                continue
224 f5174d2c Nikos Skalkotos
            elif is_extended(last_part):
225 f5174d2c Nikos Skalkotos
                part_del(last_part['part_num'])
226 f5174d2c Nikos Skalkotos
                continue
227 f5174d2c Nikos Skalkotos
228 f5174d2c Nikos Skalkotos
            # Most disk manipulation programs leave 2048 sectors after the last
229 f5174d2c Nikos Skalkotos
            # partition
230 f5174d2c Nikos Skalkotos
            new_size = last_part['part_end'] + 1 + 2048 * sector_size
231 f5174d2c Nikos Skalkotos
            self.size = min(self.size, new_size)
232 f5174d2c Nikos Skalkotos
            break
233 f5174d2c Nikos Skalkotos
234 f5174d2c Nikos Skalkotos
        if not re.match("ext[234]", fstype):
235 c71133ce Nikos Skalkotos
            self.out.warn("Don't know how to shrink %s partitions." % fstype)
236 f5174d2c Nikos Skalkotos
            return self.size
237 f5174d2c Nikos Skalkotos
238 f5174d2c Nikos Skalkotos
        part_dev = "%s%d" % (self.guestfs_device, last_part['part_num'])
239 f5174d2c Nikos Skalkotos
        self.g.e2fsck_f(part_dev)
240 f5174d2c Nikos Skalkotos
        self.g.resize2fs_M(part_dev)
241 f5174d2c Nikos Skalkotos
242 f5174d2c Nikos Skalkotos
        out = self.g.tune2fs_l(part_dev)
243 f5174d2c Nikos Skalkotos
        block_size = int(filter(lambda x: x[0] == 'Block size', out)[0][1])
244 f5174d2c Nikos Skalkotos
        block_cnt = int(filter(lambda x: x[0] == 'Block count', out)[0][1])
245 f5174d2c Nikos Skalkotos
246 f5174d2c Nikos Skalkotos
        start = last_part['part_start'] / sector_size
247 f5174d2c Nikos Skalkotos
        end = start + (block_size * block_cnt) / sector_size - 1
248 f5174d2c Nikos Skalkotos
249 f5174d2c Nikos Skalkotos
        if is_logical(last_part):
250 f5174d2c Nikos Skalkotos
            partitions = self.g.part_list(self.guestfs_device)
251 f5174d2c Nikos Skalkotos
252 f5174d2c Nikos Skalkotos
            logical = []  # logical partitions
253 f5174d2c Nikos Skalkotos
            for partition in partitions:
254 f5174d2c Nikos Skalkotos
                if partition['part_num'] < 4:
255 f5174d2c Nikos Skalkotos
                    continue
256 f5174d2c Nikos Skalkotos
                logical.append({
257 f5174d2c Nikos Skalkotos
                    'num': partition['part_num'],
258 f5174d2c Nikos Skalkotos
                    'start': partition['part_start'] / sector_size,
259 f5174d2c Nikos Skalkotos
                    'end': partition['part_end'] / sector_size,
260 f5174d2c Nikos Skalkotos
                    'id': part_get_id(partition['part_num']),
261 f5174d2c Nikos Skalkotos
                    'bootable': part_get_bootable(partition['part_num'])
262 f5174d2c Nikos Skalkotos
                })
263 f5174d2c Nikos Skalkotos
264 f5174d2c Nikos Skalkotos
            logical[-1]['end'] = end  # new end after resize
265 f5174d2c Nikos Skalkotos
266 f5174d2c Nikos Skalkotos
            # Recreate the extended partition
267 f5174d2c Nikos Skalkotos
            extended = filter(is_extended, partitions)[0]
268 f5174d2c Nikos Skalkotos
            part_del(extended['part_num'])
269 f5174d2c Nikos Skalkotos
            part_add('e', extended['part_start'] / sector_size, end)
270 f5174d2c Nikos Skalkotos
271 f5174d2c Nikos Skalkotos
            # Create all the logical partitions back
272 f5174d2c Nikos Skalkotos
            for l in logical:
273 f5174d2c Nikos Skalkotos
                part_add('l', l['start'], l['end'])
274 f5174d2c Nikos Skalkotos
                part_set_id(l['num'], l['id'])
275 f5174d2c Nikos Skalkotos
                part_set_bootable(l['num'], l['bootable'])
276 f5174d2c Nikos Skalkotos
        else:
277 f5174d2c Nikos Skalkotos
            # Recreate the last partition
278 f5174d2c Nikos Skalkotos
            if self.meta['PARTITION_TABLE'] == 'msdos':
279 f5174d2c Nikos Skalkotos
                last_part['id'] = part_get_id(last_part['part_num'])
280 f5174d2c Nikos Skalkotos
281 f5174d2c Nikos Skalkotos
            last_part['bootable'] = part_get_bootable(last_part['part_num'])
282 f5174d2c Nikos Skalkotos
            part_del(last_part['part_num'])
283 f5174d2c Nikos Skalkotos
            part_add('p', start, end)
284 f5174d2c Nikos Skalkotos
            part_set_bootable(last_part['part_num'], last_part['bootable'])
285 f5174d2c Nikos Skalkotos
286 f5174d2c Nikos Skalkotos
            if self.meta['PARTITION_TABLE'] == 'msdos':
287 f5174d2c Nikos Skalkotos
                part_set_id(last_part['part_num'], last_part['id'])
288 f5174d2c Nikos Skalkotos
289 f5174d2c Nikos Skalkotos
        new_size = (end + 1) * sector_size
290 f5174d2c Nikos Skalkotos
291 f5174d2c Nikos Skalkotos
        assert (new_size <= self.size)
292 f5174d2c Nikos Skalkotos
293 f5174d2c Nikos Skalkotos
        if self.meta['PARTITION_TABLE'] == 'gpt':
294 f5174d2c Nikos Skalkotos
            ptable = GPTPartitionTable(self.device)
295 f5174d2c Nikos Skalkotos
            self.size = ptable.shrink(new_size, self.size)
296 f5174d2c Nikos Skalkotos
        else:
297 f5174d2c Nikos Skalkotos
            self.size = min(new_size + 2048 * sector_size, self.size)
298 f5174d2c Nikos Skalkotos
299 f5174d2c Nikos Skalkotos
        self.out.success("new size is %dMB" % ((self.size + MB - 1) // MB))
300 f5174d2c Nikos Skalkotos
301 f5174d2c Nikos Skalkotos
        return self.size
302 f5174d2c Nikos Skalkotos
303 f5174d2c Nikos Skalkotos
    def dump(self, outfile):
304 88f83027 Nikos Skalkotos
        """Dumps the content of the image into a file.
305 f5174d2c Nikos Skalkotos

306 f5174d2c Nikos Skalkotos
        This method will only dump the actual payload, found by reading the
307 f5174d2c Nikos Skalkotos
        partition table. Empty space in the end of the device will be ignored.
308 f5174d2c Nikos Skalkotos
        """
309 f5174d2c Nikos Skalkotos
        MB = 2 ** 20
310 f5174d2c Nikos Skalkotos
        blocksize = 4 * MB  # 4MB
311 f5174d2c Nikos Skalkotos
        size = self.size
312 f5174d2c Nikos Skalkotos
        progr_size = (size + MB - 1) // MB  # in MB
313 f5174d2c Nikos Skalkotos
        progressbar = self.out.Progress(progr_size, "Dumping image file", 'mb')
314 f5174d2c Nikos Skalkotos
315 f5174d2c Nikos Skalkotos
        with open(self.device, 'r') as src:
316 f5174d2c Nikos Skalkotos
            with open(outfile, "w") as dst:
317 f5174d2c Nikos Skalkotos
                left = size
318 f5174d2c Nikos Skalkotos
                offset = 0
319 f5174d2c Nikos Skalkotos
                progressbar.next()
320 f5174d2c Nikos Skalkotos
                while left > 0:
321 f5174d2c Nikos Skalkotos
                    length = min(left, blocksize)
322 f5174d2c Nikos Skalkotos
                    sent = sendfile(dst.fileno(), src.fileno(), offset, length)
323 f5174d2c Nikos Skalkotos
324 f5174d2c Nikos Skalkotos
                    # Workaround for python-sendfile API change. In
325 f5174d2c Nikos Skalkotos
                    # python-sendfile 1.2.x (py-sendfile) the returning value
326 f5174d2c Nikos Skalkotos
                    # of sendfile is a tuple, where in version 2.x (pysendfile)
327 f5174d2c Nikos Skalkotos
                    # it is just a sigle integer.
328 f5174d2c Nikos Skalkotos
                    if isinstance(sent, tuple):
329 f5174d2c Nikos Skalkotos
                        sent = sent[1]
330 f5174d2c Nikos Skalkotos
331 f5174d2c Nikos Skalkotos
                    offset += sent
332 f5174d2c Nikos Skalkotos
                    left -= sent
333 f5174d2c Nikos Skalkotos
                    progressbar.goto((size - left) // MB)
334 f5174d2c Nikos Skalkotos
        progressbar.success('image file %s was successfully created' % outfile)
335 f5174d2c Nikos Skalkotos
336 f5174d2c Nikos Skalkotos
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :