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