# ec2 backend

from __future__ import absolute_import, print_function

from AutoUpgradeTester.UpgradeTestBackendSSH import UpgradeTestBackendSSH
from DistUpgrade.sourceslist import SourcesList

import apt_pkg
import atexit
import glob
import lxc
import os
import shutil

class UpgradeTestBackendLXC(UpgradeTestBackendSSH):
    """ LXC backend """

    def __init__(self, profile):
        UpgradeTestBackendSSH.__init__(self, profile)
        self.profiledir = os.path.abspath(os.path.dirname(profile))

        self.profilename = "upgrader-%s" % self.config.get("NonInteractive","ProfileName")
        self.lxc_container = lxc.Container(self.profilename)

        atexit.register(self._cleanup)

    def _cleanup(self):
        print("_cleanup()")
        if self.lxc_container.running:
            self.lxc_container.stop()
        self.lxc_container.destroy()

    def restart(self):
        print("_restart()")
        self.stop()
        self.start()

    def start(self):
        print("_start()")
        self.lxc_container.start()
        self.lxc_ips = self.lxc_container.get_ips()
        self.ssh_hostname = self.lxc_ips[0]
        self.ssh_port = "22"

    def stop(self):
        print("_stop()")
        self.lxc_container.stop()
        self.ssh_hostname = None
        self.ssh_port = None

    def bootstrap(self, force=False):
        print("bootstrap()")

        src_release = self.config.getWithDefault("DEFAULT", "SourceRelease", "precise")
        src_arch = self.config.getWithDefault("KVM", "Arch", "i386")

        self.lxc_container.create("ubuntu", {"release": src_release, "arch": src_arch})

        os.makedirs("/var/lib/lxc/%s/rootfs/root/.ssh/" % self.profilename)
        shutil.copy("%s.pub" % self.ssh_key, "/var/lib/lxc/%s/rootfs/root/.ssh/authorized_keys" % self.profilename)

        # get common vars
        basepkg = self.config.get("NonInteractive","BasePkg")
        basepkg += " language-pack-en linux-generic"

        # start the VM
        self.start()

        # FIXME: Prevents lxc-net from starting and cutting network (if eth0 and lxcbr0 end up in the same subnet)
        lxcnet = open("/var/lib/lxc/%s/rootfs/etc/init/lxc-net.override" % self.profilename, "w+")
        lxcnet.write("pre-start exec true\npost-stop exec true\n")
        lxcnet.close()

        # FIXME: Some workarounds
        # /usr/sbin/update-grub => fails in LXC
        # /usr/bin/cgroups-mount => fails in LXC unless using the nesting apparmor profile
        # /usr/bin/cgroups-umount => fails in LXC unless using the nesting apparmor profile
        for path in ("/usr/sbin/update-grub", "/usr/bin/cgroups-mount", "/usr/bin/cgroups-umount"):
            self._runInImage(["dpkg-divert", "--rename", "--divert", "%s.orig" % path, "--package", "auto-upgrade-testing", "--add", path])
            self._runInImage(["cp", "/bin/true", path])

        # prepare the sources.list (needed because a AMI may have any
        # sources.list)
        sources = self.getSourcesListFile()
        ret = self._copyToImage(sources.name, "/etc/apt/sources.list")
        assert(ret == 0)

        # install some useful stuff
        ret = self._runInImage(["apt-get","update"])
        assert(ret == 0)

        for i in range(3):
            ret = self._runInImage(["DEBIAN_FRONTEND=noninteractive","apt-get","install", "--allow-unauthenticated", "-y", basepkg ])
        assert(ret == 0)

        # Configure X
        ret = self._runInImage(["DEBIAN_FRONTEND=noninteractive","apt-get","install", "--allow-unauthenticated", "-y", "xserver-xorg-video-dummy" ])
        assert(ret == 0)

        with open("/var/lib/lxc/%s/rootfs/usr/share/X11/xorg.conf.d/00-dummy.conf" % self.profilename, "w+") as fd:
            fd.write("""
Section "Device"
    Identifier "dummy"
    Driver     "dummy"
EndSection
""")

        CMAX = 4000
        pkgs =  self.config.getListFromFile("NonInteractive","AdditionalPkgs")
        while(len(pkgs)) > 0:
            print("installing additonal: %s" % pkgs[:CMAX])
            ret= self._runInImage(["DEBIAN_FRONTEND=noninteractive","apt-get","install","--reinstall", "--allow-unauthenticated", "-y"]+pkgs[:CMAX])
            print("apt(2) returned: %s" % ret)
            if ret != 0:
                #self._cacheDebs(tmpdir)
                print("apt returned a error, stopping")
                self.stop_instance()
                return False
            pkgs = pkgs[CMAX+1:]

        if self.config.has_option("NonInteractive","PostBootstrapScript"):
            script = self.config.get("NonInteractive","PostBootstrapScript")
            print("have PostBootstrapScript: %s" % script)
            if os.path.exists(script):
                self._runInImage(["mkdir","/upgrade-tester"])
                self._copyToImage(script, "/upgrade-tester")
                print("running script: %s" % os.path.join("/tmp",script))
                self._runInImage([os.path.join("/upgrade-tester",script)])
            else:
                print("WARNING: %s not found" % script)

        if self.config.getWithDefault("NonInteractive",
                                      "UpgradeFromDistOnBootstrap", False):
            print("running apt-get upgrade in from dist (after bootstrap)")
            for i in range(3):
                ret = self._runInImage(["DEBIAN_FRONTEND=noninteractive","apt-get","--allow-unauthenticated", "-y","dist-upgrade"])
            assert(ret == 0)

        print("Cleaning image")
        ret = self._runInImage(["apt-get","clean"])
        assert(ret == 0)

        # FIXME: probably should be done in one of the generic classes
        print("configuring upgrader")
        ret = self._runInImage(["sed", "-i", "s/lts/normal/g", "/etc/update-manager/release-upgrades"])

        self.restart()

        return True

    def upgrade(self):
        print("upgrade()")

        # clean from any leftover pyc files
        for f in glob.glob("%s/*.pyc" %  self.upgradefilesdir):
            os.unlink(f)

        print("Starting for upgrade")

        assert(self.ssh_hostname)

        # copy the profile
        if os.path.exists(self.profile):
            print("Copying '%s' to image overrides" % self.profile)
            self._runInImage(["mkdir","-p","/etc/update-manager/release-upgrades.d"])
            self._copyToImage(self.profile, "/etc/update-manager/release-upgrades.d/")

        # copy test repo sources.list (if needed)
        test_repo = self.config.getWithDefault("NonInteractive","AddRepo","")
        if test_repo:
            test_repo = os.path.join(os.path.dirname(self.profile), test_repo)
            self._copyToImage(test_repo, "/etc/apt/sources.list.d")
            sourcelist = self.getSourcesListFile()
            apt_pkg.Config.Set("Dir::Etc", os.path.dirname(sourcelist.name))
            apt_pkg.Config.Set("Dir::Etc::sourcelist", 
                               os.path.basename(sourcelist.name))
            sources = SourcesList(matcherPath=".")
            sources.load(test_repo)
            # add the uri to the list of valid mirros in the image
            for entry in sources.list:
                if (not (entry.invalid or entry.disabled) and
                    entry.type == "deb"):
                    print("adding %s to mirrors" % entry.uri)
                    self._runInImage(["echo '%s' >> /upgrade-tester/mirrors.cfg" % entry.uri])

        # check if we have a bzr checkout dir to run against or
        # if we should just run the normal upgrader
        if (os.path.exists(self.upgradefilesdir) and
            self.config.getWithDefault("NonInteractive", 
                                       "UseUpgraderFromBzr", 
                                       True)):
            self._copyUpgraderFilesFromBzrCheckout()
            ret = self._runBzrCheckoutUpgrade()
        else:
            ret = self._runInImage(["do-release-upgrade","-d",
                                    "-f","DistUpgradeViewNonInteractive"])
        print("dist-upgrade.py returned: %i" % ret)

        # copy the result
        print("coyping the result")
        self._copyFromImage("/var/log/dist-upgrade/*",self.resultdir)

        # stop the machine
        print("Shuting down the container")
        self.stop()

        return True
