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