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