#! /usr/bin/python

import daemon
import os.path
import logging
import sys
import ConfigParser
from signal import signal, SIGTERM
import shutil
from string import Template

from b3_led_manager import *
import utils
import disks
import partitions

__author__ = 'Charles Leclerc <leclerc.charles@gmail.com>'

VERSION = '2.0'

CONFIG_FILE = '/mnt/usb/install/install.ini'
LOG_FILE = '/mnt/usb/install/install.log'
PID_FILE = '/var/run/installer.pid'


def write_pid():
    o = open(PID_FILE, 'w')
    o.write('%i\n' % (os.getpid(),))
    o.close()


def remove_pid():
    if os.path.exists(PID_FILE):
        os.unlink(PID_FILE)


def daemon_term_handler(signum, frame):
    global error
    remove_pid()
    if error:
        led_error()
    else:
        led_rescue()
    sys.exit(0)

# ------------------------------------
# ----- Preliminary script -----------
# ------------------------------------

if os.path.exists(PID_FILE):
    sys.stderr.write('%s exists ; refusing to run !\n' % (PID_FILE, ))
    sys.exit(1)


# Exit the script in early phase due to install key not found/not mounted or no configuration file found
def early_exit():
    if not foreground:
        logging.info("Configuring network with default settings")
        utils.configure_network()
    logging.warning("Install script ended with errors")
    logging.shutdown()
    if foreground:
        led_error()
        sys.exit(1)
    else:
        with daemon.DaemonContext():
            write_pid()
            signal(SIGTERM, daemon_term_handler)
            utils.loop_ip_forever(True)
            remove_pid()
            sys.exit(1)

led_install()

# Foreground run ?
foreground = len(sys.argv) > 1 and sys.argv[1] == '-f'

if foreground:
    # Everyting to the console
    logging.basicConfig(format='%(asctime)s - %(levelname)s - %(message)s', datefmt='%H:%M:%S',
                        level=logging.INFO)
else:
    # Early log facility in /root folder
    logging.basicConfig(format='%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S',
                        level=logging.INFO, filename='/root/install-early.log')

# Configuration defaults
config = ConfigParser.SafeConfigParser()
utils.config = config

for s in ('wan', 'lan'):
    config.add_section(s)
    config.set(s, 'proto', 'dhcp')


if os.path.exists(CONFIG_FILE):
    logging.info('Loading configuration from ' + CONFIG_FILE)
    if CONFIG_FILE not in config.read(CONFIG_FILE):
        logging.error('Unable to read configuration file !')
        early_exit()
else:
    logging.error('No configuration file found on USB key !')
    early_exit()

if not foreground:
    utils.configure_network()
    logging.info("Initialization done. Preparing daemon forking")
    # Logging facility shutdown
    logging.shutdown()
    logging.root.handlers = []


# ------------------------------------
# ----- Main install script ----------
# ------------------------------------

def do_install():
    global error, config

    image = os.path.normpath("/mnt/usb/install/" + config.get('general', 'image'))
    logging.info("Checking image file "+image)
    if not os.path.isfile(image):
        logging.error("image file not found !")
        error = True
        return

    if not disks.inventory_existing():
        error = True
        return

    if len(disks.disks_details) == 0:
        logging.error("No SATA disk found !")
        error = True
        return

    dest = disks.disks_details.keys()[0]
    if config.has_option('general', 'size') and config.get('general', 'size', ) == 'full':
        size = 'full'
    else:
        size = utils.getfloat_check_conf('general', 'size', 10.)
        if size < 8:
            logging.warning("specified size (%.1f) below 8 ; overriding with size=8" % (size, ))
            size = 8.
    swap = utils.getfloat_check_conf('general', 'swap', 512.)
    if swap < 256:
        logging.warning("specified swap size (%.1f) below 256 ; overriding with swap=256" % (swap, ))
        swap = 256.

    logging.info("Destination disk: %s (%s, %s)" %
                 (dest, utils.sizeof_fmt(disks.disks_details[dest]['size']),
                  disks.disks_details[dest]['type'] if disks.disks_details[dest]['type'] else 'blank'))

    swap_n = disks.check_and_prepare_disk(utils.getboolean_check_conf('general', 'wipe', False), size, swap, dest)
    if not swap_n:
        error = True
        return

    logging.info("Disk looking good, proceed with formatting")

    logging.info("Formatting system partition")
    if not partitions.format_system(dest):
        logging.error("Error while formatting system partition")
        error = True
        return

    logging.info("Formatting swap partition")
    if not partitions.format_swap(dest, swap_n):
        logging.error("Error while formatting swap partition")
        error = True
        return

    logging.info("Mounting system partition")
    if not partitions.mount_target(dest):
        logging.error("Error while mounting system partition")
        error = True
        return

    logging.info("Extracting image file to target")
    ex_res = utils.runcmd1(["tar", "-xf", image, "-C", "/mnt/target"], err_to_out=True) == 0
    if not ex_res:
        logging.error("Error while extracting image")
        logging.info("Umounting system partition")
        if not partitions.umount_target():
            logging.error("Error while unmounting target")
        error = True
        return

    if utils.getboolean_check_conf('general', 'copy-network-settings', True):
        if os.path.exists('/etc/resolv.conf'):
            logging.info('Copying resolv.conf to the target')
            shutil.copy('/etc/resolv.conf', '/mnt/target/etc')
        tpl = os.path.join(os.path.dirname(image), 'interfaces.tpl')
        if os.path.exists(tpl):
            logging.info('Generating network settings to target with image-supplied template')
            t = Template(open(tpl, 'r').read())
            with open("/mnt/target/etc/network/interfaces", 'w') as o:
                o.write(t.substitute(interfaces=utils.gen_network_config()))
        else:
            logging.warning("No interfaces template found in the image directory ; ignoring copy-network-settings")

    logging.info("Umounting system partition")
    um_res = partitions.umount_target()
    if not um_res:
        logging.error("Error while unmounting target")
        error = True
        return

    logging.info("System successfully installed")

if foreground:
    try:
        logging.info("Running Excito installer version %s" % (VERSION, ))
        error = False
        if config.has_option('general', 'image'):
            do_install()
        else:
            logging.info('No image in configuration. Exiting leaving the rescue system')
        # we never reboot neither loop with ip in foreground
        if error:
            led_error()
        else:
            led_rescue()
    except:
        logging.exception("Exception in the main install function :")
        led_error()
        sys.exit(1)
    finally:
        logging.shutdown()
    sys.exit(0)
else:
    with daemon.DaemonContext():
        write_pid()
        signal(SIGTERM, daemon_term_handler)

        logging.basicConfig(format='%(asctime)s.%(msecs)d - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %I:%M:%S',
                            level=logging.INFO, filename=LOG_FILE)

        logging.info("Running Excito installer version %s daemon process with pid %i" % (VERSION, os.getpid()))

        error = False

        if config.has_option('general', 'image'):
            try:
                do_install()
            except:
                logging.exception("Exception in the main install function :")
                error = True
            if not error and utils.getboolean_check_conf('general', 'reboot', 'true'):
                logging.info('install done; rebooting system')
                os.system('/sbin/reboot')
                logging.shutdown()
                os.unlink(PID_FILE)
                sys.exit(0)
            else:
                utils.loop_ip_forever(error)
        else:
            logging.info('No image in configuration. Exiting leaving the rescue system')
            utils.loop_ip_forever(error)
            os.unlink(PID_FILE)
