28171b7206885125419c03b8727e9ada530b40f7
[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
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("--compress-disk-image", dest="compress_image",
34                   type='choice', choices=("gz", "bz2"), default=None,
35                   help="Same with --compress-image")
36                   # alias to compress-image for compatibility
37     @cmdln.option("--compress-image", dest="compress_image",
38                   type='choice', choices=("gz", "bz2"), default=None,
39                   help="Compress all loop images with 'gz' or 'bz2'")
40     @cmdln.option("--shrink", action='store_true', default=False,
41                   help="Whether to shrink loop images to minimal size")
42     def do_create(self, subcmd, opts, *args):
43         """${cmd_name}: create loop 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         configmgr._ksconf = ksconf
66
67         # Called After setting the configmgr._ksconf
68         # as the creatoropts['name'] is reset there.
69         if creatoropts['release'] is not None:
70             creatoropts['outdir'] = "%s/%s/images/%s/" % (creatoropts['outdir'],
71                                                           creatoropts['release'],
72                                                           creatoropts['name'])
73
74         # try to find the pkgmgr
75         pkgmgr = None
76         for (key, pcls) in pluginmgr.get_plugins('backend').iteritems():
77             if key == creatoropts['pkgmgr']:
78                 pkgmgr = pcls
79                 break
80
81         if not pkgmgr:
82             pkgmgrs = pluginmgr.get_plugins('backend').keys()
83             raise errors.CreatorError("Can't find package manager: %s "
84                                       "(availables: %s)" \
85                                       % (creatoropts['pkgmgr'],
86                                          ', '.join(pkgmgrs)))
87
88         creator = LoopImageCreator(creatoropts,
89                                    pkgmgr,
90                                    opts.compress_image,
91                                    opts.shrink)
92
93         if len(recording_pkgs) > 0:
94             creator._recording_pkgs = recording_pkgs
95
96         self.check_image_exists(creator.destdir,
97                                 creator.pack_to,
98                                 [creator.name + ".img"],
99                                 creatoropts['release'])
100
101         try:
102             creator.check_depend_tools()
103             creator.mount(None, creatoropts["cachedir"])
104             creator.install()
105             creator.configure(creatoropts["repomd"])
106             creator.copy_kernel()
107             creator.unmount()
108             creator.package(creatoropts["outdir"])
109
110             if creatoropts['release'] is not None:
111                 creator.release_output(ksconf,
112                                        creatoropts['outdir'],
113                                        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_tar(cls, target):
126         mountfp_xml = os.path.splitext(target)[0] + '.xml'
127         if not os.path.exists(mountfp_xml):
128             raise errors.CreatorError("No mount point file found for this tar "
129                                       "image, please check %s" % mountfp_xml)
130
131         import tarfile
132         tar = tarfile.open(target, 'r')
133         tmpdir = misc.mkdtemp()
134         tar.extractall(path=tmpdir)
135         tar.close()
136
137         mntdir = misc.mkdtemp()
138
139         loops = []
140         for (mp, label, name, size, fstype) in load_mountpoints(mountfp_xml):
141             if fstype in ("ext2", "ext3", "ext4"):
142                 myDiskMount = fs_related.ExtDiskMount
143             elif fstype == "btrfs":
144                 myDiskMount = fs_related.BtrfsDiskMount
145             elif fstype in ("vfat", "msdos"):
146                 myDiskMount = fs_related.VfatDiskMount
147             else:
148                 msger.error("Cannot support fstype: %s" % fstype)
149
150             name = os.path.join(tmpdir, name)
151             size = size * 1024L * 1024L
152             loop = myDiskMount(fs_related.SparseLoopbackDisk(name, size),
153                                os.path.join(mntdir, mp.lstrip('/')),
154                                fstype, size, label)
155
156             try:
157                 msger.verbose("Mount %s to %s" % (mp, mntdir + mp))
158                 fs_related.makedirs(os.path.join(mntdir, mp.lstrip('/')))
159                 loop.mount()
160
161             except:
162                 loop.cleanup()
163                 for lp in reversed(loops):
164                     chroot.cleanup_after_chroot("img", lp, None, mntdir)
165
166                 shutil.rmtree(tmpdir, ignore_errors=True)
167                 raise
168
169             loops.append(loop)
170
171         try:
172             chroot.chroot(mntdir, None, "/usr/bin/env HOME=/root /bin/bash")
173         except:
174             raise errors.CreatorError("Failed to chroot to %s." % target)
175         finally:
176             for loop in reversed(loops):
177                 chroot.cleanup_after_chroot("img", loop, None, mntdir)
178
179             shutil.rmtree(tmpdir, ignore_errors=True)
180
181     @classmethod
182     def do_chroot(cls, target):
183         if target.endswith('.tar'):
184             import tarfile
185             if tarfile.is_tarfile(target):
186                 LoopPlugin._do_chroot_tar(target)
187                 return
188             else:
189                 raise errors.CreatorError("damaged tarball for loop images")
190
191         img = target
192         imgsize = misc.get_file_size(img) * 1024L * 1024L
193         imgtype = misc.get_image_type(img)
194         if imgtype == "btrfsimg":
195             fstype = "btrfs"
196             myDiskMount = fs_related.BtrfsDiskMount
197         elif imgtype in ("ext3fsimg", "ext4fsimg"):
198             fstype = imgtype[:4]
199             myDiskMount = fs_related.ExtDiskMount
200         else:
201             raise errors.CreatorError("Unsupported filesystem type: %s" \
202                                       % imgtype)
203
204         extmnt = misc.mkdtemp()
205         extloop = myDiskMount(fs_related.SparseLoopbackDisk(img, imgsize),
206                                                          extmnt,
207                                                          fstype,
208                                                          4096,
209                                                          "%s label" % fstype)
210         try:
211             extloop.mount()
212
213         except errors.MountError:
214             extloop.cleanup()
215             shutil.rmtree(extmnt, ignore_errors=True)
216             raise
217
218         try:
219             envcmd = fs_related.find_binary_inchroot("env", extmnt)
220             if envcmd:
221                 cmdline = "%s HOME=/root /bin/bash" % envcmd
222             else:
223                 cmdline = "/bin/bash"
224             chroot.chroot(extmnt, None, cmdline)
225         except:
226             raise errors.CreatorError("Failed to chroot to %s." % img)
227         finally:
228             chroot.cleanup_after_chroot("img", extloop, None, extmnt)
229
230     @classmethod
231     def do_unpack(cls, srcimg):
232         image = os.path.join(tempfile.mkdtemp(dir="/var/tmp", prefix="tmp"),
233                              "target.img")
234         msger.info("Copying file system ...")
235         shutil.copyfile(srcimg, image)
236         return image