#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# This file is part of Karesansui.
#
# Copyright (C) 2009 HDE, 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; either version 2
# of the License, or (at your option) any later version.
#

import os
import sys
import re
import traceback
import time
import fcntl
import optparse

from installer.const import *
from installer.trans import _, N_
from installer.snack_ui import SnackUI

env = os._Environ(os.environ)
logfile = env.get("LOGFILE")
rpmsdir = env.get("RPMSDIR")
config = env.get('KARESANSUI_CONF')

class InstallError(Exception):
    def __init__(self, value):
        self.value = value
        write_log("%s\n" % value)
    def __str__(self):
        return repr(self.value)

def opt_parser():
    global logfile
    if logfile is None:
        if os.path.basename(sys.argv[0]) == "karesansui-initdb":
            logfile = DEFAULT_INITDB_LOG
        else:
            logfile = DEFAULT_INSTALL_LOG
    parser = optparse.OptionParser()
    parser.add_option("-l", "--logfile",
                action="store", dest="logfile", default=logfile,
                help=_("Log file location"))
    parser.add_option("-p", "--rpmsdir",
                action="store", dest="rpmsdir", default=os.path.dirname(__file__)+"/../RPMS",
                help=_("RPMS directory location"))
    parser.add_option("-c", "--config",
                action="store", dest="config", default=DEFAULT_KARESANSUI_CONF,
                help=_("Config file location"))
    parser.add_option("-i", "--ini",
                action="store", dest="ini", default=DEFAULT_INSTALL_INI,
                help=_("Installer ini file location"))
    parser.add_option("-w", "--passwd",
                action="store", dest="password1", default="",
                help=_("Password"))
    return parser

def precheck(opts):
    global logfile
    ret = True

    # hypervisor check
    if ret is True:
        if not os.path.exists("/sys/hypervisor/type"):
            print >>sys.stderr, _("ERROR: No hypervisor founded.")
            traceback.format_exc()
            ret = False

    # library check
    if ret is True:
        try:
            import rpm
            import snack
            import socket
            import urllib
        except ImportError, e:
            print >>sys.stderr, _("ERROR: There are not enough libraries. - %s") % ''.join(e.args)
            traceback.format_exc()
            ret = False

    # log file write check
    if ret is True:
        try:
            if os.path.exists(opts.logfile):
                logf = open(opts.logfile, "a")
            else:
                logf = open(opts.logfile, "w")
            logf.close()
            #if opts.logfile[0:5] != "/dev/":
            #    os.unlink(opts.logfile)
        except:
            print >>sys.stderr, _("ERROR: Can't open logfile %s. Please check whether directory has write permission.") % opts.logfile
            traceback.print_exc()
            ret = False

    # ini directory check
    if ret is True:
        if not os.path.exists(os.path.dirname(opts.ini)):
            print >>sys.stderr, _("ERROR: ini dir is not found '%s'.") % (os.path.dirname(opts.ini))
            ret = False

    # rpms directory check
    if ret is True:
        if not os.path.exists(opts.rpmsdir):
            print >>sys.stderr, _("ERROR: rpms dir is not found '%s'.") % (opts.rpmsdir)
            ret = False

    # require file check
    if ret is True:
        require_rpms = ["karesansui","pysilhouette"]
        for req in require_rpms:
            exist = False
            rpm_regex = re.compile("^%s\-.*\.rpm$" % req)
            for name in os.listdir(opts.rpmsdir):
                if rpm_regex.search(name):
                    exist = True
            if exist is False:
                print >>sys.stderr, _("ERROR: '%s' rpm file is not found in '%s'.") % (req,opts.rpmsdir)
                ret = False

    # distribution check
    if ret is True:
        from installer.utils import get_distrubution
        type,version = get_distrubution()

        supported_os = False
        for _d,_v in SUPPORTED_DISTROS:
            if type == "%s-release" % _d and version[0:len(_v)] == _v:
                supported_os = True
                break
        if supported_os is False:
            print >>sys.stderr, _("ERROR: Your distribution is not supported by Karesansui.")
            ret = False

    # architecture check (now supported arch is x86 only)
    if ret is True:
        import platform
        arch = platform.processor()
        x86_regex = re.compile("^i[3456]86$")
        if x86_regex.search(arch):
            pass
        else:
            print >>sys.stderr, _("ERROR: Processor '%s' is not supported by Karesansui.") % arch
            ret = False

    return ret

def process_rebuild_package(opts,p_callback=None):
    write_log(_("Entering '%s'") % sys._getframe(0).f_code.co_name)

    global logfile
    from installer.rpmlib import RpmLib

    rpmlib = RpmLib(p_callback)
    srpmsdir = os.path.realpath("%s/../SRPMS" % opts.rpmsdir)
    if os.path.exists(srpmsdir):
        for name in os.listdir(srpmsdir):
            for _pkg,_linenum in BUILD_PKGS_LINENUMS.iteritems():
                rpm_regex = re.compile("%s-.*\.src\.rpm$" % _pkg)
                if rpm_regex.match(name):
                    _srpmpath = os.path.join(srpmsdir, name)
                    #import pdb; pdb.set_trace()
                    if rpmlib.compareVersion(_srpmpath) < 0:
                        rpmlib.doRebuildSRPM(_srpmpath)
    rpmlib.__del__
    del rpmlib

    time.sleep(1)
    write_log(_("Leaving '%s'") % sys._getframe(0).f_code.co_name)
    return True

def process_install_package(opts,p_callback=None):
    write_log(_("Entering '%s'") % sys._getframe(0).f_code.co_name)

    global logfile
    from installer.utils import execute_command
    from installer.rpmlib import RpmLib

    rpmlib = RpmLib(p_callback)

    added = []
    karesansui_files = []
    other_files = []

    #for _path in [opts.rpmsdir]:
    for _path in [GENERATE_RPMDIR,opts.rpmsdir]:
        if os.path.exists(_path):
            for name in os.listdir(_path):
                for _arch in rpmlib.detectArchOrder():
                    rpm_regex = re.compile("\.%s\.rpm$" % _arch)
                    if rpm_regex.search(name):
                        _rpmpath = os.path.join(_path, name)
                        pkg_name = rpmlib.getFileInfo(_rpmpath)["name"]
                        if not pkg_name in added:
                            added.append(pkg_name)
                            if name[0:10] == "karesansui":
                                karesansui_files.append(_rpmpath)
                            else:
                                other_files.append(_rpmpath)

    rpmlib.doInstallAll(other_files)
    rpmlib.doInstallAll(karesansui_files)
    rpmlib.__del__
    del rpmlib

    time.sleep(1)
    write_log(_("Leaving '%s'") % sys._getframe(0).f_code.co_name)
    return True

def process_create_admin(opts,p_callback=None):
    write_log(_("Entering '%s'") % sys._getframe(0).f_code.co_name)

    global logfile
    from installer.utils import r_chmod, r_chgrp, sh_config_read, sh_config_write

    cnt = 0
    if p_callback: p_callback.start(text=_("Creating administrator..."), size=2)
    if p_callback: p_callback.update(cnt)
    cnt = cnt + 1

    """ PHASE 1: prepare """
    if opts.config is not None and os.path.exists(opts.config):
        search_path = sh_config_read(opts.config)["application.search.path"]
        for y in [x.strip() for x in search_path.split(',') if x]:
            if (y in sys.path) is False: sys.path.insert(0, y)

    try:
        import karesansui
        import karesansui.lib.file.k2v
        from karesansui.lib.const import KARESANSUI_GROUP
    except ImportError, e:
        traceback.format_exc()
        raise InstallError(_("ERROR: There are not enough libraries. - %s") % ''.join(e.args))

    karesansui.config = karesansui.lib.file.k2v.K2V(os.path.abspath(opts.config)).read()
    if not karesansui.config:
        traceback.format_exc()
        raise InstallError(_("ERROR: Failed to load configuration file. - %s") % opts.config)

    try:
        import sqlalchemy
        import pysilhouette
    except ImportError, e:
        traceback.format_exc()
        raise InstallError(_("ERROR: There are not enough libraries. - %s") % ''.join(e.args))

    from pysilhouette.prep import readconf
    karesansui.sheconf = readconf(karesansui.config['pysilhouette.conf.path'])

    if karesansui.sheconf is None:
        traceback.format_exc()
        raise InstallError(_("ERROR: Failed to load configuration file. - %s") % karesansui.config['pysilhouette.conf.path'])

    """ PHASE 2: initialize database """
    import installer.database
    dbtype, value = karesansui.config['database.bind'].split(':',1)
    installer.database.import_database_module(dbtype)
    installer.database.start(opts)

    try:
        database_bind = karesansui.config['database.bind']
        if database_bind[0:11] == "sqlite:////":
            sqlitedb_file = database_bind[10:]
            if os.path.exists(sqlitedb_file) and os.path.isfile(sqlitedb_file):
                r_chmod(os.path.dirname(sqlitedb_file),"g+rw")
                r_chmod(os.path.dirname(sqlitedb_file),"o-rwx")
                r_chgrp(os.path.dirname(sqlitedb_file),KARESANSUI_GROUP)
    except:
        raise InstallError(_("ERROR: parameter '%s' is not found in %s.") % ("database.bind",opts.config,))

    try:
        pysilhouette_conf = karesansui.config['pysilhouette.conf.path']
        database_url = sh_config_read(pysilhouette_conf)["database.url"]
        if database_url[0:11] == "sqlite:////":
            sqlitedb_file = database_url[10:]
            if os.path.exists(sqlitedb_file) and os.path.isfile(sqlitedb_file):
                r_chmod(os.path.dirname(sqlitedb_file),"g+rw")
                r_chmod(os.path.dirname(sqlitedb_file),"o-rwx")
                r_chgrp(os.path.dirname(sqlitedb_file),KARESANSUI_GROUP)
    except:
        raise InstallError(_("ERROR: parameter '%s' is not found in %s.") % ("pysilhouette.conf.path",opts.config,))

    if p_callback: p_callback.update(cnt)
    cnt = cnt + 1

    time.sleep(1)
    write_log(_("Leaving '%s'") % sys._getframe(0).f_code.co_name)
    return True

def process_write_config(opts,p_callback=None):
    write_log(_("Entering '%s'") % sys._getframe(0).f_code.co_name)

    global logfile
    from installer.utils import sh_config_write, sh_config_read

    cnt = 0
    if p_callback: p_callback.start(text=_("Writing configuration files..."), size=3)
    if p_callback: p_callback.update(cnt)
    cnt = cnt + 1

    if os.path.exists(opts.config):
        res = {}
        for k,v in sh_config_read(opts.config).iteritems():
            if k == "pysilhouette.conf.path":
                pysilhouette_conf_path = v
            if k == "database.bind":
                v = opts.dbbind
            if k == "application.uniqkey":
                v = opts.uuid
            res[k] = v
        try:
            res["application.uniqkey"]
        except:
            res["application.uniqkey"] = opts.uuid
        sh_config_write(opts.config,res)

    if p_callback: p_callback.update(cnt)
    cnt = cnt + 1

    opts.dbinit = 0
    sh_config_write(opts.ini,opts)

    if p_callback: p_callback.update(cnt)
    cnt = cnt + 1

    if os.path.exists(SILHOUETTE_SYSCONFIG):
        res = {}
        for k,v in sh_config_read(SILHOUETTE_SYSCONFIG).iteritems():
            if k == "PYTHON_SEARCH_PATH":
                paths = v.strip("\"'").split(":")
                if not VENDOR_PYTHONPATH in paths:
                    paths.append(VENDOR_PYTHONPATH)
                if not KARESANSUI_PYTHONPATH in paths:
                    paths.append(KARESANSUI_PYTHONPATH)
                v = "\"%s\"" % ":".join(paths)
            res[k] = v
        sh_config_write(SILHOUETTE_SYSCONFIG,res)


    if pysilhouette_conf_path is not None and os.path.exists(pysilhouette_conf_path):
        res = {}
        for k,v in sh_config_read(pysilhouette_conf_path).iteritems():
            if k == "env.uniqkey" and ( v == "" or v == "aaaaaaaa-0000-0000-0000-aaaaaaaaaaaa" ):
                from installer.utils import generate_uuid, string_from_uuid
                v = string_from_uuid(generate_uuid())
            res[k] = v
        sh_config_write(pysilhouette_conf_path,res)

    if p_callback: p_callback.update(cnt)
    cnt = cnt + 1

    time.sleep(1)
    write_log(_("Leaving '%s'") % sys._getframe(0).f_code.co_name)
    return True

def process_start_service(opts,p_callback=None):
    write_log(_("Entering '%s'") % sys._getframe(0).f_code.co_name)

    global logfile
    from installer.utils import run_chkconfig, run_service, execute_command

    cnt = 0
    if p_callback: p_callback.start(text=_("Enabling services ..."), size=len(REQUIRE_SERVICES)+1)
    if p_callback: p_callback.update(cnt)
    cnt = cnt + 1

    run_chkconfig("libvirtd","off")
    run_service("libvirtd","stop")
    if p_callback: p_callback.update(cnt)
    cnt = cnt + 1
    for _service in REQUIRE_SERVICES:
        run_chkconfig(_service,"on")
        run_service(_service,"restart")
        if p_callback: p_callback.update(cnt)
        cnt = cnt + 1

    for _port in [80, 443]:
        cmd = "%s -I INPUT 1 -p tcp --dport %d -j ACCEPT" % (COMMAND_IPTABLES,_port)
        (ret,res) = execute_command(cmd.split(' '))

    time.sleep(1)
    write_log(_("Leaving '%s'") % sys._getframe(0).f_code.co_name)
    return True

def run(opts, **kwargs):
    p_callback = kwargs.get('progress_callback', None)
    dbinit = opts.dbinit

    if opts.action & FLAG_RPM_REBUILD:
        process_rebuild_package(opts,p_callback)

    if opts.action & FLAG_RPM_INST:
        process_install_package(opts,p_callback)

    if opts.action & FLAG_WRITE_CONF:
        process_write_config(opts,p_callback)

    if dbinit is True:
        if opts.action & FLAG_CREATE_ADM:
            process_create_admin(opts,p_callback)

    if opts.is_installed is False:
        if opts.action & FLAG_START_SERV:
            process_start_service(opts,p_callback)

    return True

def write_log(message):
    global logfile

    ret = True
    try:
        if os.path.exists(logfile):
            fp = open(logfile,"a")
        else:
            fp = open(logfile,"w")
        fcntl.lockf(fp.fileno(), fcntl.LOCK_EX)
        fp.write("%s\n" % (message))
        fcntl.lockf(fp.fileno(), fcntl.LOCK_UN)
        fp.close()
        os.chmod(logfile, 0600)
    except TypeError, e:
        pass
    except IOError, e:
        raise "%s" % e
        ret = False
    return ret

def main(action=FLAG_DO_ALL):
    if os.getuid() != 0:
        print _("ERROR: This program must be executed by super user.")
        sys.exit()

    parser = opt_parser()
    (opts,args) = parser.parse_args()

    if opts.logfile is not None:
        logfile = opts.logfile
    opts.logfile = logfile
    os.environ['LOGFILE'] = opts.logfile

    from installer.utils import sh_config_read
    if os.path.exists(opts.ini):
        for k,v in sh_config_read(opts.ini).iteritems():
            setattr(opts,k,v)
    opts.logfile = os.environ['LOGFILE']

    if rpmsdir is not None:
        opts.rpmsdir = rpmsdir

    if config is not None:
        opts.config = config
    os.environ['KARESANSUI_CONF'] = opts.config

    if os.path.exists(opts.config):
        opts.dbbind = sh_config_read(opts.config)["database.bind"]

    opts.uuid = ""
    if os.path.exists(opts.config):
        try:
            opts.uuid = sh_config_read(opts.config)["application.uniqkey"]
        except:
            pass
    if opts.uuid == "":
        from installer.utils import generate_uuid, string_from_uuid
        opts.uuid = string_from_uuid(generate_uuid())

    if opts.password1 != "":
        opts.password2 = opts.password1

    from installer.utils import is_installed
    opts.is_installed = is_installed("karesansui")

    if NEWT_MONO is True:
        os.environ['NEWT_MONO'] = "1"

    opts.action = action

    #print opts
    #opts.lang = "ja_JP"
    #opts.mailaddr = "ja@localhost"
    #opts.password1 = "password"
    #process_create_admin(opts)
    #process_start_service(opts)
    #process_install_package(opts)
    #sys.exit()

    """ Pre checker
    """
    if precheck(opts) is False:
        return -1

    """ Start logging
    """
    #if opts.logfile[0:5] != "/dev/" and os.path.exists(opts.logfile):
    #    os.unlink(opts.logfile)

    write_log("""
@====================================================================@
@ %s %s        @
@ %s           Start time: %s  @
@====================================================================@

Action: %d
 
""" % (TITLE,VERSION,os.path.basename(sys.argv[0]),time.ctime(),opts.action,))

    """ Run Run Run!!
    """
    return SnackUI(opts).run()

if __name__ == '__main__':
    sys.exit(main())
