Add new 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             msger.info("Running command in parameter run_script: "+"".join(cmd))
121             try:
122                 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
123                 p.communicate()
124             except OSError,err:
125                 msger.warning(str(err))
126
127
128         msger.info("Finished.")
129         return 0
130
131     @classmethod
132     def do_chroot(cls, target, cmd=[]):
133         img = target
134         imgsize = misc.get_file_size(img) * 1024L * 1024L
135         partedcmd = fs_related.find_binary_path("parted")
136         disk = fs_related.SparseLoopbackDisk(img, imgsize)
137         imgmnt = misc.mkdtemp()
138         imgloop = PartitionedMount(imgmnt, skipformat = True)
139         imgloop.add_disk('/dev/sdb', disk)
140         img_fstype = "ext3"
141
142         msger.info("Partition Table:")
143         partnum = []
144         for line in runner.outs([partedcmd, "-s", img, "print"]).splitlines():
145             # no use strip to keep line output here
146             if "Number" in line:
147                 msger.raw(line)
148             if line.strip() and line.strip()[0].isdigit():
149                 partnum.append(line.strip()[0])
150                 msger.raw(line)
151
152         rootpart = None
153         if len(partnum) > 1:
154             rootpart = msger.choice("please choose root partition", partnum)
155
156         # Check the partitions from raw disk.
157         # if choose root part, the mark it as mounted
158         if rootpart:
159             root_mounted = True
160         else:
161             root_mounted = False
162         partition_mounts = 0
163         for line in runner.outs([ partedcmd, "-s", img, "unit", "B", "print" ]).splitlines():
164             line = line.strip()
165
166             # Lines that start with number are the partitions,
167             # because parted can be translated we can't refer to any text lines.
168             if not line or not line[0].isdigit():
169                 continue
170
171             # Some vars have extra , as list seperator.
172             line = line.replace(",","")
173
174             # Example of parted output lines that are handled:
175             # Number  Start        End          Size         Type     File system    Flags
176             #  1      512B         3400000511B  3400000000B  primary
177             #  2      3400531968B  3656384511B  255852544B   primary  linux-swap(v1)
178             #  3      3656384512B  3720347647B  63963136B    primary  fat16          boot, lba
179
180             partition_info = re.split("\s+", line)
181
182             size = partition_info[3].split("B")[0]
183
184             if len(partition_info) < 6 or partition_info[5] in ["boot"]:
185                 # No filesystem can be found from partition line. Assuming
186                 # btrfs, because that is the only MeeGo fs that parted does
187                 # not recognize properly.
188                 # TODO: Can we make better assumption?
189                 fstype = "btrfs"
190             elif partition_info[5] in [ "ext2", "ext3", "ext4", "btrfs" ]:
191                 fstype = partition_info[5]
192             elif partition_info[5] in [ "fat16", "fat32" ]:
193                 fstype = "vfat"
194             elif "swap" in partition_info[5]:
195                 fstype = "swap"
196             else:
197                 raise errors.CreatorError("Could not recognize partition fs type '%s'." %
198                         partition_info[5])
199
200             if rootpart and rootpart == line[0]:
201                 mountpoint = '/'
202             elif not root_mounted and fstype in [ "ext2", "ext3", "ext4", "btrfs" ]:
203                 # TODO: Check that this is actually the valid root partition from /etc/fstab
204                 mountpoint = "/"
205                 root_mounted = True
206             elif fstype == "swap":
207                 mountpoint = "swap"
208             else:
209                 # TODO: Assing better mount points for the rest of the partitions.
210                 partition_mounts += 1
211                 mountpoint = "/media/partition_%d" % partition_mounts
212
213             if "boot" in partition_info:
214                 boot = True
215             else:
216                 boot = False
217
218             msger.verbose("Size: %s Bytes, fstype: %s, mountpoint: %s, boot: %s" %
219                     (size, fstype, mountpoint, boot))
220             # TODO: add_partition should take bytes as size parameter.
221             imgloop.add_partition((int)(size)/1024/1024, "/dev/sdb", mountpoint,
222                     fstype = fstype, boot = boot)
223
224         try:
225             imgloop.mount()
226
227         except errors.MountError:
228             imgloop.cleanup()
229             raise
230
231         try:
232             if len(cmd) != 0:
233                 cmdline = ' '.join(cmd)
234             else:
235                 cmdline = "/bin/bash"
236             envcmd = fs_related.find_binary_inchroot("env", imgmnt)
237             if envcmd:
238                 cmdline = "%s HOME=/root %s" % (envcmd, cmdline)
239             chroot.chroot(imgmnt, None, cmdline)
240         except:
241             raise errors.CreatorError("Failed to chroot to %s." %img)
242         finally:
243             chroot.cleanup_after_chroot("img", imgloop, None, imgmnt)
244
245     @classmethod
246     def do_unpack(cls, srcimg):
247         srcimgsize = (misc.get_file_size(srcimg)) * 1024L * 1024L
248         srcmnt = misc.mkdtemp("srcmnt")
249         disk = fs_related.SparseLoopbackDisk(srcimg, srcimgsize)
250         srcloop = PartitionedMount(srcmnt, skipformat = True)
251
252         srcloop.add_disk('/dev/sdb', disk)
253         srcloop.add_partition(srcimgsize/1024/1024, "/dev/sdb", "/", "ext3", boot=False)
254         try:
255             srcloop.mount()
256
257         except errors.MountError:
258             srcloop.cleanup()
259             raise
260
261         image = os.path.join(tempfile.mkdtemp(dir = "/var/tmp", prefix = "tmp"), "target.img")
262         args = ['dd', "if=%s" % srcloop.partitions[0]['device'], "of=%s" % image]
263
264         msger.info("`dd` image ...")
265         rc = runner.show(args)
266         srcloop.cleanup()
267         shutil.rmtree(os.path.dirname(srcmnt), ignore_errors = True)
268
269         if rc != 0:
270             raise errors.CreatorError("Failed to dd")
271         else:
272             return image