#!/usr/bin/env python

import os
import errno
import shutil
import glob
import sys
import tempfile
import inspect
import time

try:
    sysname, nodename, x, x, machine = os.uname()
except:
    import platform
    sysname, nodename, x, x, machine, x = platform.uname()

p0755 = int("0755", 8)
p0700 = int("0700", 8)
p0644 = int("0644", 8)

postinstall_d = sys.path[0]
if '/catalog/' in postinstall_d:
    # hpux packaging subsystem executes the postinstall from dir
    # /var/tmp/XXXXXXXXX/catalog/opensvc/commands/
    postinstall_d = "/usr/share/opensvc/bin"
    lsb = True
elif postinstall_d == "/usr/share/opensvc/bin":
    lsb = True
else:
    # windows install or unix execution from a non-lsb tree (ex: /opt/opensvc/)
    lsb = False

if lsb:
    pathsbin = "/usr/bin"
    pathsvc = None
    pathetc = "/etc/opensvc"
    pathvar = "/var/lib/opensvc"
    pathlck = '/var/lib/opensvc/lock'
    pathtmp = "/var/tmp/opensvc"
    pathlog = "/var/log/opensvc"
    pathbin = "/usr/share/opensvc/bin"
    pathlib = "/usr/share/opensvc/lib"
    pathini = "/usr/share/opensvc/bin/init"
    pathusr = None
else:
    pathsbin = postinstall_d
    pathsvc = os.path.realpath(os.path.join(pathsbin, '..'))
    pathetc = os.path.join(pathsvc, 'etc')
    pathvar = os.path.join(pathsvc, 'var')
    pathlck = os.path.join(pathvar, 'lock')
    pathtmp = os.path.join(pathsvc, 'tmp')
    pathlog = os.path.join(pathsvc, 'log')
    pathbin = postinstall_d
    pathlib = os.path.join(pathsvc, 'lib')
    pathini = os.path.join(pathsvc, 'bin', 'init')
    pathusr = os.path.join(pathsvc, 'usr')

def make_sure_path_exists(path):
    if os.path.exists(path):
        return
    os.makedirs(path, p0755)

def logit(msg, stdout=False, stderr=False):
    curframe = inspect.currentframe()
    calframe = inspect.getouterframes(curframe, 2)
    try:
        import datetime
        timestamp = str(datetime.datetime.now())
    except:
        timestamp = '?'

    osvlog = os.path.join(pathlog, 'postinstall.log')
    content = '['+calframe[1][3]+']['+timestamp+'] '+msg
    f = open(osvlog, 'a')
    f.write(content+'\n')
    f.close()
    if stdout:
        sys.stdout.write(msg+"\n")
    if stderr:
        sys.stderr.write("error ==> %s\n" % (msg))

def osrun(cmd, stdout=False, stderr=False):
    ret = None
    if sysname == 'Windows':
        logit("cmd [%s]"%cmd, stdout, stderr)
        os.system(cmd)
    else:
        ret = os.WEXITSTATUS(os.system(cmd))
        logit("cmd [%s]  ret[%s]"%(cmd, ret), stdout, stderr)
    if ret is not None:
        return ret

make_sure_path_exists(pathlog)

if sysname == 'SunOS':
    osvcfmri = 'svc:/site/application/opensvc:boot'
    cmd = ['svcs', '-vl', osvcfmri, '2>/dev/null']
    ret = osrun(' '.join(cmd))
    if ret == 0:
        # Solaris install from IPS package with standard FMRI
        logit("Solaris postinstall check for first run", stdout=False)
        cmd = ['svcprop', '-p', 'boot/firstrun', osvcfmri, '|', 'grep', 'done']
        ret = osrun(' '.join(cmd))
        if ret == 0:
            logit("Solaris postinstall already executed. Exiting.", stdout=False)
            sys.exit(0)

logit("\nStarting OpenSVC postinstall\n", stdout=True)

SolarisRootRelocate = False
if sysname == 'SunOS' and "PKG_INSTALL_ROOT" in os.environ and os.environ['PKG_INSTALL_ROOT'] != '/':
    logit("SunOS PKG_INSTALL_ROOT <%s>"%(os.environ['PKG_INSTALL_ROOT']))
    SolarisRootRelocate = True

variables = {
    "pathsvc": pathsvc,
    "pathsbin": pathsbin,
    "pathbin": pathbin,
    "pathetc": pathetc,
    "pathvar": pathvar,
    "pathtmp": pathtmp,
    "pathlib": pathlib,
    "pathlog": pathlog,
    "pathusr": pathusr,
    "SolarisRootRelocate": SolarisRootRelocate
}
for key in variables:
    logit("var %s <%s>"%(key, variables[key]))

def install_cron():
    logit("begin")
    if sysname == 'Windows':
        logit("windows not applicable")
        return
    else:
        return install_cron_unix()

def install_winservice():
    if sysname != 'Windows':
        return
    logit("begin")
    logit("install OpenSVC agent winservice", stdout=True)
    schedstop()
    time.sleep(3)
    schedremove()
    schedinstall()
    schedstart()

def schedremove():
    logit("begin")
    cmd = ' remove'
    schedcmd(cmd)

def schedstart():
    logit("begin")
    cmd = ' start'
    schedcmd(cmd)

def schedstop():
    logit("begin")
    cmd = ' stop'
    schedcmd(cmd)

def schedinstall():
    logit("begin")
    cmd = ''
    cmd += ' --username LocalSystem'
    cmd += ' --startup auto'
    cmd += ' install\n'
    schedcmd(cmd)

def schedcmd(_cmd):
    logit("begin")
    logit("_cmd %s"%_cmd)
    rc = '"'+sys.executable+'" "'+os.path.join(pathlib, 'osvcd_winservice.py')+'"'
    cmd = "@echo off\n"
    cmd += rc
    cmd += _cmd
    fd, fname = tempfile.mkstemp(dir=pathtmp, suffix='.cmd')
    f = os.fdopen(fd, 'w')
    f.write(cmd)
    f.close()
    import subprocess
    subprocess.call([fname])
    os.unlink(fname)

def save_file(infile):
    logit("begin")
    logit("infile <%s>"%infile)
    if not os.path.exists(infile):
        return True
    try:
        import datetime
        timestamp = str(datetime.datetime.now())
        tmp = timestamp.replace(" ", ".")
        ts = tmp.replace(":", ".")
    except:
        ts = 'opensvc.postinstall'
    ofname = os.path.basename(infile)
    logit("ofname <%s>"%ofname)
    nfname = ofname + '.crontab.' + ts
    logit("nfname <%s>"%nfname)
    outfile = os.path.join(os.sep, pathtmp, nfname)
    logit("outfile <%s>"%outfile)

    logit("saving file <%s> to <%s>"%(infile, outfile), stdout=True)
    try:
        shutil.copyfile(infile, outfile)
    except:
        import traceback
        traceback.print_exc()
        logit("error while trying to save file <%s> to <%s>"%(infile, outfile), stderr=True)
        return False
    return True


def install_cron_unix():
    logit("begin")
    """install opensvc cron jobs
    """

    remove_entries = [
        'bin/nodemgr schedulers',
        'bin/nodemgr compliance check',
        'bin/svcmon ',
        'bin/cron/opensvc',
        'svcmgr resource monitor',
        'svcmgr resource_monitor',
        'nodemgr cron',
        'perfagt.'+sysname,
    ]

    purge = []
    root_crontab = False

    """ order of preference
    """
    if sysname == 'SunOS':
        if SolarisRootRelocate is True:
            suncron = os.environ["PKG_INSTALL_ROOT"] + '/var/spool/cron/crontabs/root'
            root_crontab_locs = suncron
        else:
            root_crontab_locs = ['/var/spool/cron/crontabs/root']
    else:
        root_crontab_locs = [
            '/etc/cron.d/opensvc',
            '/var/spool/cron/crontabs/root',
            '/var/spool/cron/root',
            '/var/cron/tabs/root',
            '/usr/lib/cron/tabs/root',
        ]
    for loc in root_crontab_locs:
        logit("looping crontab location <%s>"%loc)
        if os.path.exists(os.path.dirname(loc)):
            if not root_crontab:
                root_crontab = loc
                logit("identifying <%s> as root crontab"%root_crontab)
            elif os.path.exists(loc):
                logit("adding <%s> to purge table"%loc)
                purge.append(loc)

    if not root_crontab:
        logit("no root crontab found in usual locations <%s>"%str(root_crontab_locs), stderr=True)
        return False

    new = False
    if os.path.exists(root_crontab):
        try:
            f = open(root_crontab, 'r')
            new = f.readlines()
            f.close()
            logit("loaded crontab <%s> content <%s>"%(root_crontab, new))
        except:
            f.close()
            import traceback
            traceback.print_exc()

    if not new:
        logit("empty crontab found. skipping purge.", stderr=False)
        if os.path.exists(root_crontab) and os.stat(root_crontab).st_size == 0:
            logit("removing crontab %s"%root_crontab)
            os.unlink(root_crontab)
        return False

    i = -1
    for line in new:
        i += 1
        for re in remove_entries:
            logit("looping re <%s>"%re)
            if line.find(re) != -1:
                logit("delete line <%s> from <%s>"%(re, root_crontab))
                del new[i]

    logit("saving crontab <%s>"%root_crontab)
    try:
        save_file(root_crontab)
    except:
        logit('Error while trying to backup crontab <%s>. skipping crontab update'%(root_crontab), stderr=True)
        return False

    logit("updating crontab <%s> with content <%s>"%(root_crontab, new))
    try:
        f = open(root_crontab, 'w')
        f.write(''.join(new))
        f.close()
    except:
        logit("error while trying to update crontab %s"%root_crontab, stderr=True)
        f.close()
        import traceback
        traceback.print_exc()

    """ Activate changes (actually only needed on HP-UX)
    """
    if sysname in ("HP-UX", "SunOS") and root_crontab.find('/var/spool/') != -1:
        logit("crontab activation requested")
        cmd = ['crontab', root_crontab]
        ret = osrun(' '.join(cmd))

    for loc in purge:
        try:
            f = open(loc, 'r')
            new = [line for line in f.readlines() if line.find('opensvc.daily') == -1 and line.find('svcmon --updatedb') == -1]
            f.close()
            f = open(loc, 'w')
            f.write(''.join(new))
            f.close()
        except:
            f.close()
            import traceback
            traceback.print_exc()

    """ Clean up old standard file locations
    """
    for f in ['/etc/cron.daily/opensvc', '/etc/cron.daily/opensvc.daily']:
        if os.path.exists(f):
            logit("removing %s"%f)
            os.unlink(f)


def activate_chkconfig(svc):
    logit("begin")
    cmd = ['chkconfig', '--add', svc]
    ret = osrun(' '.join(cmd))
    if ret > 0:
        return False
    return True

def activate_systemd(launcher):
    logit("begin")
    systemdsvcs = ['opensvc-agent.service', 'opensvc-services.service']

    for systemdsvc in systemdsvcs:
        _activate_systemd(systemdsvc)

def _activate_systemd(systemdsvc):
    # remove unit file from /etc/systemd/system
    olddst = os.path.join('/etc/systemd/system/', systemdsvc)
    if os.path.exists(olddst):
        logit("found unit file in /etc/systemd/system", stdout=False)
        logit("disabling old systemd unit file", stdout=False)
        cmd = ['systemctl', '-q', 'disable', systemdsvc]
        ret = osrun(' '.join(cmd))
        if ret > 0:
            logit("issue met while trying to disable old %s systemd unit file"%systemdsvc, stderr=True)
        logit("removing old systemd unit file", stdout=False)
        cmd = ['rm', '-f', olddst]
        ret = osrun(' '.join(cmd))
        if ret > 0:
            logit("issue met while trying to remove old %s systemd unit file"%systemdsvc, stderr=True)

    systemd_unit_path = [
        '/usr/lib/systemd/system/',
        '/lib/systemd/system/',
    ]

    installed = False
    for path in systemd_unit_path:
        if os.path.exists(os.path.dirname(path)):
            # populate systemd tree with opensvc unit file
            src = os.path.join(pathini, "systemd."+systemdsvc)
            dst = os.path.join(path, systemdsvc)
            logit("installing %s systemd unit file in %s"%(systemdsvc, path), stdout=True)
            try:
                shutil.copyfile(src, dst)
                os.chmod(dst, p0644)
                installed = True
            except Exception as exc:
                logit("issue met while trying to install systemd unit file: %s"%str(exc), stderr=True)

    if installed is not True:
        logit("issue met while trying to identify suitable systemd unit file location", stderr=True)

    # reset paths in ExecStart and ExecStop
    osrun("sed -i 's@/usr/share/opensvc/bin@"+pathbin+"@' "+dst)

    # reload systemd configuration
    logit("reloading systemd configuration", stdout=True)
    cmd = ['systemctl', '-q', 'daemon-reload']
    ret = osrun(' '.join(cmd))
    if ret > 0:
        logit("issue met during systemctl reload", stderr=True)

    # enable opensvc agent startup through systemd
    logit("enabling systemd configuration")
    cmd = ['systemctl', '-q', 'enable', systemdsvc]
    ret = osrun(' '.join(cmd))
    if ret > 0:
        logit("issue met during systemctl enable", stderr=True)


def systemd_mgmt():
    logit("begin")
    cmd = ['systemctl', '--version', '>>/dev/null', '2>&1']
    ret = osrun(' '.join(cmd))
    if ret > 0:
        return False
    return True

def activate_ovm(launcher):
    logit("begin")
    activate_chkconfig('zopensvc')

def activate_redhat(launcher):
    logit("begin")
    activate_chkconfig('opensvc')

def activate_debian(launcher):
    logit("begin")
    cmd = ['update-rc.d', '-f', 'opensvc', 'remove']
    ret = osrun(' '.join(cmd))
    if ret > 0:
        logit("issue met while trying to remove opensvc rc launchers", stderr=True)
        return False
    cmd = ['update-rc.d', 'opensvc', 'defaults']
    ret = osrun(' '.join(cmd))
    if ret > 0:
        logit("issue met while trying to install opensvc rc launchers", stderr=True)
        return False
    return True

def activate_hpux(launcher):
    logit("begin")
    rc = "/sbin/init.d/opensvc"
    links = ["/sbin/rc1.d/K010opensvc", "/sbin/rc2.d/K010opensvc", "/sbin/rc3.d/S990opensvc"]
    if os.path.exists("/sbin/rc2.d/S990opensvc"):
        logit("removing /sbin/rc2.d/S990opensvc")
        os.unlink("/sbin/rc2.d/S990opensvc")
    for l in links:
        if not os.path.islink(l):
            if os.path.exists(l):
                logit("removing %s"%l)
                os.unlink(l)
            logit("create link %s -> %s"%(l, rc))
            os.symlink(rc, l)
    try:
        f = open("/etc/rc.config.d/opensvc", "w")
        f.write("RUN_OPENSVC=1\n")
        f.close()
    except:
        logit("issue met while trying to install rc.config.d opensvc file", stderr=True)
        f.close()
        import traceback
        traceback.print_exc()
    return True

def activate_AIX(launcher):
    logit("begin")
    rc = "/etc/rc.d/init.d/opensvc"
    links = ["/etc/rc.d/rc2.d/S990opensvc"]
    for l in links:
        if not os.path.islink(l):
            if os.path.exists(l):
                logit("removing %s"%l)
                os.unlink(l)
            logit("create link %s -> %s"%(l, rc))
            os.symlink(rc, l)
    return True

def activate_OSF1(launcher):
    rc = "/sbin/init.d/opensvc"
    links = ["/sbin/rc0.d/K010opensvc", "/sbin/rc2.d/K010opensvc", "/sbin/rc3.d/S990opensvc"]
    for l in links:
        if not os.path.islink(l):
            if os.path.exists(l):
                logit("removing %s"%l)
                os.unlink(l)
            logit("symlinking %s and %s"%(rc, l))
            os.symlink(rc, l)
            return True

def activate_SunOS(launcher):
    logit("begin")
    if SolarisRootRelocate is True:
        rc = "/etc/init.d/opensvc"
        links = [os.environ["PKG_INSTALL_ROOT"] + "/etc/rc0.d/K00opensvc", os.environ["PKG_INSTALL_ROOT"] + "/etc/rc3.d/S99opensvc"]
    else:
        rc = "/etc/init.d/opensvc"
        links = ["/etc/rc0.d/K00opensvc", "/etc/rc3.d/S99opensvc"]
    logit("rc <%s>"%rc)
    for l in links:
        logit("link <%s>"%l)
        if not os.path.islink(l):
            if os.path.exists(l):
                logit("removing %s"%l)
                os.unlink(l)
            logit("symlinking %s and %s"%(rc, l))
            os.symlink(rc, l)
    return True

def activate_FreeBSD(launcher):
    logit("begin")
    return True

def activate_Darwin(launcher):
    logit("begin")
    return True

def update_file(filename, srctext, replacetext):
    logit("begin")
    """ replace into filename srctext by replacetext
    """
    import fileinput
    for line in fileinput.input(filename, inplace=1):
        if line.rstrip('\n') == srctext.rstrip('\n'):
            line = replacetext
        msg = line.rstrip('\n')
        logit(msg, stdout=True)
    fileinput.close()

def install_params(path2file):
    logit("begin")
    """ install template file with tunable variables
    """
    if os.path.exists(path2file):
        logit("file %s already present"%path2file)
        return

    try:
        f = open(path2file, "w")
    except:
        import traceback
        traceback.print_exc()
    else:
        logit("writing new <%s>"%path2file)
        f.write("# OpenSVC startup and wrapper configuration file\n")
        f.write("#\n")
        f.write("# You may need to adapt parameters to fit your environment\n")
        f.write("# This file is not modified during software upgrades\n")
        f.write("# If empty, default settings are used in the init script\n\n\n")
        f.write("#\n")
        f.write("\n")
        f.write("#\n")
        f.write("# Wrapper configuration\n")
        f.write("#\n")
        f.write("#OSVC_ROOT_PATH=/opt/opensvc\n")
        f.write("#OSVC_PYTHON=python\n")
        f.write("#LD_LIBRARY_PATH=\n")
        f.write("#LD_PRELOAD=\n")
        f.write("\n")
        f.close()

def install_rc():
    logit("begin")
    """install startup script
    """
    params = None
    copyrc = True

    if os.path.exists('/etc/debian_version'):
        rc = '/etc/init.d/opensvc'
        params = '/etc/default/opensvc'
        src = os.path.join(pathini, 'opensvc.init.debian')
        if systemd_mgmt():
            logit("debian with systemd")
            copyrc = False
            activate = activate_systemd
        else:
            logit("debian with update-rc.d (rely on insserv)")
            activate = activate_debian
    elif os.path.exists('/etc/arch-release'):
        rc = '/etc/init.d/opensvc'
        params = '/etc/default/opensvc'
        src = os.path.join(pathini, 'opensvc.init.debian')
        if systemd_mgmt():
            logit("arch with systemd")
            copyrc = False
            activate = activate_systemd
    elif os.path.exists('/etc/SuSE-release'):
        rc = '/etc/init.d/opensvc'
        params = '/etc/sysconfig/opensvc'
        src = os.path.join(pathini, 'opensvc.init.suse')
        if systemd_mgmt():
            logit("SuSE with systemd")
            copyrc = False
            activate = activate_systemd
        else:
            logit("SuSE with chkconfig (rely on insserv)")
            activate = activate_redhat
    elif os.path.exists('/etc/redhat-release'):
        params = '/etc/sysconfig/opensvc'
        src = os.path.join(pathini, 'opensvc.init.redhat')
        try:
            f = open('/etc/redhat-release', 'r')
            buff = f.read()
            f.close()
        except:
            buff = ""
        if buff.find('Oracle VM server') != -1:
            rc = '/etc/init.d/zopensvc'
            activate = activate_ovm
        else:
            rc = '/etc/init.d/opensvc'
            if systemd_mgmt():
                logit("Red Hat with systemd")
                copyrc = False
                activate = activate_systemd
            else:
                logit("Red Hat with chkconfig (rely on insserv)")
                activate = activate_redhat
    elif sysname == "HP-UX":
        rc = '/sbin/init.d/opensvc'
        src = os.path.join(pathini, 'opensvc.init.hpux')
        activate = activate_hpux
    elif sysname == "SunOS":
        if SolarisRootRelocate is True:
            rc = os.environ["PKG_INSTALL_ROOT"] + '/etc/init.d/opensvc'
            params = os.environ["PKG_INSTALL_ROOT"] + '/etc/default/opensvc'
            src = os.environ["PKG_INSTALL_ROOT"] + os.path.join(pathini, 'opensvc.init.SunOS')
        else:
            rc = '/etc/init.d/opensvc'
            params = '/etc/default/opensvc'
            src = os.path.join(pathini, 'opensvc.init.SunOS')
        activate = activate_SunOS
    elif sysname == "OSF1":
        rc = '/sbin/init.d/opensvc'
        src = os.path.join(pathini, 'opensvc.init.OSF1')
        activate = activate_OSF1
    elif sysname == "FreeBSD":
        rc = '/etc/rc.d/opensvc'
        params = '/etc/defaults/opensvc'
        src = os.path.join(pathini, 'opensvc.init.FreeBSD')
        activate = activate_FreeBSD
    elif sysname == "AIX":
        rc = '/etc/rc.d/init.d/opensvc'
        src = os.path.join(pathini, 'opensvc.init.AIX')
        activate = activate_AIX
    elif sysname == "Darwin":
        rc = '/Library/LaunchDaemons/com.opensvc.svcmgr.plist'
        params = '/etc/defaults/opensvc'
        src = os.path.join(pathini, 'darwin.com.opensvc.svcmgr.plist')
        activate = activate_Darwin
    elif sysname == 'Windows':
        return False
    else:
        logit("could not select an init script: unsupported operating system", stderr=True)
        return False

    if os.path.islink(rc):
        logit("removing link %s"%rc)
        os.unlink(rc)

    if copyrc:
        logit("copying src launcher script to rc")
        shutil.copyfile(src, rc)
        os.chmod(rc, p0755)

    if params is not None and not os.path.exists(params):
        logit("installing default parameters file")
        install_params(params)

    activate(src)

def gen_keys():
    logit("begin")
    if sysname == 'Windows':
        return
    home = os.path.expanduser("~root")
    logit("home <%s>"%home)
    if SolarisRootRelocate is True:
        home = os.environ['PKG_INSTALL_ROOT'] + os.path.expanduser("~root")
        logit("SunOS and relocatable install home is now <%s>"%home)
    sshhome = os.path.join(home, ".ssh")
    logit("sshhome <%s>"%sshhome)
    if not os.path.exists(sshhome):
        logit("create dir %s"%sshhome, stdout=True)
        os.makedirs(sshhome, p0700)
    priv = os.path.join(sshhome, "id_rsa")
    pub = os.path.join(sshhome, "id_rsa.pub")
    if os.path.exists(pub) or os.path.exists(priv):
        logit("either %s or %s already exist"%(pub, priv))
        return
    cmd = ['ssh-keygen', '-t', 'rsa', '-b', '2048', '-P', '""', '-f', priv]
    try:
        ret = osrun(' '.join(cmd))
    except:
        logit("Error while trying to generate ssh keys")

def missing_dir(pathd):
    logit("begin")
    if not os.path.exists(pathd):
        logit("create dir %s"%pathd, stdout=True)
        os.makedirs(pathd, p0755)

def missing_dirs():
    logit("begin")
    missing_dir(pathlog)
    missing_dir(pathtmp)
    missing_dir(pathvar)
    missing_dir(pathetc)
    missing_dir(pathlck)

def convert_svclinks():
    logit("begin")
    missing_dir(pathetc)
    svcmgr = os.path.join(pathsbin, 'svcmgr')
    if not os.path.exists(svcmgr):
        logit("%s does not exist"%svcmgr)
        return 1
    rcService = os.path.realpath(os.path.join(pathbin, 'rcService'))
    if not os.path.exists(rcService):
        logit("%s does not exist"%rcService)
        return 1
    for fname in os.listdir(pathetc):
        fpath = os.path.join(pathetc, fname)
        if not os.path.islink(fpath):
            logit("%s is not a symlink"%fpath)
            continue
        rpath = os.path.realpath(fpath)
        if rpath != rcService:
            logit("%s != %s"%(rpath, rcService))
            continue
        logit("removing %s"%fpath)
        os.unlink(fpath)
        logit("create link %s -> %s"%(fpath, svcmgr))
        os.symlink(svcmgr, fpath)

def move_env_to_conf():
    for fpath in glob.glob(os.path.join(pathetc, "*.env")):
        svcname = os.path.basename(fpath)[:-4]
        new_basename = svcname+".conf"
        new_fpath = os.path.join(pathetc, new_basename)
        shutil.move(fpath, new_fpath)

def move_var_files_in_subdirs():
    for fpath in glob.glob(os.path.join(pathvar, "last_*")):
        dst = os.path.join(pathvar, "node")
        if not os.path.exists(dst):
            os.makedirs(dst)
        fname = os.path.basename(fpath)
        new_fpath = os.path.join(dst, fname)
        logit("move %s to %s" % (fpath, new_fpath))
        shutil.move(fpath, new_fpath)

    for fpath in glob.glob(os.path.join(pathvar, "*_last_*")):
        fname = os.path.basename(fpath)
        svcname = fname.split("_last_")[0]
        dst = os.path.join(pathvar, svcname)
        if not os.path.exists(dst):
            os.makedirs(dst)
        fname = fname.replace(svcname+"_", "")
        new_fpath = os.path.join(dst, fname)
        logit("move %s to %s" % (fpath, new_fpath))
        shutil.move(fpath, new_fpath)

    for fpath in glob.glob(os.path.join(pathvar, "*.push")):
        svcname = os.path.basename(fpath).split(".push")[0]
        dst = os.path.join(pathvar, svcname)
        if not os.path.exists(dst):
            os.makedirs(dst)
        fname = "last_pushed_env"
        new_fpath = os.path.join(dst, fname)
        logit("move %s to %s" % (fpath, new_fpath))
        shutil.move(fpath, new_fpath)

def move_usr_to_opt():
    logit("begin")
    linksvc = os.path.join(os.sep, 'service')
    old_pathsvc = os.path.join(os.sep, 'usr', 'local', 'opensvc')
    old_pathvar = os.path.join(old_pathsvc, 'var')
    old_pathetc = os.path.join(old_pathsvc, 'etc')

    if os.path.exists(old_pathvar):
        logit("found old var %s"%old_pathvar)
        for f in glob.glob(old_pathvar+'/*'):
            dst = os.path.join(pathvar, os.path.basename(f))
            if os.path.exists(dst) and dst.find('host_mode') == -1:
                logit("file %s already exist"%dst)
                continue
            if os.path.isdir(f):
                logit("copying dir %s to %s"%(f, dst))
                shutil.copytree(f, dst, symlinks=True)
            elif os.path.islink(f):
                linkto = os.readlink(f)
                logit("create link %s -> %s"%(dst, linto))
                os.symlink(linkto, dst)
            else:
                logit("copying file %s to %s"%(f, dst))
                shutil.copy2(f, dst)

    if os.path.exists(old_pathetc):
        logit("found old etc %s"%old_pathetc)
        for f in glob.glob(old_pathetc+'/*'):
            dst = os.path.join(pathetc, os.path.basename(f))
            if os.path.exists(dst):
                logit("file %s already exist"%dst)
                continue
            if os.path.islink(f):
                linkto = os.readlink(f)
                logit("create link %s -> %s"%(dst, linto))
                os.symlink(linkto, dst)
            elif os.path.isdir(f):
                logit("copying dir %s to %s"%(f, dst))
                shutil.copytree(f, dst, symlinks=True)
            else:
                logit("copying file %s to %s"%(f, dst))
                shutil.copy2(f, dst)

    if os.path.exists(old_pathsvc):
        logit("removing old_pathsvc %s"%old_pathsvc)
        shutil.rmtree(old_pathsvc)

    if os.path.islink(linksvc) and os.path.realpath(linksvc) == old_pathsvc:
        logit("removing linksvc %s"%linksvc)
        os.unlink(linksvc)

def move_var_files_19():
    # node frozen flag
    old_node_frozen = os.path.join(pathvar, "FROZEN")
    pathvar_node = os.path.join(pathvar, "node")
    if not os.path.exists(pathvar_node):
        os.makedirs(pathvar_node, p0755)
    if os.path.exists(old_node_frozen):
        node_frozen = os.path.join(pathvar_node, "frozen")
        shutil.move(old_node_frozen, node_frozen)

    # service frozen flag
    for fpath in glob.glob(os.path.join(pathvar, "FROZEN.*")):
        svcname = os.path.basename(fpath).replace("FROZEN.", "")
        pathvar_svc = os.path.join(pathvar, "services", svcname)
        if not os.path.exists(pathvar_svc):
            os.makedirs(pathvar_svc, p0755)
        svc_frozen = os.path.join(pathvar_svc, "frozen")
        shutil.move(fpath, svc_frozen)

    # stats
    pathstats = os.path.join(pathvar, "stats")
    if not os.path.exists(pathstats):
        os.makedirs(pathstats, p0755)
    for fpath in glob.glob(os.path.join(pathvar, "stats_*")):
        fname = os.path.basename(fpath).replace("stats_", "")
        dst = os.path.join(pathvar, "stats", fname)
        shutil.move(fpath, dst)


def install_etc_path():
    logit("begin")
    p = os.path.join(os.sep, 'etc', 'PATH')
    if not os.path.exists(p):
        logit("etc/PATH not found")
        return
    try:
        logit("loading %s"%(p))
        f = open(p, "r")
        buff = f.read()
        f.close()
    except:
        logit("issue met while trying to read %s"%(p), stderr=True)
        return
    l = buff.strip().split(":")
    n = len(l)
    for op in (pathbin, pathetc):
        if op in l:
            logit("dir %s already present in %s"%(op, p))
            continue
        logit("adding dir %s"%(op))
        l.append(op)
    if len(l) == n:
        logit("nothing changed in %s"%(p))
        return
    try:
        logit("updating %s"%(p))
        f = open(p, "w")
        f.write(":".join(l)+'\n')
        f.close()
    except:
        logit("issue met while trying to write %s"%(p), stderr=True)
        return

def install_profile():
    logit("begin")
    prof_d = os.path.join(os.sep, 'etc', 'profile.d')
    prof = os.path.join(prof_d, 'opensvc.sh')
    buff = "if ! echo ${PATH} | grep -q "+pathetc+"; then"+"\n"
    buff = buff+"    PATH=${PATH}:"+pathetc+"\n"
    buff = buff+"fi\n\n"
    buff = buff+"if ! echo ${PATH} | grep -qw "+pathsbin+"; then"+"\n"
    buff = buff+"    PATH=${PATH}:"+pathsbin+"\n"
    buff = buff+"fi\n"
    if not os.path.exists(prof_d):
        logit("no profile directory found")
        return
    try:
        logit("installing profile in file %s"%(prof))
        f = open(prof, 'w')
        f.write(buff)
        f.close()
    except:
        logit("issue met while trying to install profile in file %s"%(prof), stderr=True)
        f.close()
        import traceback
        traceback.print_exc()

def install_bash_completion():
    logit("begin")
    if pathsvc is None:
        return
    src = os.path.join(pathsvc, 'usr', 'share', 'bash_completion.d', 'opensvc')
    ds = [os.path.join(os.sep, 'etc', 'bash_completion.d'),
          os.path.join(os.sep, 'etc', 'bash', 'bash_completion.d')]
    for d in ds:
        dst = os.path.join(d, 'opensvc')
        if not os.path.exists(d):
            d = None
            continue
        else:
            break
    if d is None:
        logit("no bash completion directory found")
        return
    try:
        logit("installing bash completion file src %s to tgt %s"%(src, dst))
        shutil.copyfile(src, dst)
        os.chmod(dst, p0644)
    except:
        logit("issue met while trying to install bash completion file src %s to tgt %s"%(src, dst))

def install_link(source, target):
    logit("begin")
    if source == '' or target == '':
        logit("bad parameters")
        return False
    if os.path.realpath(source) == os.path.realpath(target):
        logit("link already ok")
        return True
    if os.path.islink(target) or os.path.exists(target):
        logit("unlink %s", target)
        os.unlink(target)
    try:
        logit("create link %s -> %s"%(target, source))
        os.symlink(source, target)
    except:
        logit("issue met while trying to symlink src %s with tgt %s"%(source, target))

def install_pythonlink():
    logit("begin")
    if sysname == 'Windows':
        return install_pythonlink_windows()
    else:
        return


def install_pythonlink_windows():
    logit("begin")
    logit("before appending pathlib to syspath")
    logit(os.environ["PATH"])
    sys.path = [pathlib] + sys.path
    logit("after appending pathlib to syspath")
    logit(os.environ["PATH"])
    from rcUtilitiesWindows import get_registry_value
    logit("before reading installfolder in registry")
    try:
        installfolder = get_registry_value('HKEY_CURRENT_USER', 'Software\\OpenSVC', 'path')
    except:
        logit("issue met while trying to read path into registry HKCU/Software/OpenSVC/path", stderr=True)
        sys.exit(1)

    installfolder = installfolder.rstrip('\\')
    logit("installfolder = <"+installfolder+">")
    osvcenv = os.path.join(installfolder, 'osvcenv.cmd')
    content = '@echo off\nset OSVCROOT='+installfolder+'\nset OSVCPYTHONROOT=%OSVCROOT%\python\nset PYTHONPATH=%OSVCROOT%\lib\nset OSVCPYTHONEXEC=%OSVCPYTHONROOT%\python.exe\ncall inpath.cmd OSVCPYTHONROOT'
    logit(content)
    f = open(osvcenv, 'w')
    f.write(content)
    f.close()

def move_host_mode():
    logit("begin")
    hm = os.path.join(pathvar, 'host_mode')
    cf = os.path.join(pathetc, 'node.conf')
    nodemgr = os.path.join(pathsbin, 'nodemgr')
    if not os.path.exists(hm):
        logit("file %s does not exist"%hm)
        return
    try:
        fp = open(hm, 'r')
        mode = fp.read().split()[0]
        fp.close()
    except:
        logit("failed to read old host_mode. renamed to %s"%(hm+'.old'))
        shutil.move(hm, hm+'.old')
        return
    cmd = [nodemgr, 'set', '--param', 'node.host_mode', '--value', mode]
    ret = osrun(' '.join(cmd))
    if ret != 0:
        logit("failed to set host_mode in node.conf", stdout=True)
        return
    shutil.move(hm, hm+'.old')

def nodeconf_params():
    logit("begin")
    nodeconf = os.path.join(pathetc, 'node.conf')
    dotnodeconf = os.path.join(pathetc, '.node.conf')

    # reset etc/.node.conf (autogenerated)
    if os.path.exists(dotnodeconf):
        logit("unlink file %s"%dotnodeconf)
        os.unlink(dotnodeconf)

    if not os.path.exists(nodeconf):
        logit("file %s does not exist"%nodeconf)
        return

    try:
        import ConfigParser
    except ImportError:
        import configparser as ConfigParser
    import copy
    try:
        config = ConfigParser.RawConfigParser()
    except AttributeError:
        logit("issue occured while trying to instantiate configparser")
        return
    config.read(nodeconf)
    changed = False

    # no DEFAULT in etc/node.conf
    for o in copy.copy(config.defaults()):
        logit("removing DEFAULT in node.conf")
        config.remove_option('DEFAULT', o)
        changed = True

    # sync section goes to etc/.node.conf
    if config.has_section('sync'):
        logit("removing sync in node.conf")
        config.remove_section('sync')
        changed = True

    for s in config.sections():
        for o in config.options(s):
            if o in ['sync_interval', 'push_interval', 'comp_check_interval']:
                logit("looping %s"%o)
                v = config.getint(s, o)
                config.remove_option(s, o)
                config.set(s, 'interval', v)
                changed = True
            if o in ['sync_days', 'push_days', 'comp_check_days']:
                logit("looping %s"%o)
                v = config.get(s, o)
                config.remove_option(s, o)
                config.set(s, 'days', v)
                changed = True
            if o in ['sync_period', 'push_period', 'comp_check_period']:
                logit("looping %s"%o)
                v = config.get(s, o)
                config.remove_option(s, o)
                config.set(s, 'period', v)
                changed = True

    if changed:
        logit("writing new node.conf")
        try:
            fp = open(nodeconf, 'w')
            config.write(fp)
            fp.close()
        except:
            logit("failed to write new %s"%nodeconf, stderr=True)

def save_exc():
    logit("begin")
    import traceback
    try:
        import tempfile

        try:
            import datetime
            now = str(datetime.datetime.now()).replace(' ', '-')
        except:
            now = ""

        try:
            f = tempfile.NamedTemporaryFile(dir=pathtmp, prefix='exc-'+now+'-')
        except:
            return
        f.close()
        f = open(f.name, 'w')
        traceback.print_exc(file=f)
        logit("unexpected error. stack saved in %s"%f.name, stderr=True)
        f.close()
    except:
        logit("unexpected error", stderr=True)
        traceback.print_exc()

def purge_collector_api_cache():
    logit("begin")
    fname = os.path.join(pathvar, "collector")
    if os.path.exists(fname) and os.path.isfile(fname):
        logit("unlink file %s"%fname)
        os.unlink(fname)

def chmod_directories():
    logit("begin")
    if not hasattr(os, "walk"):
        logit("os.walk not available")
        return
    if sysname == 'Windows':
        logit("skip : unsupported on Windows")
        return
    for d in (pathbin, pathlib, pathusr):
        if d is None:
            continue
        for dirname, dirnames, filenames in os.walk(d):
            for subdirname in dirnames:
                dirpath = os.path.join(dirname, subdirname)
                try:
                    os.chmod(dirpath, p0755)
                    msg = "setting %s permissions to 0755" % dirpath
                except:
                    msg = "issue met while trying to set %s permissions to 0755" % dirpath
                logit(msg)

def log_file_info(path):
    try:
        info = os.lstat(path)
    except:
        msg = "issue met while trying to get [%s] os.lstat information" % path
        logit(msg)
    return
    string = "uid[%d] gid[%d] perms[%s] file[%s]" % (info.st_uid, info.st_gid, oct(info.st_mode & 777), path)
    logit(string)

def dump_install_content():
    logit("begin")
    if sysname == 'Windows':
        logit("skip : unsupported on Windows")
        return
    if not hasattr(os, "walk"):
        logit("os.walk not available")
        return
    for d in (pathbin, pathlib, pathusr):
        if d is None:
            continue
        for dirname, dirnames, filenames in os.walk(d):
            for subdirname in dirnames:
                dirpath = os.path.join(dirname, subdirname)
                log_file_info(dirpath)
            for filename in filenames:
                filepath = os.path.join(dirname, filename)
                log_file_info(filepath)

def convert_to_lsb():
    logit("begin")
    if sysname == 'Windows':
        logit("skip : unsupported on Windows")
        return
    if len(glob.glob(pathetc+"/*")) > 0:
        logit("skip : skip convert to lsb because /etc/opensvc/ is not empty")
        return
    if not os.path.exists("/opt/opensvc"):
        logit("skip : skip convert to lsb because /opt/opensvc/ does not exist")
        return
    for p in glob.glob("/opt/opensvc/etc/*conf") + glob.glob("/opt/opensvc/etc/sssu") + glob.glob("/opt/opensvc/etc/*pem") + glob.glob("/opt/opensvc/etc/*pub"):
        logit("migrate " + p)
        shutil.copy(p, pathetc)
    for p in glob.glob("/opt/opensvc/etc/*.env"):
        logit("migrate " + p)
        svcname = os.path.basename(p)[:-4]
        shutil.copy(os.path.realpath(p), pathetc)
        os.symlink("/usr/bin/svcmgr", os.path.join(pathetc, svcname))
    for p in glob.glob("/opt/opensvc/etc/*.d") + glob.glob("/opt/opensvc/etc/*.dir"):
        logit("migrate " + p)
        if os.path.islink(p):
            bp = os.path.basename(p)
            linkto = os.readlink(p)
            if linkto.startswith("/opt/opensvc/etc"):
                linkto.replace("/opt/opensvc/etc/", "")
            dst = os.path.join(pathetc, bp)
            os.symlink(linkto, dst)
        elif os.path.isdir(p):
            bp = os.path.basename(p)
            dst = os.path.join(pathetc, bp)
            shutil.copytree(p, dst, symlinks=True)
        else:
            shutil.copy(p, pathetc)
    for p in glob.glob("/opt/opensvc/var/*"):
        if os.path.basename(p) == "btrfs":
            continue
        logit("migrate " + p)
        bp = os.path.basename(p)
        dst = os.path.join(pathvar, bp)
        if os.path.exists(dst):
            continue
        if os.path.isdir(p):
            try:
                shutil.copytree(p, dst, symlinks=True)
            except:
                # best effort for var
                pass
        else:
            shutil.copy(p, pathvar)

def restart_daemon():
    logit("begin")
    flag = os.path.join(pathvar, "postinstall.restart")
    if not os.path.exists(flag):
        return
    os.unlink(flag)
    os.environ['OPENSVC_AGENT_UPGRADE'] = "1"
    cmd = "%s/nodemgr daemon start" % pathbin
    osrun(cmd, stdout=True)
    cmd = "%s/nodemgr daemon running" % pathbin
    osrun(cmd)
    del os.environ['OPENSVC_AGENT_UPGRADE']

def init_secret():
    logit("begin")
    time.sleep(1)
    if sysname == 'Windows':
        cmd = "nodemgr ping --node 127.0.0.1"
    else:
        cmd = "%s/nodemgr ping --node 127.0.0.1" % pathbin
    osrun(cmd)

def solaris_fmri_setprop():
    logit("begin")
    if sysname != 'SunOS':
        logit("Only useful on SunOS", stdout=False)
        return
    else:
        logit("issuing setprop on fmri", stdout=False)
        osvcfmri = 'svc:/site/application/opensvc:boot'
        cmd = ['svccfg', '-s', osvcfmri, 'setprop', 'boot/firstrun', '=', 'done', '2>/dev/null']
        ret = osrun(' '.join(cmd))
        if ret == 0:
            # setprop ok, Solaris install from IPS package with standard FMRI
            logit("setprop ok. now refreshing fmri", stdout=False)
            cmd = ['svccfg', '-s', osvcfmri, 'refresh']
            ret = osrun(' '.join(cmd))
            if ret == 0:
                logit("fmri refresh successfully executed", stdout=False)

try:
    move_var_files_in_subdirs()
    move_usr_to_opt()
    missing_dirs()
    convert_svclinks()
    install_cron()
    install_rc()
    gen_keys()
    install_profile()
    install_etc_path()
    install_bash_completion()
    move_host_mode()
    install_pythonlink()
    nodeconf_params()
    purge_collector_api_cache()
    chmod_directories()
    convert_to_lsb()
    move_env_to_conf()
    move_var_files_19()
    dump_install_content()
    restart_daemon()
    init_secret()
    solaris_fmri_setprop()
    logit("\nOpenSVC postinstall terminated\n", stdout=True)
except:
    save_exc()
    sys.exit(1)
