more accurate help message for subcommands
[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
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     def do_create(self, subcmd, opts, *args):
37         """${cmd_name}: create raw image
38
39         Usage:
40             ${name} ${cmd_name} <ksfile> [OPTS]
41
42         ${cmd_option_list}
43         """
44
45         if not args:
46             raise errors.Usage("need one argument as the path of ks file")
47
48         if len(args) != 1:
49             raise errors.Usage("Extra arguments given")
50
51         creatoropts = configmgr.create
52         ksconf = args[0]
53
54         if not os.path.exists(ksconf):
55             raise errors.CreatorError("Can't find the file: %s" % ksconf)
56
57         recording_pkgs = []
58         if len(creatoropts['record_pkgs']) > 0:
59             recording_pkgs = creatoropts['record_pkgs']
60
61         if creatoropts['release'] is not None:
62             if 'name' not in recording_pkgs:
63                 recording_pkgs.append('name')
64             ksconf = misc.save_ksconf_file(ksconf, creatoropts['release'])
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         for (key, pcls) in pluginmgr.get_plugins('backend').iteritems():
75             if key == creatoropts['pkgmgr']:
76                 pkgmgr = pcls
77                 break
78
79         if not pkgmgr:
80             pkgmgrs = pluginmgr.get_plugins('backend').keys()
81             raise errors.CreatorError("Can't find package manager: %s (availables: %s)" % (creatoropts['pkgmgr'], ', '.join(pkgmgrs)))
82
83         if creatoropts['runtime']:
84             rt_util.runmic_in_runtime(creatoropts['runtime'], creatoropts, ksconf, None)
85
86         creator = raw.RawImageCreator(creatoropts, pkgmgr)
87
88         if len(recording_pkgs) > 0:
89             creator._recording_pkgs = recording_pkgs
90
91         if creatoropts['release'] is None:
92             for item in creator.get_diskinfo():
93                 imagefile = "%s-%s.raw" % (os.path.join(creator.destdir, creator.name), item['name'])
94                 if os.path.exists(imagefile):
95                     if msger.ask('The target image: %s already exists, cleanup and continue?' % imagefile):
96                        os.unlink(imagefile)
97                     else:
98                        raise errors.Abort('Canceled')
99
100         try:
101             creator.check_depend_tools()
102             creator.mount(None, creatoropts["cachedir"])
103             creator.install()
104             creator.configure(creatoropts["repomd"])
105             creator.unmount()
106             creator.package(creatoropts["outdir"])
107             if creatoropts['release'] is not None:
108                 creator.release_output(ksconf, creatoropts['outdir'], creatoropts['release'])
109             creator.print_outimage_info()
110
111         except errors.CreatorError:
112             raise
113         finally:
114             creator.cleanup()
115
116         msger.info("Finished.")
117         return 0
118
119     @classmethod
120     def do_chroot(cls, target):
121         img = target
122         imgsize = misc.get_file_size(img) * 1024L * 1024L
123         partedcmd = fs_related.find_binary_path("parted")
124         disk = fs_related.SparseLoopbackDisk(img, imgsize)
125         imgmnt = misc.mkdtemp()
126         imgloop = PartitionedMount({'/dev/sdb':disk}, imgmnt, skipformat = True)
127         img_fstype = "ext3"
128
129         # Check the partitions from raw disk.
130         root_mounted = False
131         partition_mounts = 0
132         for line in runner.outs([partedcmd,"-s",img,"unit","B","print"]).splitlines():
133             line = line.strip()
134
135             # Lines that start with number are the partitions,
136             # because parted can be translated we can't refer to any text lines.
137             if not line or not line[0].isdigit():
138                 continue
139
140             # Some vars have extra , as list seperator.
141             line = line.replace(",","")
142
143             # Example of parted output lines that are handled:
144             # Number  Start        End          Size         Type     File system     Flags
145             #  1      512B         3400000511B  3400000000B  primary
146             #  2      3400531968B  3656384511B  255852544B   primary  linux-swap(v1)
147             #  3      3656384512B  3720347647B  63963136B    primary  fat16           boot, lba
148
149             partition_info = re.split("\s+",line)
150
151             size = partition_info[3].split("B")[0]
152
153             if len(partition_info) < 6 or partition_info[5] in ["boot"]:
154                 # No filesystem can be found from partition line. Assuming
155                 # btrfs, because that is the only MeeGo fs that parted does
156                 # not recognize properly.
157                 # TODO: Can we make better assumption?
158                 fstype = "btrfs"
159             elif partition_info[5] in ["ext2","ext3","ext4","btrfs"]:
160                 fstype = partition_info[5]
161             elif partition_info[5] in ["fat16","fat32"]:
162                 fstype = "vfat"
163             elif "swap" in partition_info[5]:
164                 fstype = "swap"
165             else:
166                 raise errors.CreatorError("Could not recognize partition fs type '%s'." % partition_info[5])
167
168             if not root_mounted and fstype in ["ext2","ext3","ext4","btrfs"]:
169                 # TODO: Check that this is actually the valid root partition from /etc/fstab
170                 mountpoint = "/"
171                 root_mounted = True
172             elif fstype == "swap":
173                 mountpoint = "swap"
174             else:
175                 # TODO: Assing better mount points for the rest of the partitions.
176                 partition_mounts += 1
177                 mountpoint = "/media/partition_%d" % partition_mounts
178
179             if "boot" in partition_info:
180                 boot = True
181             else:
182                 boot = False
183
184             msger.verbose("Size: %s Bytes, fstype: %s, mountpoint: %s, boot: %s" % (size, fstype, mountpoint, boot))
185             # TODO: add_partition should take bytes as size parameter.
186             imgloop.add_partition((int)(size)/1024/1024, "/dev/sdb", mountpoint, fstype = fstype, boot = boot)
187
188         try:
189             imgloop.mount()
190
191         except errors.MountError:
192             imgloop.cleanup()
193             raise
194
195         try:
196             chroot.chroot(imgmnt, None,  "/bin/env HOME=/root /bin/bash")
197         except:
198             raise errors.CreatorError("Failed to chroot to %s." %img)
199         finally:
200             chroot.cleanup_after_chroot("img", imgloop, None, imgmnt)
201
202     @classmethod
203     def do_unpack(cls, srcimg):
204         srcimgsize = (misc.get_file_size(srcimg)) * 1024L * 1024L
205         srcmnt = misc.mkdtemp("srcmnt")
206         disk = fs_related.SparseLoopbackDisk(srcimg, srcimgsize)
207         srcloop = PartitionedMount({'/dev/sdb':disk}, srcmnt, skipformat = True)
208
209         srcloop.add_partition(srcimgsize/1024/1024, "/dev/sdb", "/", "ext3", boot=False)
210         try:
211             srcloop.mount()
212
213         except errors.MountError:
214             srcloop.cleanup()
215             raise
216
217         image = os.path.join(tempfile.mkdtemp(dir = "/var/tmp", prefix = "tmp"), "target.img")
218         args = ['dd', "if=%s" % srcloop.partitions[0]['device'], "of=%s" % image]
219
220         msger.info("`dd` image ...")
221         rc = runner.show(args)
222         srcloop.cleanup()
223         shutil.rmtree(os.path.dirname(srcmnt), ignore_errors = True)
224
225         if rc != 0:
226             raise errors.CreatorError("Failed to dd")
227         else:
228             return image