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