#!/usr/bin/python -tt
#
# Copyright (c) 2011 Intel, Inc.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; version 2 of the License
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc., 59
# Temple Place - Suite 330, Boston, MA 02111-1307, USA.

import os
import shutil
import tempfile

from mic import chroot, msger, rt_util
from mic.utils import misc, fs_related, errors, cmdln
from mic.conf import configmgr
from mic.plugin import pluginmgr
from mic.imager.loop import LoopImageCreator, load_mountpoints

from mic.pluginbase import ImagerPlugin
class LoopPlugin(ImagerPlugin):
    name = 'loop'

    @classmethod
    @cmdln.option("--compress-disk-image", dest="compress_image",
                  type='choice', choices=("gz", "bz2", "lzo"), default=None,
                  help="Same with --compress-image")
                  # alias to compress-image for compatibility
    @cmdln.option("--compress-image", dest="compress_image",
                  type='choice', choices=("gz", "bz2", "lzo"), default=None,
                  help="Compress all loop images with 'gz' or 'bz2' or 'lzo',"
                  "Note: if you want to use 'lzo', package 'lzop' is needed to"
                  "be installed manually.")
    @cmdln.option("--shrink", action='store_true', default=False,
                  help="Whether to shrink loop images to minimal size")
    def do_create(self, subcmd, opts, *args):
        """${cmd_name}: create loop image

        Usage:
            ${name} ${cmd_name} <ksfile> [OPTS]

        ${cmd_option_list}
        """

        if len(args) != 1:
            raise errors.Usage("Extra arguments given")

        creatoropts = configmgr.create
        ksconf = args[0]

        if creatoropts['runtime'] == "bootstrap":
            configmgr._ksconf = ksconf
            rt_util.bootstrap_mic()
        elif not rt_util.inbootstrap():
            try:
                fs_related.find_binary_path('mic-native')
            except errors.CreatorError:
                if not msger.ask("Subpackage \"mic-native\" has not been "
                                 "installed in your host system, still "
                                 "continue with \"native\" running mode?",
                                 False):
                    raise errors.Abort("Abort because subpackage 'mic-native' "
                                       "has not been installed")

        recording_pkgs = []
        if len(creatoropts['record_pkgs']) > 0:
            recording_pkgs = creatoropts['record_pkgs']

        if creatoropts['release'] is not None:
            if 'name' not in recording_pkgs:
                recording_pkgs.append('name')
            if 'vcs' not in recording_pkgs:
                recording_pkgs.append('vcs')

        configmgr._ksconf = ksconf

        # try to find the pkgmgr
        pkgmgr = None
        backends = pluginmgr.get_plugins('backend')
        if 'auto' == creatoropts['pkgmgr']:
            for key in configmgr.prefer_backends:
                if key in backends:
                    pkgmgr = backends[key]
                    break
        else:
            for key in backends.keys():
                if key == creatoropts['pkgmgr']:
                    pkgmgr = backends[key]
                    break

        if not pkgmgr:
            raise errors.CreatorError("Can't find backend: %s, "
                                      "available choices: %s" %
                                      (creatoropts['pkgmgr'],
                                       ','.join(backends.keys())))

        creator = LoopImageCreator(creatoropts,
                                   pkgmgr,
                                   opts.compress_image,
                                   opts.shrink)

        if len(recording_pkgs) > 0:
            creator._recording_pkgs = recording_pkgs

        image_names = [creator.name + ".img"]
        image_names.extend(creator.get_image_names())
        self.check_image_exists(creator.destdir,
                                creator.pack_to,
                                image_names,
                                creatoropts['release'])

        try:
            creator.check_depend_tools()
            creator.mount(None, creatoropts["cachedir"])
            creator.install()
            creator.configure(creatoropts["repomd"])
            creator.copy_kernel()
            creator.unmount()
            creator.package(creatoropts["destdir"])
            creator.create_manifest()

            if creatoropts['release'] is not None:
                creator.release_output(ksconf,
                                       creatoropts['destdir'],
                                       creatoropts['release'])
            creator.print_outimage_info()

        except errors.CreatorError:
            raise
        finally:
            creator.cleanup()

        msger.info("Finished.")
        return 0

    @classmethod
    def _do_chroot_tar(cls, target, cmd=[]):
        mountfp_xml = os.path.splitext(target)[0] + '.xml'
        if not os.path.exists(mountfp_xml):
            raise errors.CreatorError("No mount point file found for this tar "
                                      "image, please check %s" % mountfp_xml)

        import tarfile
        tar = tarfile.open(target, 'r')
        tmpdir = misc.mkdtemp()
        tar.extractall(path=tmpdir)
        tar.close()

        mntdir = misc.mkdtemp()

        loops = []
        for (mp, label, name, size, fstype) in load_mountpoints(mountfp_xml):
            if fstype in ("ext2", "ext3", "ext4"):
                myDiskMount = fs_related.ExtDiskMount
            elif fstype == "btrfs":
                myDiskMount = fs_related.BtrfsDiskMount
            elif fstype in ("vfat", "msdos"):
                myDiskMount = fs_related.VfatDiskMount
            else:
                raise errors.CreatorError("Cannot support fstype: %s" % fstype)

            name = os.path.join(tmpdir, name)
            size = size * 1024L * 1024L
            loop = myDiskMount(fs_related.SparseLoopbackDisk(name, size),
                               os.path.join(mntdir, mp.lstrip('/')),
                               fstype, size, label)

            try:
                msger.verbose("Mount %s to %s" % (mp, mntdir + mp))
                fs_related.makedirs(os.path.join(mntdir, mp.lstrip('/')))
                loop.mount()

            except:
                loop.cleanup()
                for lp in reversed(loops):
                    chroot.cleanup_after_chroot("img", lp, None, mntdir)

                shutil.rmtree(tmpdir, ignore_errors=True)
                raise

            loops.append(loop)

        try:
            if len(cmd) != 0:
                cmdline = "/usr/bin/env HOME=/root " + ' '.join(cmd)
            else:
                cmdline = "/usr/bin/env HOME=/root /bin/bash"
            chroot.chroot(mntdir, None, cmdline)
        except:
            raise errors.CreatorError("Failed to chroot to %s." % target)
        finally:
            for loop in reversed(loops):
                chroot.cleanup_after_chroot("img", loop, None, mntdir)

            shutil.rmtree(tmpdir, ignore_errors=True)

    @classmethod
    def do_chroot(cls, target, cmd=[]):
        if target.endswith('.tar'):
            import tarfile
            if tarfile.is_tarfile(target):
                LoopPlugin._do_chroot_tar(target, cmd)
                return
            else:
                raise errors.CreatorError("damaged tarball for loop images")

        img = target
        imgsize = misc.get_file_size(img) * 1024L * 1024L
        imgtype = misc.get_image_type(img)
        if imgtype == "btrfsimg":
            fstype = "btrfs"
            myDiskMount = fs_related.BtrfsDiskMount
        elif imgtype in ("ext3fsimg", "ext4fsimg"):
            fstype = imgtype[:4]
            myDiskMount = fs_related.ExtDiskMount
        else:
            raise errors.CreatorError("Unsupported filesystem type: %s" \
                                      % imgtype)

        extmnt = misc.mkdtemp()
        extloop = myDiskMount(fs_related.SparseLoopbackDisk(img, imgsize),
                                                         extmnt,
                                                         fstype,
                                                         4096,
                                                         "%s label" % fstype)
        try:
            extloop.mount()

        except errors.MountError:
            extloop.cleanup()
            shutil.rmtree(extmnt, ignore_errors=True)
            raise

        try:
            if len(cmd) != 0:
                cmdline = ' '.join(cmd)
            else:
                cmdline = "/bin/bash"
            envcmd = fs_related.find_binary_inchroot("env", extmnt)
            if envcmd:
                cmdline = "%s HOME=/root %s" % (envcmd, cmdline)
            chroot.chroot(extmnt, None, cmdline)
        except:
            raise errors.CreatorError("Failed to chroot to %s." % img)
        finally:
            chroot.cleanup_after_chroot("img", extloop, None, extmnt)

    @classmethod
    def do_unpack(cls, srcimg):
        image = os.path.join(tempfile.mkdtemp(dir="/var/tmp", prefix="tmp"),
                             "target.img")
        msger.info("Copying file system ...")
        shutil.copyfile(srcimg, image)
        return image
