#!/usr/bin/env python

import os
import shutil
import glob
import sys
from subprocess import *

python_names = ['python2.7', 'python2.6']

sysname, nodename, x, x, machine = os.uname()

def python_path():
    for n in python_names:
        p = _python_path(n)
        if p is not None:
            return p

def _python_path(python_exe):
    for path in os.environ["PATH"].split(os.pathsep):
        exe_file = os.path.join(path, python_exe)
        if os.path.exists(exe_file) and os.access(exe_file, os.X_OK):
            return exe_file
    return None

def install_cron():
    """install opensvc cron jobs
    """

    agt = '/opt/opensvc/bin/perfagt.'+sysname
    ce = [{
           'sched': "0,10,20,30,40,50 * * * *",
           'reset_sched': False,
           'user': "",
           'cmd': "[ -x /opt/opensvc/bin/svcmon ] && /opt/opensvc/bin/svcmon --updatedb --maxdelaydb 120 >/dev/null 2>&1",
           'marker': 'svcmon --updatedb',
           'ok': False
    },{
           'sched': "0,10,20,30,40,50 * * * *",
           'reset_sched': True,
           'user': "",
           'cmd': "[ -x /opt/opensvc/bin/cron/opensvc ] && /opt/opensvc/bin/cron/opensvc >/dev/null 2>&1",
           'marker': '/opt/opensvc/bin/cron/opensvc',
           'ok': False
    },{
           'sched': "0,10,20,30,40,50 * * * *",
           'reset_sched': False,
           'user': "",
           'cmd': "[ -x %(agt)s ] && %(agt)s >/dev/null 2>&1"%dict(agt=agt),
           'marker': agt,
           'ok': False
    }]
    remove_entries = ['bin/nodemgr compliance check']

    purge = []
    root_crontab = False

    """ order of preference
    """
    if sysname == 'SunOS' :
        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:
        if os.path.exists(os.path.dirname(loc)):
            if not root_crontab:
                root_crontab = loc
                if root_crontab == '/etc/cron.d/opensvc':
                    ce[0]['user'] = "root"
                    ce[1]['user'] = "root"
                    ce[2]['user'] = "root"
            elif os.path.exists(loc):
                purge.append(loc)

    if not root_crontab:
        print "no root crontab found in usual locations %s"%str(root_crontab_locs)
        return False

    ce[0]['full'] = ' '.join([ce[0]['sched'], ce[0]['user'], ce[0]['cmd']])
    ce[1]['full'] = ' '.join([ce[1]['sched'], ce[1]['user'], ce[1]['cmd']])

    if os.path.exists(agt):
        ce[2]['full'] = ' '.join([ce[2]['sched'], ce[2]['user'], ce[2]['cmd']])
    else:
        ce[2]['full'] = None

    new = False
    if os.path.exists(root_crontab):
        try:
            f = open(root_crontab, 'r')
            new = f.readlines()
            f.close()
        except:
            f.close()
            import traceback
            traceback.print_exc()

        for i, line in enumerate(new):
            for c in ce:
                if c['full'] is None:
                    continue
                if c['marker'] in line:
                    if c['cmd'] in line:
                        sched = ' '.join(line.split()[:5])
                    if c['reset_sched'] and sched != c['sched']:
                        new[i] = ' '.join([c['sched'], c['user'], c['cmd']])+'\n'
                        c['ok'] = True
                    else:
                        # preserve scheduling
                        if c['reset_sched']:
                            sched = c['sched']
                        else:
                            sched = ' '.join(line.split()[:5])
                        new[i] = ' '.join([sched, c['user'], c['cmd']])+'\n'
                        c['ok'] = True
        for c in ce:
            if c['full'] is not None and not c['ok']:
                new.append(c['full']+'\n')
    else:
        new = []
        for c in ce:
            if c['full'] is not None and not c['ok']:
                new.append(c['full']+'\n')

    if not new:
        print "problem preparing the new crontab"
        return False
 
    for i, line in enumerate(new):
        for re in remove_entries:
            if re in line:
                print 'delete line "%s" from %s'%(re,root_crontab)
                del new[i]

    try:
        f = open(root_crontab, 'w')
        f.write(''.join(new))
        f.close()
    except:
        f.close()
        import traceback
        traceback.print_exc()

    """ Activate changes (actually only needed on HP-UX)
    """
    if '/var/spool/' in root_crontab:
        cmd = ['crontab', root_crontab]
        process = Popen(cmd, stdout=PIPE, stderr=PIPE, close_fds=True)
        buff = process.communicate()

    for loc in purge:
        try:
            f = open(loc, 'r')
            new = [ line for line in f.readlines() if not 'opensvc.daily' in line and not  'svcmon --updatedb' in line ]
            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):
            os.unlink(f)

def activate_chkconfig(svc):
    cmd = ['chkconfig', '--add', svc]
    process = Popen(cmd, stdout=PIPE, stderr=PIPE, close_fds=True)
    buff = process.communicate()
    if process.returncode > 0:
        print buff[1]
        return False
    return True

def activate_ovm():
    activate_chkconfig('zopensvc')

def activate_redhat():
    activate_chkconfig('opensvc')

def activate_debian():
    cmd = ['update-rc.d', '-f', 'opensvc', 'remove']
    process = Popen(cmd, stdout=PIPE, stderr=PIPE, close_fds=True)
    buff = process.communicate()
    if process.returncode > 0:
        print buff[1]
        return False
    cmd = ['update-rc.d', 'opensvc', 'defaults', '99', '01']
    process = Popen(cmd, stdout=PIPE, stderr=PIPE, close_fds=True)
    buff = process.communicate()
    if process.returncode > 0:
        print buff[1]
        return False
    return True

def activate_hpux():
    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"):
        os.unlink("/sbin/rc2.d/S990opensvc")
    for l in links:
        if not os.path.islink(l):
            if os.path.exists(l):
                os.unlink(l)
            os.symlink(rc, l)
    try:
        f = open("/etc/rc.config.d/opensvc", "w")
        f.write("RUN_OPENSVC=1\n")
        f.close()
    except:
        f.close()
        import traceback
        traceback.print_exc()
    return True

def activate_AIX():
    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):
                os.unlink(l)
            print "create link %s -> %s"%(l, rc)
            os.symlink(rc, l)
    return True

def activate_SunOS():
    rc = "/etc/init.d/opensvc"
    links = ["/etc/rc0.d/K00opensvc", "/etc/rc3.d/S99opensvc"]
    for l in links:
        if not os.path.islink(l):
            if os.path.exists(l):
                os.unlink(l)
            os.symlink(rc, l)
    return True

def activate_FreeBSD():
    return True

def activate_Darwin():
    return True

def update_file(filename, srctext, replacetext):
    """ 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
        print line.rstrip('\n')
    fileinput.close()

def install_rc():
    """install startup script
    """

    if os.path.exists('/etc/debian_version'):
        rc = '/etc/init.d/opensvc'
        src = '/opt/opensvc/bin/opensvc.init.debian'
        activate = activate_debian
    elif os.path.exists('/etc/SuSE-release'):
        rc = '/etc/init.d/opensvc'
        src = '/opt/opensvc/bin/opensvc.init.debian'
        activate = activate_redhat
    elif os.path.exists('/etc/redhat-release'):
        try:
            f = open('/etc/redhat-release', 'r')
            buff = f.read()
            f.close()
        except:
            buff = ""
        if 'Oracle VM server' in buff:
            rc = '/etc/init.d/zopensvc'
            activate = activate_ovm
        else:
            rc = '/etc/init.d/opensvc'
            activate = activate_redhat
        src = '/opt/opensvc/bin/opensvc.init.redhat'
    elif sysname == "HP-UX":
        rc = '/sbin/init.d/opensvc'
        src = '/opt/opensvc/bin/opensvc.init.hpux'
        activate = activate_hpux
    elif sysname == "SunOS":
        rc = '/etc/init.d/opensvc'
        src = '/opt/opensvc/bin/opensvc.init.SunOS'
        activate = activate_SunOS
    elif sysname == "FreeBSD":
        rc = '/etc/rc.d/opensvc'
        src = '/opt/opensvc/bin/opensvc.init.FreeBSD'
        activate = activate_FreeBSD
    elif sysname == "AIX":
        rc = '/etc/rc.d/init.d/opensvc'
        src = '/opt/opensvc/bin/opensvc.init.AIX'
        activate = activate_AIX
    elif sysname == "Darwin":
        rc = '/System/Library/LaunchDaemons/opensvc.plist'
        src = '/opt/opensvc/bin/opensvc.init.Darwin'
        activate = activate_Darwin
    else:
        print "could not select an init script: unsupported operating system"
        return False

    if os.path.islink(rc):
        os.unlink(rc)

    shutil.copyfile(src, rc)
    os.chmod(rc, 0755)
    activate()

def gen_keys():
    home = os.environ['HOME']
    priv = os.path.join(home, ".ssh", "id_dsa")
    pub = os.path.join(home, ".ssh", "id_dsa.pub")
    if os.path.exists(pub) or os.path.exists(priv):
        return
    cmd = ['ssh-keygen', '-t', 'dsa', '-b', '1024', '-P', '', '-f', priv]
    process = Popen(cmd, stdout=PIPE, stderr=PIPE, close_fds=True)
    buff = process.communicate()

def missing_dir(sub):
    pathd = os.path.join(os.sep, 'opt', 'opensvc', sub)
    if not os.path.exists(pathd):
	os.makedirs(pathd, 755)

def missing_dirs():
    missing_dir('log')
    missing_dir('tmp')

def convert_svclinks():
    missing_dir('etc')
    svcmgr = os.path.join('..', 'bin', 'svcmgr')
    if not os.path.exists(svcmgr):
	return 1
    rcService = os.path.realpath(os.path.join(os.sep, 'opt', 'opensvc', 'bin', 'rcService'))
    if not os.path.exists(rcService):
	return 1
    for fname in os.listdir(pathetc):
        fpath = os.path.join(pathetc, fname)
        if not os.path.islink(fpath):
            continue
        rpath = os.path.realpath(fpath)
        if rpath != rcService:
            continue
        os.unlink(fpath)
        os.symlink(svcmgr, fpath)

def move_usr_to_opt():
    linksvc = os.path.join(os.sep, 'service')
    pathsvc = os.path.join(os.sep, 'opt', 'opensvc')
    pathvar = os.path.join(pathsvc, 'var')
    pathetc = os.path.join(pathsvc, 'etc')
    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):
        for f in glob.glob(old_pathvar+'/*'):
            dst = os.path.join(pathvar, os.path.basename(f))
            if os.path.exists(dst) and 'host_mode' not in dst:
                continue
            if os.path.isdir(f):
                shutil.copytree(f, dst, symlinks=True)
            elif os.path.islink(f):
                linkto = os.readlink(f)
                os.symlink(linkto, dst)
            else:
                shutil.copy2(f, dst)

    if os.path.exists(old_pathetc):
        for f in glob.glob(old_pathetc+'/*'):
            dst = os.path.join(pathetc, os.path.basename(f))
            if os.path.exists(dst):
                continue
            if os.path.islink(f):
                linkto = os.readlink(f)
                os.symlink(linkto, dst)
            elif os.path.isdir(f):
                shutil.copytree(f, dst, symlinks=True)
            else:
                shutil.copy2(f, dst)

    if os.path.exists(old_pathsvc):
        shutil.rmtree(old_pathsvc)

    if os.path.islink(linksvc) and os.path.realpath(linksvc) == old_pathsvc:
        os.unlink(linksvc)

def install_profile():
    prof_d = os.path.join(os.sep, 'etc', 'profile.d')
    prof = os.path.join(prof_d, 'opensvc.sh')
    buff = "export PATH=$PATH:/opt/opensvc/bin:/opt/opensvc/etc\n"
    if not os.path.exists(prof_d):
        return
    try:
        f = open(prof, 'w')
        f.write(buff)
        f.close()
    except:
        f.close()
        import traceback
        traceback.print_exc()

def install_user_linux():
    cmd = ['useradd', '-m', 'opensvc', '-s', '/bin/bash']
    process = Popen(cmd, stdout=PIPE, stderr=PIPE, close_fds=True)
    buff = process.communicate()

def install_user():
    cmd = ['id', 'opensvc']
    process = Popen(cmd, stdout=PIPE, stderr=PIPE, close_fds=True)
    buff = process.communicate()
    if process.returncode == 0:
        return

    if sysname == 'Linux':
        install_user_linux()

def install_bash_completion():
    src = '/opt/opensvc/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:
        return
    shutil.copyfile(src, dst)
    os.chmod(dst, 0644)

def install_link(source, target):
    if source == '' or target == '':
        return False
    if os.path.realpath(source) == os.path.realpath(target):
        return True
    if os.path.islink(target) or os.path.exists(target):
        os.unlink(target)
    os.symlink(source,target)

def install_pythonlink():
    p = python_path()
    if p is None:
        print >>sys.stderr, "could not find a valid python installation"
        sys.exit(1)
    target = '/opt/opensvc/bin/python'
    return install_link(source=p, target=target)

def move_host_mode():
    hm = '/opt/opensvc/var/host_mode'
    cf = '/opt/opensvc/etc/node.conf'
    if not os.path.exists(hm):
        return
    try:
        fp = open(hm, 'r')
        mode = fp.read().split()[0]
        fp.close()
    except:
        print 'failed to read old host_mode. renamed to', hm+'.old'
        shutil.move(hm, hm+'.old')
        return
    cmd = ['/opt/opensvc/bin/nodemgr', 'set', '--param', 'node.host_mode', '--value', mode]
    process = Popen(cmd, stdout=PIPE, stderr=PIPE, close_fds=True)
    buff = process.communicate()
    if process.returncode != 0:
        print 'failed to set host_mode in node.conf'
        return
    shutil.move(hm, hm+'.old')
 
def nodeconf_params():
    nodeconf = os.path.join(os.sep, 'opt', 'opensvc', 'etc', 'node.conf')
    dotnodeconf = os.path.join(os.sep, 'opt', 'opensvc', 'etc', '.node.conf')

    # reset etc/.node.conf (autogenerated)
    if os.path.exists(dotnodeconf):
        os.unlink(dotnodeconf)

    if not os.path.exists(nodeconf):
        return

    import ConfigParser
    import copy
    config = ConfigParser.RawConfigParser()
    config.read(nodeconf)
    changed = False

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

    # sync section goes to etc/.node.conf
    if config.has_section('sync'):
        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']:
                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']:
                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']:
                v = config.get(s, o)
                config.remove_option(s, o)
                config.set(s, 'period', v)
                changed = True

    if changed:
        try:
            fp = open(nodeconf, 'w')
            config.write(fp)
            fp.close()
        except:
            print >>sys.stderr, "failed to write new %s"%nodeconf

try:
    install_pythonlink()
    move_usr_to_opt()
    missing_dirs()
    convert_svclinks()
    install_cron()
    install_rc()
    gen_keys()
    install_profile()
    install_user()
    install_bash_completion()
    move_host_mode()
    nodeconf_params()
except:
    import traceback
    traceback.print_exc()
    sys.exit(1)
