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