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