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