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