#!/bin/sh
# -*- mode: python -*-

''''which python2 >/dev/null 2>&1 && exec python2 "$0" "$@" # '''
''''which python  >/dev/null 2>&1 && exec python  "$0" "$@" # '''
''''exec echo "Error: I can't find python anywhere"         # '''

import os
import sys
import argparse
import subprocess
import re

INCLUDEOS_PREFIX = None
default_prefix = "/usr/local"

# Locate IncludeOS installation
if "INCLUDEOS_PREFIX" not in os.environ:
    INCLUDEOS_PREFIX = default_prefix
else:
    INCLUDEOS_PREFIX = os.environ['INCLUDEOS_PREFIX']

if not os.path.isdir(INCLUDEOS_PREFIX + "/includeos"):
    print "Couldn't find IncludeOS installation. If you installed to a non-default location (e.g. not " + default_loc + ") please set the environment variable INCLUDEOS_PREFIX to point to this location."
    sys.exit(0)

# Location of vmrunner
sys.path.append(INCLUDEOS_PREFIX + "/includeos")

from vmrunner.prettify import color


# Argparse
parser = argparse.ArgumentParser(
  description="Boot - Build and run IncludeOS services")

parser.add_argument("-c", "--clean", dest="clean", action="store_true",
                    help="Clean previous build before building")

parser.add_argument("-b", "--build", dest="build", action="store_true", default=False,
                    help="Only build the service, don't start it in a VM")

parser.add_argument("--drivers", dest="list_drivers", action="store_true", default=False,
                    help="List all available drivers")

parser.add_argument("--plugins", dest="list_plugins", action="store_true", default=False,
                    help="List all available plugins")

parser.add_argument("-v", "--verbose", dest="verbose", action="store_true", default=False,
                    help="Verbose output when building")

parser.add_argument("--create-bridge", dest="bridge", action="store_true",
                    help="Create bridge43, used in local testing when TAP devices are supported")

parser.add_argument("-g", "--grub", dest="grub", action="store_true",
                    help="Create image with GRUB bootloader that will boot provided binary")

parser.add_argument("--grub-reuse", dest="grub_reuse", action="store_true",
                    help="Reuse existing GRUB image if exists. Avoids reinstalling GRUB.")

parser.add_argument("-j", "--config", dest="config", type = str, metavar = "PATH",
                    help="Location of VM config file - JSON validated against a schema")

parser.add_argument('vm_location', action="store", type = str, default=".",
                    help="Location of the IncludeOS service binary, image or source")

parser.add_argument("-d", "--debug", dest="debug", action="store_true",
                    help="Start hypervisor in debug mode if available")

parser.add_argument("--with-solo5", dest="solo5", action="store_true",
                    help="Run includeOS on solo5 kernel with ukvm-bin as monitor")

parser.add_argument('vmargs', nargs='*', help="Arguments to pass on to the VM start / main")

args = parser.parse_args()


# Pretty printing from this command
nametag = "<boot>"
INFO = color.INFO(nametag)

# Override VM output prepension
color.VM_PREPEND = ""

# in verbose mode we will set VERBOSE=1 for this environment
VERB = False
if (args.verbose):
    os.environ["VERBOSE"] = "1"
    VERB = True
    print INFO, "VERBOSE mode set for environment"
# Avoid the verbose var to hang around next run
elif "VERBOSE" in os.environ:
    del os.environ["VERBOSE"]

## list of drivers by processing library filenames
if (args.list_drivers):
    if "ARCH" not in os.environ:
        ARCH = "x86_64"
    else:
        ARCH = os.environ["ARCH"]
    FLD = INCLUDEOS_PREFIX + "/includeos/" + ARCH + "/drivers"
    print "Listing drivers from " + FLD + "..."
    for file in os.listdir(FLD):
        m = re.search('.*lib(.*)\.a', file)
        line = "=> " + m.group(1) + "\t (" + file + ")"
        print line.expandtabs(20)
    exit(0)

## list of plugins by processing library filenames
if (args.list_plugins):
    if "ARCH" not in os.environ:
        ARCH = "x86_64"
    else:
        ARCH = os.environ["ARCH"]
    FLD = INCLUDEOS_PREFIX + "/includeos/" + ARCH + "/plugins"
    print "Listing plugins from " + FLD + "..."
    for file in os.listdir(FLD):
        m = re.search('.*lib(.*)\.a', file)
        line = "=> " + m.group(1) + "\t (" + file + ")"
        print line.expandtabs(20)
    exit(0)


# Note: importing vmrunner will make it start looking for VM's
# vmrunner also relies on the verbose env var to be set on initialization
from vmrunner import vmrunner

# We can boot either a binary without bootloader, or an image with bootloader already attached
has_bootloader = False

# File extensions we assume to be bootable images, not just a kernel
image_extensions = [".img", ".raw"]

if VERB: print INFO , "Args to pass to VM: ", args.vmargs
# path w/name of VM image to run
image_name = args.vm_location

# if the binary argument is a directory, go there immediately and
# then initialize stuff ...
if (os.path.isdir(args.vm_location)):
    image_name = os.path.abspath(args.vm_location)
    if VERB: print INFO, "Changing directory to  " + image_name
    os.chdir(os.path.abspath(args.vm_location))


if len(vmrunner.vms) < 1:
    # This should never happen - there should always be a default
    print color.FAIL("No vm object found in vmrunner - nothing to boot")
    exit(-1)

if VERB:
    print INFO, len(vmrunner.vms), "VM initialized. Commencing build- and boot..."

config = None
if args.config:
    config = os.path.abspath(args.config)
    if VERB: print INFO, "Using config file", config

hyper_name = "qemu"
if args.solo5:
    hyper_name = "ukvm"
    os.environ['PLATFORM'] = "x86_solo5"
    qkvm_bin = INCLUDEOS_PREFIX + "/includeos/x86_64/lib/ukvm-bin"

    subprocess.call(['touch', INCLUDEOS_PREFIX + 'dummy.disk'])
    subprocess.call(['chmod', '+x', qkvm_bin])
    subprocess.call(['sudo', INCLUDEOS_PREFIX + "/includeos/scripts/ukvm-ifup.sh" ])

vm = vmrunner.vm(config = config, hyper_name = hyper_name)

# Don't listen to events needed by testrunner
vm.on_success(lambda(x): None, do_exit = False)
vm.on_panic(lambda(x): None, do_exit = False)

file_extension = os.path.splitext(args.vm_location)[1]

if args.clean:
    print INFO, "Cleaning build"
    vm.clean()

if (args.debug):
    print INFO, "Booting in debug mode"

if args.bridge:
    print INFO, "Creating bridge"
    subprocess.call(INCLUDEOS_PREFIX + "/includeos/scripts/create_bridge.sh", shell=True)

# If the binary name is a folder, such as ".", build the service
if os.path.isdir(image_name):
    vm.cmake()
    # also, set image name to
    image_name = open("binary.txt", 'r').read()

elif file_extension in image_extensions:
    if VERB: print INFO, "File extension recognized as bootable image"
    has_bootloader = True

elif not file_extension:
    if VERB: print INFO, "No file extension. Trying to boot as kernel"

else:
    if VERB: print INFO, "Unrecognized file extension. Trying to boot as kernel"

if (args.grub or args.grub_reuse):
    print INFO, "Creating GRUB image from ", args.vm_location
    opts = ""

    if args.grub_reuse:
        opts += "-u "

    subprocess.call(INCLUDEOS_PREFIX + "/includeos/scripts/grubify.sh " + opts + image_name, shell=True)
    image_name = image_name + ".grub.img"
    has_bootloader = True

# Skip starting a VM if we are only building
if (args.build): exit(0);

if (not has_bootloader):
    vm.boot(timeout = None, multiboot = True, debug = args.debug, kernel_args = " ".join(args.vmargs), image_name = image_name)
else:
    vm.boot(timeout = None, multiboot = False, debug = args.debug, kernel_args = None, image_name = image_name)
exit(0);
