Pylint warnings.
[tools/mic.git] / plugins / imager / raw_plugin.py
1 #!/usr/bin/python -tt
2 #
3 # Copyright (c) 2011 Intel, Inc.
4 #
5 # This program is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by the Free
7 # Software Foundation; version 2 of the License
8 #
9 # This program is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 # or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 # for more details.
13 #
14 # You should have received a copy of the GNU General Public License along
15 # with this program; if not, write to the Free Software Foundation, Inc., 59
16 # Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18 import os
19 import shutil
20 import re
21 import tempfile
22
23 from mic import chroot, msger, rt_util
24 from mic.utils import misc, fs_related, errors, runner, cmdln
25 from mic.conf import configmgr
26 from mic.plugin import pluginmgr
27 from mic.utils.partitionedfs import PartitionedMount
28
29 import mic.imager.raw as raw
30
31 from mic.pluginbase import ImagerPlugin
32 class RawPlugin(ImagerPlugin):
33     name = 'raw'
34
35     @classmethod
36     @cmdln.option("--compress-disk-image", dest="compress_image", type='choice',
37                   choices=("gz", "bz2"), default=None,
38                   help="Same with --compress-image")
39     @cmdln.option("--compress-image", dest="compress_image", type='choice',
40                   choices=("gz", "bz2"), default = None,
41                   help="Compress all raw images before package")
42     @cmdln.option("--generate-bmap", action="store_true", default = None,
43                   help="also generate the block map file")
44     @cmdln.option("--fstab-entry", dest="fstab_entry", type='choice',
45                   choices=("name", "uuid"), default="uuid",
46                   help="Set fstab entry, 'name' means using device names, "
47                        "'uuid' means using filesystem uuid")
48     def do_create(self, subcmd, opts, *args):
49         """${cmd_name}: create raw image
50
51         Usage:
52             ${name} ${cmd_name} <ksfile> [OPTS]
53
54         ${cmd_option_list}
55         """
56
57         if len(args) != 1:
58             raise errors.Usage("Extra arguments given")
59
60         creatoropts = configmgr.create
61         ksconf = args[0]
62
63         if creatoropts['runtime'] == "bootstrap":
64             configmgr._ksconf = ksconf
65             rt_util.bootstrap_mic()
66         elif not rt_util.inbootstrap():
67             try:
68                 fs_related.find_binary_path('mic-native')
69             except errors.CreatorError:
70                 if not msger.ask("Subpackage \"mic-native\" has not been "
71                                  "installed in your host system, still "
72                                  "continue with \"native\" running mode?",
73                                  False):
74                     raise errors.Abort("Abort because subpackage 'mic-native' "
75                                        "has not been installed")
76
77         recording_pkgs = []
78         if len(creatoropts['record_pkgs']) > 0:
79             recording_pkgs = creatoropts['record_pkgs']
80
81         if creatoropts['release'] is not None:
82             if 'name' not in recording_pkgs:
83                 recording_pkgs.append('name')
84             if 'vcs' not in recording_pkgs:
85                 recording_pkgs.append('vcs')
86
87         configmgr._ksconf = ksconf
88
89         # Called After setting the configmgr._ksconf as the creatoropts['name'] is reset there.
90         if creatoropts['release'] is not None:
91             creatoropts['outdir'] = "%s/%s/images/%s/" % (creatoropts['outdir'],
92                     creatoropts['release'], creatoropts['name'])
93
94         # try to find the pkgmgr
95         pkgmgr = None
96         backends = pluginmgr.get_plugins('backend')
97         if 'auto' == creatoropts['pkgmgr']:
98             for key in configmgr.prefer_backends:
99                 if key in backends:
100                     pkgmgr = backends[key]
101                     break
102         else:
103             for key in backends.keys():
104                 if key == creatoropts['pkgmgr']:
105                     pkgmgr = backends[key]
106                     break
107
108         if not pkgmgr:
109             raise errors.CreatorError("Can't find backend: %s, "
110                                       "available choices: %s" %
111                                       (creatoropts['pkgmgr'],
112                                        ','.join(backends.keys())))
113
114         creator = raw.RawImageCreator(creatoropts, pkgmgr, opts.compress_image,
115                                       opts.generate_bmap, opts.fstab_entry)
116
117         if len(recording_pkgs) > 0:
118             creator._recording_pkgs = recording_pkgs
119
120         images = ["%s-%s.raw" % (creator.name, disk_name)
121                   for disk_name in creator.get_disk_names()]
122         self.check_image_exists(creator.destdir,
123                                 creator.pack_to,
124                                 images,
125                                 creatoropts['release'])
126
127         try:
128             creator.check_depend_tools()
129             creator.mount(None, creatoropts["cachedir"])
130             creator.install()
131             creator.configure(creatoropts["repomd"])
132             creator.copy_kernel()
133             creator.unmount()
134             creator.generate_bmap()
135             creator.package(creatoropts["outdir"])
136             creator.create_manifest()
137             if creatoropts['release'] is not None:
138                 creator.release_output(ksconf, creatoropts['outdir'], creatoropts['release'])
139             creator.print_outimage_info()
140
141         except errors.CreatorError:
142             raise
143         finally:
144             creator.cleanup()
145
146         msger.info("Finished.")
147         return 0
148
149     @classmethod
150     def do_chroot(cls, target, cmd=[]):
151         img = target
152         imgsize = misc.get_file_size(img) * 1024L * 1024L
153         partedcmd = fs_related.find_binary_path("parted")
154         disk = fs_related.SparseLoopbackDisk(img, imgsize)
155         imgmnt = misc.mkdtemp()
156         imgloop = PartitionedMount(imgmnt, skipformat = True)
157         imgloop.add_disk('/dev/sdb', disk)
158         img_fstype = "ext3"
159
160         msger.info("Partition Table:")
161         partnum = []
162         for line in runner.outs([partedcmd, "-s", img, "print"]).splitlines():
163             # no use strip to keep line output here
164             if "Number" in line:
165                 msger.raw(line)
166             if line.strip() and line.strip()[0].isdigit():
167                 partnum.append(line.strip()[0])
168                 msger.raw(line)
169
170         rootpart = None
171         if len(partnum) > 1:
172             rootpart = msger.choice("please choose root partition", partnum)
173
174         # Check the partitions from raw disk.
175         # if choose root part, the mark it as mounted
176         if rootpart:
177             root_mounted = True
178         else:
179             root_mounted = False
180         partition_mounts = 0
181         for line in runner.outs([ partedcmd, "-s", img, "unit", "B", "print" ]).splitlines():
182             line = line.strip()
183
184             # Lines that start with number are the partitions,
185             # because parted can be translated we can't refer to any text lines.
186             if not line or not line[0].isdigit():
187                 continue
188
189             # Some vars have extra , as list seperator.
190             line = line.replace(",","")
191
192             # Example of parted output lines that are handled:
193             # Number  Start        End          Size         Type     File system    Flags
194             #  1      512B         3400000511B  3400000000B  primary
195             #  2      3400531968B  3656384511B  255852544B   primary  linux-swap(v1)
196             #  3      3656384512B  3720347647B  63963136B    primary  fat16          boot, lba
197
198             partition_info = re.split("\s+", line)
199
200             size = partition_info[3].split("B")[0]
201
202             if len(partition_info) < 6 or partition_info[5] in ["boot"]:
203                 # No filesystem can be found from partition line. Assuming
204                 # btrfs, because that is the only MeeGo fs that parted does
205                 # not recognize properly.
206                 # TODO: Can we make better assumption?
207                 fstype = "btrfs"
208             elif partition_info[5] in [ "ext2", "ext3", "ext4", "btrfs" ]:
209                 fstype = partition_info[5]
210             elif partition_info[5] in [ "fat16", "fat32" ]:
211                 fstype = "vfat"
212             elif "swap" in partition_info[5]:
213                 fstype = "swap"
214             else:
215                 raise errors.CreatorError("Could not recognize partition fs type '%s'." %
216                         partition_info[5])
217
218             if rootpart and rootpart == line[0]:
219                 mountpoint = '/'
220             elif not root_mounted and fstype in [ "ext2", "ext3", "ext4", "btrfs" ]:
221                 # TODO: Check that this is actually the valid root partition from /etc/fstab
222                 mountpoint = "/"
223                 root_mounted = True
224             elif fstype == "swap":
225                 mountpoint = "swap"
226             else:
227                 # TODO: Assing better mount points for the rest of the partitions.
228                 partition_mounts += 1
229                 mountpoint = "/media/partition_%d" % partition_mounts
230
231             if "boot" in partition_info:
232                 boot = True
233             else:
234                 boot = False
235
236             msger.verbose("Size: %s Bytes, fstype: %s, mountpoint: %s, boot: %s" %
237                     (size, fstype, mountpoint, boot))
238             # TODO: add_partition should take bytes as size parameter.
239             imgloop.add_partition((int)(size)/1024/1024, "/dev/sdb", mountpoint,
240                     fstype = fstype, boot = boot)
241
242         try:
243             imgloop.mount()
244
245         except errors.MountError:
246             imgloop.cleanup()
247             raise
248
249         try:
250             if len(cmd) != 0:
251                 cmdline = ' '.join(cmd)
252             else:
253                 cmdline = "/bin/bash"
254             envcmd = fs_related.find_binary_inchroot("env", imgmnt)
255             if envcmd:
256                 cmdline = "%s HOME=/root %s" % (envcmd, cmdline)
257             chroot.chroot(imgmnt, None, cmdline)
258         except:
259             raise errors.CreatorError("Failed to chroot to %s." %img)
260         finally:
261             chroot.cleanup_after_chroot("img", imgloop, None, imgmnt)
262
263     @classmethod
264     def do_unpack(cls, srcimg):
265         srcimgsize = (misc.get_file_size(srcimg)) * 1024L * 1024L
266         srcmnt = misc.mkdtemp("srcmnt")
267         disk = fs_related.SparseLoopbackDisk(srcimg, srcimgsize)
268         srcloop = PartitionedMount(srcmnt, skipformat = True)
269
270         srcloop.add_disk('/dev/sdb', disk)
271         srcloop.add_partition(srcimgsize/1024/1024, "/dev/sdb", "/", "ext3", boot=False)
272         try:
273             srcloop.mount()
274
275         except errors.MountError:
276             srcloop.cleanup()
277             raise
278
279         image = os.path.join(tempfile.mkdtemp(dir = "/var/tmp", prefix = "tmp"), "target.img")
280         args = ['dd', "if=%s" % srcloop.partitions[0]['device'], "of=%s" % image]
281
282         msger.info("`dd` image ...")
283         rc = runner.show(args)
284         srcloop.cleanup()
285         shutil.rmtree(os.path.dirname(srcmnt), ignore_errors = True)
286
287         if rc != 0:
288             raise errors.CreatorError("Failed to dd")
289         else:
290             return image