add option '--fstab-entry' to set the entry in /etc/fstab
[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
67         recording_pkgs = []
68         if len(creatoropts['record_pkgs']) > 0:
69             recording_pkgs = creatoropts['record_pkgs']
70
71         if creatoropts['release'] is not None:
72             if 'name' not in recording_pkgs:
73                 recording_pkgs.append('name')
74
75         configmgr._ksconf = ksconf
76
77         # Called After setting the configmgr._ksconf as the creatoropts['name'] is reset there.
78         if creatoropts['release'] is not None:
79             creatoropts['outdir'] = "%s/%s/images/%s/" % (creatoropts['outdir'], creatoropts['release'], creatoropts['name'])
80
81         # try to find the pkgmgr
82         pkgmgr = None
83         backends = pluginmgr.get_plugins('backend')
84         if 'auto' == creatoropts['pkgmgr']:
85             for key in configmgr.prefer_backends:
86                 if key in backends:
87                     pkgmgr = backends[key]
88                     break
89         else:
90             for key in backends.keys():
91                 if key == creatoropts['pkgmgr']:
92                     pkgmgr = backends[key]
93                     break
94
95         if not pkgmgr:
96             raise errors.CreatorError("Can't find backend: %s, "
97                                       "available choices: %s" %
98                                       (creatoropts['pkgmgr'],
99                                        ','.join(backends.keys())))
100
101         creator = raw.RawImageCreator(creatoropts, pkgmgr, opts.compress_image,
102                                       opts.generate_bmap, opts.fstab_entry)
103
104         if len(recording_pkgs) > 0:
105             creator._recording_pkgs = recording_pkgs
106
107         images = ["%s-%s.raw" % (creator.name, disk_name)
108                   for disk_name in creator.get_disk_names()]
109         self.check_image_exists(creator.destdir,
110                                 creator.pack_to,
111                                 images,
112                                 creatoropts['release'])
113
114         try:
115             creator.check_depend_tools()
116             creator.mount(None, creatoropts["cachedir"])
117             creator.install()
118             creator.configure(creatoropts["repomd"])
119             creator.copy_kernel()
120             creator.unmount()
121             creator.generate_bmap()
122             creator.package(creatoropts["outdir"])
123             if creatoropts['release'] is not None:
124                 creator.release_output(ksconf, creatoropts['outdir'], creatoropts['release'])
125             creator.print_outimage_info()
126
127         except errors.CreatorError:
128             raise
129         finally:
130             creator.cleanup()
131
132         msger.info("Finished.")
133         return 0
134
135     @classmethod
136     def do_chroot(cls, target, cmd=[]):
137         img = target
138         imgsize = misc.get_file_size(img) * 1024L * 1024L
139         partedcmd = fs_related.find_binary_path("parted")
140         disk = fs_related.SparseLoopbackDisk(img, imgsize)
141         imgmnt = misc.mkdtemp()
142         imgloop = PartitionedMount(imgmnt, skipformat = True)
143         imgloop.add_disk('/dev/sdb', disk)
144         img_fstype = "ext3"
145
146         msger.info("Partition Table:")
147         partnum = []
148         for line in runner.outs([partedcmd, "-s", img, "print"]).splitlines():
149             # no use strip to keep line output here
150             if "Number" in line:
151                 msger.raw(line)
152             if line.strip() and line.strip()[0].isdigit():
153                 partnum.append(line.strip()[0])
154                 msger.raw(line)
155
156         rootpart = None
157         if len(partnum) > 1:
158             rootpart = msger.choice("please choose root partition", partnum)
159
160         # Check the partitions from raw disk.
161         # if choose root part, the mark it as mounted
162         if rootpart:
163             root_mounted = True
164         else:
165             root_mounted = False
166         partition_mounts = 0
167         for line in runner.outs([partedcmd,"-s",img,"unit","B","print"]).splitlines():
168             line = line.strip()
169
170             # Lines that start with number are the partitions,
171             # because parted can be translated we can't refer to any text lines.
172             if not line or not line[0].isdigit():
173                 continue
174
175             # Some vars have extra , as list seperator.
176             line = line.replace(",","")
177
178             # Example of parted output lines that are handled:
179             # Number  Start        End          Size         Type     File system     Flags
180             #  1      512B         3400000511B  3400000000B  primary
181             #  2      3400531968B  3656384511B  255852544B   primary  linux-swap(v1)
182             #  3      3656384512B  3720347647B  63963136B    primary  fat16           boot, lba
183
184             partition_info = re.split("\s+",line)
185
186             size = partition_info[3].split("B")[0]
187
188             if len(partition_info) < 6 or partition_info[5] in ["boot"]:
189                 # No filesystem can be found from partition line. Assuming
190                 # btrfs, because that is the only MeeGo fs that parted does
191                 # not recognize properly.
192                 # TODO: Can we make better assumption?
193                 fstype = "btrfs"
194             elif partition_info[5] in ["ext2","ext3","ext4","btrfs"]:
195                 fstype = partition_info[5]
196             elif partition_info[5] in ["fat16","fat32"]:
197                 fstype = "vfat"
198             elif "swap" in partition_info[5]:
199                 fstype = "swap"
200             else:
201                 raise errors.CreatorError("Could not recognize partition fs type '%s'." % partition_info[5])
202
203             if rootpart and rootpart == line[0]:
204                 mountpoint = '/'
205             elif not root_mounted and fstype in ["ext2","ext3","ext4","btrfs"]:
206                 # TODO: Check that this is actually the valid root partition from /etc/fstab
207                 mountpoint = "/"
208                 root_mounted = True
209             elif fstype == "swap":
210                 mountpoint = "swap"
211             else:
212                 # TODO: Assing better mount points for the rest of the partitions.
213                 partition_mounts += 1
214                 mountpoint = "/media/partition_%d" % partition_mounts
215
216             if "boot" in partition_info:
217                 boot = True
218             else:
219                 boot = False
220
221             msger.verbose("Size: %s Bytes, fstype: %s, mountpoint: %s, boot: %s" % (size, fstype, mountpoint, boot))
222             # TODO: add_partition should take bytes as size parameter.
223             imgloop.add_partition((int)(size)/1024/1024, "/dev/sdb", mountpoint, fstype = fstype, boot = boot)
224
225         try:
226             imgloop.mount()
227
228         except errors.MountError:
229             imgloop.cleanup()
230             raise
231
232         try:
233             if len(cmd) != 0:
234                 cmdline = ' '.join(cmd)
235             else:
236                 cmdline = "/bin/bash"
237             envcmd = fs_related.find_binary_inchroot("env", imgmnt)
238             if envcmd:
239                 cmdline = "%s HOME=/root %s" % (envcmd, cmdline)
240             chroot.chroot(imgmnt, None, cmdline)
241         except:
242             raise errors.CreatorError("Failed to chroot to %s." %img)
243         finally:
244             chroot.cleanup_after_chroot("img", imgloop, None, imgmnt)
245
246     @classmethod
247     def do_unpack(cls, srcimg):
248         srcimgsize = (misc.get_file_size(srcimg)) * 1024L * 1024L
249         srcmnt = misc.mkdtemp("srcmnt")
250         disk = fs_related.SparseLoopbackDisk(srcimg, srcimgsize)
251         srcloop = PartitionedMount(srcmnt, skipformat = True)
252
253         srcloop.add_disk('/dev/sdb', disk)
254         srcloop.add_partition(srcimgsize/1024/1024, "/dev/sdb", "/", "ext3", boot=False)
255         try:
256             srcloop.mount()
257
258         except errors.MountError:
259             srcloop.cleanup()
260             raise
261
262         image = os.path.join(tempfile.mkdtemp(dir = "/var/tmp", prefix = "tmp"), "target.img")
263         args = ['dd', "if=%s" % srcloop.partitions[0]['device'], "of=%s" % image]
264
265         msger.info("`dd` image ...")
266         rc = runner.show(args)
267         srcloop.cleanup()
268         shutil.rmtree(os.path.dirname(srcmnt), ignore_errors = True)
269
270         if rc != 0:
271             raise errors.CreatorError("Failed to dd")
272         else:
273             return image