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