77e039c160a47bd70d9a545713938f05018f94a4
[tools/mic.git] / plugins / imager / loop_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 tempfile
21
22 from mic import chroot, msger, rt_util
23 from mic.utils import misc, fs_related, errors, cmdln
24 from mic.conf import configmgr
25 from mic.plugin import pluginmgr
26 from mic.imager.loop import LoopImageCreator, load_mountpoints
27
28 from mic.pluginbase import ImagerPlugin
29 class LoopPlugin(ImagerPlugin):
30     name = 'loop'
31
32     @classmethod
33     @cmdln.option("--taring-to", dest="compress_to", type='string',
34                   default=None, help="same with '--compress-to'")
35     @cmdln.option("--compress-to", dest="compress_to", type='string',
36                   default=None, help="Specify compress filename for all image "
37                   "output, compress type decided by file extension, '.zip' for "
38                   "zip format, '.tar' for tar format, default is tar format")
39     def do_create(self, subcmd, opts, *args):
40         """${cmd_name}: create loop image
41
42         Usage:
43             ${name} ${cmd_name} <ksfile> [OPTS]
44
45         ${cmd_option_list}
46         """
47
48         if not args:
49             raise errors.Usage("need one argument as the path of ks file")
50
51         if len(args) != 1:
52             raise errors.Usage("Extra arguments given")
53
54         creatoropts = configmgr.create
55         ksconf = args[0]
56
57         if not os.path.exists(ksconf):
58             raise errors.CreatorError("Can't find the file: %s" % ksconf)
59
60         recording_pkgs = []
61         if len(creatoropts['record_pkgs']) > 0:
62             recording_pkgs = creatoropts['record_pkgs']
63
64         if creatoropts['release'] is not None:
65             if 'name' not in recording_pkgs:
66                 recording_pkgs.append('name')
67             ksconf = misc.save_ksconf_file(ksconf, creatoropts['release'])
68
69         configmgr._ksconf = ksconf
70
71         # Called After setting the configmgr._ksconf
72         # as the creatoropts['name'] is reset there.
73         if creatoropts['release'] is not None:
74             creatoropts['outdir'] = "%s/%s/images/%s/" % (creatoropts['outdir'],
75                                                           creatoropts['release'],
76                                                           creatoropts['name'])
77
78         # try to find the pkgmgr
79         pkgmgr = None
80         for (key, pcls) in pluginmgr.get_plugins('backend').iteritems():
81             if key == creatoropts['pkgmgr']:
82                 pkgmgr = pcls
83                 break
84
85         if not pkgmgr:
86             pkgmgrs = pluginmgr.get_plugins('backend').keys()
87             raise errors.CreatorError("Can't find package manager: %s "
88                                       "(availables: %s)" \
89                                       % (creatoropts['pkgmgr'],
90                                          ', '.join(pkgmgrs)))
91
92         if creatoropts['runtime']:
93             rt_util.runmic_in_runtime(creatoropts['runtime'], creatoropts, ksconf, None)
94
95         creator = LoopImageCreator(creatoropts, pkgmgr, opts.compress_to)
96
97         if len(recording_pkgs) > 0:
98             creator._recording_pkgs = recording_pkgs
99
100         if creatoropts['release'] is None:
101             if opts.compress_to:
102                 imagefile = "%s" % os.path.join(creator.destdir, creator.compress_to)
103             else:
104                 imagefile = "%s.img" % os.path.join(creator.destdir, creator.name)
105
106             if os.path.exists(imagefile):
107                 if msger.ask('The target image: %s already exists, cleanup '
108                              'and continue?' % imagefile):
109                     os.unlink(imagefile)
110                 else:
111                     raise errors.Abort('Canceled')
112
113         try:
114             creator.check_depend_tools()
115             creator.mount(None, creatoropts["cachedir"])
116             creator.install()
117             creator.configure(creatoropts["repomd"])
118             creator.copy_kernel()            
119             creator.unmount()
120             creator.package(creatoropts["outdir"])
121
122             if creatoropts['release'] is not None:
123                 creator.release_output(ksconf, 
124                                        creatoropts['outdir'],
125                                        creatoropts['release'])
126             creator.print_outimage_info()
127
128         except errors.CreatorError:
129             raise
130         finally:
131             creator.cleanup()
132
133         msger.info("Finished.")
134         return 0
135
136     @classmethod
137     def _do_chroot_tar(cls, target):
138         mountfp_xml = os.path.splitext(target)[0] + '.xml'
139         if not os.path.exists(mountfp_xml):
140             raise errors.CreatorError("No mount point file found for this tar "
141                                       "image, please check %s" % mountfp_xml)
142
143         import tarfile
144         tar = tarfile.open(target, 'r')
145         tmpdir = misc.mkdtemp()
146         tar.extractall(path=tmpdir)
147         tar.close()
148
149         mntdir = misc.mkdtemp()
150
151         loops = []
152         for (mp, label, name, size, fstype) in load_mountpoints(mountfp_xml):
153             if fstype in ("ext2", "ext3", "ext4"):
154                 myDiskMount = fs_related.ExtDiskMount
155             elif fstype == "btrfs":
156                 myDiskMount = fs_related.BtrfsDiskMount
157             elif fstype in ("vfat", "msdos"):
158                 myDiskMount = fs_related.VfatDiskMount
159             else:
160                 msger.error("Cannot support fstype: %s" % fstype)
161
162             name = os.path.join(tmpdir, name)
163             size = size * 1024L * 1024L
164             loop = myDiskMount(fs_related.SparseLoopbackDisk(name, size),
165                                os.path.join(mntdir, mp.lstrip('/')),
166                                fstype, size, label)
167
168             try:
169                 msger.verbose("Mount %s to %s" % (mp, mntdir + mp))
170                 fs_related.makedirs(os.path.join(mntdir, mp.lstrip('/')))
171                 loop.mount()
172
173             except:
174                 loop.cleanup()
175                 for lp in reversed(loops):
176                     chroot.cleanup_after_chroot("img", lp, None, mntdir)
177
178                 shutil.rmtree(tmpdir, ignore_errors=True)
179                 raise
180
181             loops.append(loop)
182
183         try:
184             chroot.chroot(mntdir, None, "/bin/env HOME=/root /bin/bash")
185         except:
186             raise errors.CreatorError("Failed to chroot to %s." % target)
187         finally:
188             for loop in reversed(loops):
189                 chroot.cleanup_after_chroot("img", loop, None, mntdir)
190
191             shutil.rmtree(tmpdir, ignore_errors=True)
192
193     @classmethod
194     def do_chroot(cls, target):
195         if target.endswith('.tar'):
196             import tarfile
197             if tarfile.is_tarfile(target):
198                 LoopPlugin._do_chroot_tar(target)
199                 return
200             else:
201                 raise errors.CreatorError("damaged tarball for loop images")
202
203         img = target
204         imgsize = misc.get_file_size(img) * 1024L * 1024L
205         imgtype = misc.get_image_type(img)
206         if imgtype == "btrfsimg":
207             fstype = "btrfs"
208             myDiskMount = fs_related.BtrfsDiskMount
209         elif imgtype in ("ext3fsimg", "ext4fsimg"):
210             fstype = imgtype[:4]
211             myDiskMount = fs_related.ExtDiskMount
212         else:
213             raise errors.CreatorError("Unsupported filesystem type: %s" \
214                                       % imgtype)
215
216         extmnt = misc.mkdtemp()
217         extloop = myDiskMount(fs_related.SparseLoopbackDisk(img, imgsize),
218                                                          extmnt,
219                                                          fstype,
220                                                          4096,
221                                                          "%s label" % fstype)
222         try:
223             extloop.mount()
224
225         except errors.MountError:
226             extloop.cleanup()
227             shutil.rmtree(extmnt, ignore_errors=True)
228             raise
229
230         try:
231             chroot.chroot(extmnt, None,  "/bin/env HOME=/root /bin/bash")
232         except:
233             raise errors.CreatorError("Failed to chroot to %s." % img)
234         finally:
235             chroot.cleanup_after_chroot("img", extloop, None, extmnt)
236
237     @classmethod
238     def do_unpack(cls, srcimg):
239         image = os.path.join(tempfile.mkdtemp(dir="/var/tmp", prefix="tmp"),
240                              "target.img")
241         msger.info("Copying file system ...")
242         shutil.copyfile(srcimg, image)
243         return image