Merge branch 'master' of https://github.com/jfding/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
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         creator = raw.RawImageCreator(creatoropts, pkgmgr)
82
83         if len(recording_pkgs) > 0:
84             creator._recording_pkgs = recording_pkgs
85
86         if creatoropts['release'] is None:
87             imagefile = "%s-sda.raw" % os.path.join(creator.destdir, creator.name)
88             if os.path.exists(imagefile):
89                 if msger.ask('The target image: %s already exists, cleanup and continue?' % imagefile):
90                     os.unlink(imagefile)
91                 else:
92                     raise errors.Abort('Canceled')
93
94         try:
95             creator.check_depend_tools()
96             creator.mount(None, creatoropts["cachedir"])
97             creator.install()
98             creator.configure(creatoropts["repomd"])
99             creator.unmount()
100             creator.package(creatoropts["outdir"])
101             if creatoropts['release'] is not None:
102                 creator.release_output(ksconf, creatoropts['outdir'], creatoropts['release'])
103             creator.print_outimage_info()
104
105         except errors.CreatorError:
106             raise
107         finally:
108             creator.cleanup()
109
110         msger.info("Finished.")
111         return 0
112
113     @classmethod
114     def do_chroot(cls, target):
115         img = target
116         imgsize = misc.get_file_size(img) * 1024L * 1024L
117         partedcmd = fs_related.find_binary_path("parted")
118         disk = fs_related.SparseLoopbackDisk(img, imgsize)
119         imgmnt = misc.mkdtemp()
120         imgloop = PartitionedMount({'/dev/sdb':disk}, imgmnt, skipformat = True)
121         img_fstype = "ext3"
122
123         # Check the partitions from raw disk.
124         root_mounted = False
125         partition_mounts = 0
126         for line in runner.outs([partedcmd,"-s",img,"unit","B","print"]).splitlines():
127             line = line.strip()
128
129             # Lines that start with number are the partitions,
130             # because parted can be translated we can't refer to any text lines.
131             if not line or not line[0].isdigit():
132                 continue
133
134             # Some vars have extra , as list seperator.
135             line = line.replace(",","")
136
137             # Example of parted output lines that are handled:
138             # Number  Start        End          Size         Type     File system     Flags
139             #  1      512B         3400000511B  3400000000B  primary
140             #  2      3400531968B  3656384511B  255852544B   primary  linux-swap(v1)
141             #  3      3656384512B  3720347647B  63963136B    primary  fat16           boot, lba
142
143             partition_info = re.split("\s+",line)
144
145             size = partition_info[3].split("B")[0]
146
147             if len(partition_info) < 6 or partition_info[5] in ["boot"]:
148                 # No filesystem can be found from partition line. Assuming
149                 # btrfs, because that is the only MeeGo fs that parted does
150                 # not recognize properly.
151                 # TODO: Can we make better assumption?
152                 fstype = "btrfs"
153             elif partition_info[5] in ["ext2","ext3","ext4","btrfs"]:
154                 fstype = partition_info[5]
155             elif partition_info[5] in ["fat16","fat32"]:
156                 fstype = "vfat"
157             elif "swap" in partition_info[5]:
158                 fstype = "swap"
159             else:
160                 raise errors.CreatorError("Could not recognize partition fs type '%s'." % partition_info[5])
161
162             if not root_mounted and fstype in ["ext2","ext3","ext4","btrfs"]:
163                 # TODO: Check that this is actually the valid root partition from /etc/fstab
164                 mountpoint = "/"
165                 root_mounted = True
166             elif fstype == "swap":
167                 mountpoint = "swap"
168             else:
169                 # TODO: Assing better mount points for the rest of the partitions.
170                 partition_mounts += 1
171                 mountpoint = "/media/partition_%d" % partition_mounts
172
173             if "boot" in partition_info:
174                 boot = True
175             else:
176                 boot = False
177
178             msger.verbose("Size: %s Bytes, fstype: %s, mountpoint: %s, boot: %s" % (size, fstype, mountpoint, boot))
179             # TODO: add_partition should take bytes as size parameter.
180             imgloop.add_partition((int)(size)/1024/1024, "/dev/sdb", mountpoint, fstype = fstype, boot = boot)
181
182         try:
183             imgloop.mount()
184
185         except errors.MountError:
186             imgloop.cleanup()
187             raise
188
189         try:
190             chroot.chroot(imgmnt, None,  "/bin/env HOME=/root /bin/bash")
191         except:
192             raise errors.CreatorError("Failed to chroot to %s." %img)
193         finally:
194             chroot.cleanup_after_chroot("img", imgloop, None, imgmnt)
195
196     @classmethod
197     def do_unpack(cls, srcimg):
198         srcimgsize = (misc.get_file_size(srcimg)) * 1024L * 1024L
199         srcmnt = misc.mkdtemp("srcmnt")
200         disk = fs_related.SparseLoopbackDisk(srcimg, srcimgsize)
201         srcloop = PartitionedMount({'/dev/sdb':disk}, srcmnt, skipformat = True)
202
203         srcloop.add_partition(srcimgsize/1024/1024, "/dev/sdb", "/", "ext3", boot=False)
204         try:
205             srcloop.mount()
206
207         except errors.MountError:
208             srcloop.cleanup()
209             raise
210
211         image = os.path.join(tempfile.mkdtemp(dir = "/var/tmp", prefix = "tmp"), "target.img")
212         args = ['dd', "if=%s" % srcloop.partitions[0]['device'], "of=%s" % image]
213
214         msger.info("`dd` image ...")
215         rc = runner.show(args)
216         srcloop.cleanup()
217         shutil.rmtree(os.path.dirname(srcmnt), ignore_errors = True)
218
219         if rc != 0:
220             raise errors.CreatorError("Failed to dd")
221         else:
222             return image