raw.py: kill get_diskinfo()
[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     def do_create(self, subcmd, opts, *args):
45         """${cmd_name}: create raw image
46
47         Usage:
48             ${name} ${cmd_name} <ksfile> [OPTS]
49
50         ${cmd_option_list}
51         """
52
53         if len(args) != 1:
54             raise errors.Usage("Extra arguments given")
55
56         creatoropts = configmgr.create
57         ksconf = args[0]
58
59         if creatoropts['runtime'] == "bootstrap":
60             configmgr._ksconf = ksconf
61             rt_util.bootstrap_mic()
62
63         recording_pkgs = []
64         if len(creatoropts['record_pkgs']) > 0:
65             recording_pkgs = creatoropts['record_pkgs']
66
67         if creatoropts['release'] is not None:
68             if 'name' not in recording_pkgs:
69                 recording_pkgs.append('name')
70
71         configmgr._ksconf = ksconf
72
73         # Called After setting the configmgr._ksconf as the creatoropts['name'] is reset there.
74         if creatoropts['release'] is not None:
75             creatoropts['outdir'] = "%s/%s/images/%s/" % (creatoropts['outdir'], creatoropts['release'], creatoropts['name'])
76
77         # try to find the pkgmgr
78         pkgmgr = None
79         backends = pluginmgr.get_plugins('backend')
80         if 'auto' == creatoropts['pkgmgr']:
81             for key in configmgr.prefer_backends:
82                 if key in backends:
83                     pkgmgr = backends[key]
84                     break
85         else:
86             for key in backends.keys():
87                 if key == creatoropts['pkgmgr']:
88                     pkgmgr = backends[key]
89                     break
90
91         if not pkgmgr:
92             raise errors.CreatorError("Can't find backend: %s, "
93                                       "available choices: %s" %
94                                       (creatoropts['pkgmgr'],
95                                        ','.join(backends.keys())))
96
97         creator = raw.RawImageCreator(creatoropts, pkgmgr, opts.compress_image,
98                                       opts.generate_bmap)
99
100         if len(recording_pkgs) > 0:
101             creator._recording_pkgs = recording_pkgs
102
103         images = ["%s-%s.raw" % (creator.name, disk_name)
104                   for disk_name in creator.get_disk_names()]
105         self.check_image_exists(creator.destdir,
106                                 creator.pack_to,
107                                 images,
108                                 creatoropts['release'])
109
110         try:
111             creator.check_depend_tools()
112             creator.mount(None, creatoropts["cachedir"])
113             creator.install()
114             creator.configure(creatoropts["repomd"])
115             creator.copy_kernel()
116             creator.unmount()
117             creator.generate_bmap()
118             creator.package(creatoropts["outdir"])
119             if creatoropts['release'] is not None:
120                 creator.release_output(ksconf, creatoropts['outdir'], creatoropts['release'])
121             creator.print_outimage_info()
122
123         except errors.CreatorError:
124             raise
125         finally:
126             creator.cleanup()
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_disks({'/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'." % partition_info[5])
198
199             if rootpart and rootpart == line[0]:
200                 mountpoint = '/'
201             elif not root_mounted and fstype in ["ext2","ext3","ext4","btrfs"]:
202                 # TODO: Check that this is actually the valid root partition from /etc/fstab
203                 mountpoint = "/"
204                 root_mounted = True
205             elif fstype == "swap":
206                 mountpoint = "swap"
207             else:
208                 # TODO: Assing better mount points for the rest of the partitions.
209                 partition_mounts += 1
210                 mountpoint = "/media/partition_%d" % partition_mounts
211
212             if "boot" in partition_info:
213                 boot = True
214             else:
215                 boot = False
216
217             msger.verbose("Size: %s Bytes, fstype: %s, mountpoint: %s, boot: %s" % (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, fstype = fstype, boot = boot)
220
221         try:
222             imgloop.mount()
223
224         except errors.MountError:
225             imgloop.cleanup()
226             raise
227
228         try:
229             if len(cmd) != 0:
230                 cmdline = ' '.join(cmd)
231             else:
232                 cmdline = "/bin/bash"
233             envcmd = fs_related.find_binary_inchroot("env", imgmnt)
234             if envcmd:
235                 cmdline = "%s HOME=/root %s" % (envcmd, cmdline)
236             chroot.chroot(imgmnt, None, cmdline)
237         except:
238             raise errors.CreatorError("Failed to chroot to %s." %img)
239         finally:
240             chroot.cleanup_after_chroot("img", imgloop, None, imgmnt)
241
242     @classmethod
243     def do_unpack(cls, srcimg):
244         srcimgsize = (misc.get_file_size(srcimg)) * 1024L * 1024L
245         srcmnt = misc.mkdtemp("srcmnt")
246         disk = fs_related.SparseLoopbackDisk(srcimg, srcimgsize)
247         srcloop = PartitionedMount(srcmnt, skipformat = True)
248
249         srcloop.add_disks({'/dev/sdb':disk})
250         srcloop.add_partition(srcimgsize/1024/1024, "/dev/sdb", "/", "ext3", boot=False)
251         try:
252             srcloop.mount()
253
254         except errors.MountError:
255             srcloop.cleanup()
256             raise
257
258         image = os.path.join(tempfile.mkdtemp(dir = "/var/tmp", prefix = "tmp"), "target.img")
259         args = ['dd', "if=%s" % srcloop.partitions[0]['device'], "of=%s" % image]
260
261         msger.info("`dd` image ...")
262         rc = runner.show(args)
263         srcloop.cleanup()
264         shutil.rmtree(os.path.dirname(srcmnt), ignore_errors = True)
265
266         if rc != 0:
267             raise errors.CreatorError("Failed to dd")
268         else:
269             return image