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