__author__ = """Copyright Martin J. Bligh, 2006,
                Copyright IBM Corp. 2006, Ryan Harper <ryanh@us.ibm.com>"""

import os, shutil, copy, pickle, re, glob
from autotest_lib.client.bin import kernel, kernel_config, os_dep, test
from autotest_lib.client.bin import utils


class xen(kernel.kernel):

    def log(self, msg):
        print msg
        self.logfile.write('%s\n' % msg)


    def __init__(self, job, base_tree, results_dir, tmp_dir, build_dir,
                                        leave = False, kjob = None):
        # call base-class
        kernel.kernel.__init__(self, job, base_tree, results_dir,
                                        tmp_dir, build_dir, leave)
        self.kjob = kjob


    def config(self, config_file, config_list = None):
        raise NotImplementedError('config() not implemented for xen')


    def build(self, make_opts = '', logfile = '', extraversion='autotest'):
        """build xen

        make_opts
                additional options to make, if any
        """
        self.log('running build')
        os_dep.commands('gcc', 'make')
        # build xen with extraversion flag
        os.environ['XEN_EXTRAVERSION'] = '-unstable-%s'% extraversion
        if logfile == '':
            logfile = os.path.join(self.log_dir, 'xen_build')
        os.chdir(self.build_dir)
        self.log('log_dir: %s ' % self.log_dir)
        self.job.logging.tee_redirect_debug_dir(self.log_dir, log_name=logfile)

        # build xen hypervisor and user-space tools
        targets = ['xen', 'tools']
        threads = 2 * utils.count_cpus()
        for t in targets:
            build_string = 'make -j %d %s %s' % (threads, make_opts, t)
            self.log('build_string: %s' % build_string)
            utils.system(build_string)

        # make a kernel job out of the kernel from the xen src if one isn't provided
        if self.kjob is None:
            # get xen kernel tree ready
            self.log("prep-ing xen'ified kernel source tree")
            utils.system('make prep-kernels')

            v = self.get_xen_kernel_build_ver()
            self.log('building xen kernel version: %s' % v)

            # build xen-ified kernel in xen tree
            kernel_base_tree = os.path.join(self.build_dir, \
                    'linux-%s' % self.get_xen_kernel_build_ver())

            self.log('kernel_base_tree = %s' % kernel_base_tree)
            # fix up XENGUEST value in EXTRAVERSION; we can't have
            # files with '$(XENGEUST)' in the name, =(
            self.fix_up_xen_kernel_makefile(kernel_base_tree)

            # make the kernel job
            self.kjob = self.job.kernel(kernel_base_tree)

            # hardcoding dom0 config (no modules for testing, yay!)
            # FIXME: probe host to determine which config to pick
            c = self.build_dir + '/buildconfigs/linux-defconfig_xen0_x86_32'
            self.log('using kernel config: %s ' % c)
            self.kjob.config(c)

            # Xen's kernel tree sucks; doesn't use bzImage, but vmlinux
            self.kjob.set_build_target('vmlinuz')

            # also, the vmlinuz is not out in arch/*/boot, ARGH! more hackery
            self.kjob.set_build_image(self.job.tmpdir + '/build/linux/vmlinuz')

        self.kjob.build()

        self.job.logging.restore()

        xen_version = self.get_xen_build_ver()
        self.log('BUILD VERSION: Xen: %s Kernel:%s' % \
                        (xen_version, self.kjob.get_kernel_build_ver()))


    def build_timed(self, *args, **kwds):
        raise NotImplementedError('build_timed() not implemented')


    def install(self, tag='', prefix = '/', extraversion='autotest'):
        """make install in the kernel tree"""
        self.log('Installing ...')

        os.chdir(self.build_dir)

        if not os.path.isdir(prefix):
            os.mkdir(prefix)
        self.boot_dir = os.path.join(prefix, 'boot')
        if not os.path.isdir(self.boot_dir):
            os.mkdir(self.boot_dir)

        # remember what we are going to install
        xen_version = '%s-%s' % (self.get_xen_build_ver(), extraversion)
        self.xen_image = self.boot_dir + '/xen-' + xen_version + '.gz'
        self.xen_syms  = self.boot_dir + '/xen-syms-' + xen_version

        self.log('Installing Xen ...')
        os.environ['XEN_EXTRAVERSION'] = '-unstable-%s'% extraversion

        # install xen
        utils.system('make DESTDIR=%s -C xen install' % prefix)

        # install tools
        utils.system('make DESTDIR=%s -C tools install' % prefix)

        # install kernel
        ktag = self.kjob.get_kernel_build_ver()
        kprefix = prefix
        self.kjob.install(tag=ktag, prefix=kprefix)


    def add_to_bootloader(self, tag='autotest', args=''):
        """ add this kernel to bootloader, taking an
            optional parameter of space separated parameters
            e.g.:  kernel.add_to_bootloader('mykernel', 'ro acpi=off')
        """

        # turn on xen mode
        self.job.bootloader.enable_xen_mode()

        # remove existing entry if present
        self.job.bootloader.remove_kernel(tag)

        # add xen and xen kernel
        self.job.bootloader.add_kernel(
                self.kjob.image, tag, initrd=self.kjob.initrd,
                xen_hypervisor=self.xen_image)

        # if no args passed, populate from /proc/cmdline
        if not args:
            args = open('/proc/cmdline', 'r').readline().strip()

        # add args to entry one at a time
        for a in args.split(' '):
            self.job.bootloader.add_args(tag, a)

        # turn off xen mode
        self.job.bootloader.disable_xen_mode()


    def get_xen_kernel_build_ver(self):
        """Check xen buildconfig for current kernel version"""
        version = patchlevel = sublevel = ''
        extraversion = localversion = ''

        version_file = self.build_dir + '/buildconfigs/mk.linux-2.6-xen'

        for line in open(version_file, 'r').readlines():
            if line.startswith('LINUX_VER'):
                start = line.index('=') + 1
                version = line[start:].strip() + "-xen"
                break

        return version


    def fix_up_xen_kernel_makefile(self, kernel_dir):
        """Fix up broken EXTRAVERSION in xen-ified Linux kernel Makefile"""
        xenguest = ''
        makefile = kernel_dir + '/Makefile'

        for line in open(makefile, 'r').readlines():
            if line.startswith('XENGUEST'):
                start = line.index('=') + 1
                xenguest = line[start:].strip()
                break;

        # change out $XENGUEST in EXTRAVERSION line
        utils.system('sed -i.old "s,\$(XENGUEST),%s," %s' % (xenguest,
                                                             makefile))


    def get_xen_build_ver(self):
        """Check Makefile and .config to return kernel version"""
        version = patchlevel = sublevel = ''
        extraversion = localversion = ''

        for line in open(self.build_dir + '/xen/Makefile', 'r').readlines():
            if line.startswith('export XEN_VERSION'):
                start = line.index('=') + 1
                version = line[start:].strip()
            if line.startswith('export XEN_SUBVERSION'):
                start = line.index('=') + 1
                sublevel = line[start:].strip()
            if line.startswith('export XEN_EXTRAVERSION'):
                start = line.index('=') + 1
                extraversion = line[start:].strip()

        return "%s.%s%s" % (version, sublevel, extraversion)