#!/usr/bin/python
# Mavericks NFS upgrade script

import logging
import os
import subprocess
import sys
import syslog
import time

import isi.gconfig as gcfg

from isi.app.lib import safelockfile
from isi.error import IsiError

SCRIPTNAME = 'mavericks-nfs-upgrade'
IFS_BACKUP_PATH = "/ifs/.ifsvar/tmp"
LOG_FILE = os.path.join(IFS_BACKUP_PATH, "mavericks-nfs-upgrade.log")
NFS_UPGRADE_LOCKFILE = os.path.join(IFS_BACKUP_PATH, "mavericks-nfs-upgraded")
NFS_DEFAULT_PATH = "/ifs/.ifsvar/modules/nfs"
NFS_UPGRADED_FILE = os.path.join(NFS_DEFAULT_PATH, "mavericks-nfs-upgrade-complete")

# sysctl.conf defines
SYSCTL_CONF_PATH = "/etc/mcp/override"
SYSCTL_CONF_FILE = "sysctl.conf"
SYSCTL_CONF = os.path.join(SYSCTL_CONF_PATH, SYSCTL_CONF_FILE)
SYSCTL_CONF_UPGRADE = SYSCTL_CONF + ".upgrade"

logger = logging.getLogger("mavericks_nfs_upgrade")

def _invalid_sysctl(outfile, line):
    outfile.write('# OneFS 7.0 invalid sysctl\n')
    outfile.write('#'+line+'\n')

def _upgraded_sysctl(outfile, line):
    outfile.write('# OneFS 7.0 moved to gconfig\n')
    outfile.write('#'+line+'\n')

def _new_sysctl(outfile, line):
    outfile.write('# Added by OneFS 7.0 upgrade\n')
    outfile.write(line+'\n')

# Many sysctls were replaced with gconfig for Mavericks
def sysctl_upgrade():
    if not os.path.isfile(SYSCTL_CONF):
        logger.info("%s: File %s does not exist, no sysctl upgrade" %
                    (time.ctime(), SYSCTL_CONF))
        return

    try:
        infile = open(SYSCTL_CONF,'r')
    except:
        logger.info("%s: NFS upgrade did not detect a %s file" %
                    (SCRIPTNAME, SYSCTL_CONF))
        return

    try:
        outfile = open(SYSCTL_CONF_UPGRADE,'w')
    except:
        infile.close()
        raise RuntimeError("%s: NFS upgrade could not create %s" %
                           (SCRIPTNAME, SYSCTL_CONF))

    try:
        cfg = gcfg.open(gcfg.gcfg_nfs_config).ctx_new()
    except:
        infile.close()
        outfile.close()
        raise RuntimeError("%s: NFS upgrade could not open NFS gconfig" %
                           SCRIPTNAME)

    for line in infile.readlines():
        if len(line) > 0 and line[-1] == '\n':
            line = line[:-1]
        
        fullline = line
        try:
            line = line[0:line.index('#')]
        except ValueError:
            pass

        if line[0:8] != 'vfs.nfs.' and line[0:10] != 'vfs.nfsrv.':
            outfile.write(fullline+'\n'),
            continue

        try:
            sysctl, value = line.split('=',2)
        except ValueError:
            _invalid_sysctl(outfile, fullline)
            continue
        line = fullline
            
        if sysctl == 'vfs.nfs.alert_mountd_host_lookup_failed':
            try:
                value = long(value)
            except ValueError:
                _invalid_sysctl(outfile, line)
                continue
            cfg.write('mountd_config', {'md_alert_host_lookup_failed': (value != 0)})
            _upgraded_sysctl(outfile, line)
            continue

        if sysctl == 'vfs.nfs.alert_mount_fail_throttle_count':
            try:
                value = long(value)
            except ValueError:
                _invalid_sysctl(outfile, line)
                continue
            cfg.write('mountd_config', {'md_alert_mount_fail_throttle_count': value})
            _upgraded_sysctl(outfile, line)
            continue

        if sysctl == 'vfs.nfs.alert_mount_fail_throttle_duration':
            try:
                value = long(value)
            except ValueError:
                _invalid_sysctl(outfile, line)
                continue
            cfg.write('mountd_config', {'md_alert_mount_fail_throttle_duration': value})
            _upgraded_sysctl(outfile, line)
            continue

        if sysctl == 'vfs.nfs.mountd_client_dns_lookup':
            try:
                value = long(value)
            except ValueError:
                _invalid_sysctl(outfile, line)
                continue
            cfg.write('mountd_config', {'md_client_dns_lookup': (value != 0)})
            _upgraded_sysctl(outfile, line)
            continue

        # These sysctls got renamed
        if sysctl == 'vfs.nfsrv.rpc.minthreads':
            _new_sysctl(outfile, 'vfs.nfsrv.rpc.threads_min=' + value)
            continue
        if sysctl == 'vfs.nfsrv.rpc.maxthreads':
            _new_sysctl(outfile, 'vfs.nfsrv.rpc.threads_max=' + value)
            continue

        # This is not a sysctl we are upgrading, so write as is
        outfile.write(line+'\n')

    infile.close()
    outfile.close()

    try:
        cfg.commit()
    except:
        raise RuntimeError("%s: NFS upgrade could not commit NFS gconfig" %
                           SCRIPTNAME)
    clean_conf = True
    try:
        os.rename(SYSCTL_CONF_UPGRADE, SYSCTL_CONF)
        clean_conf = False
    except OSError:
        raise RuntimeError("%s: failed to replace %s" %
                           (SCRIPTNAME, SYSCTL_CONF))

    if clean_conf:
        process = subprocess.Popen("rm -r " + SYSCTL_CONF_UPGRADE,
                                   shell=True, stdout=subprocess.PIPE)
        for line in process.stdout:
            print line
        process.wait()
        if process.returncode != 0:
            logger.error("%s: Unable to delete sysctl.conf.upgrade" % SCRIPTNAME)

def main():
    try:
        logger.setLevel(logging.DEBUG)
        fh = logging.FileHandler(LOG_FILE, 'a')
        fh.setLevel(logging.DEBUG)
        logger.addHandler(fh)

    except EnvironmentError, e:
        syslog.syslog(syslog.LOG_NOTICE,
                      "Error running nfs upgrade script: %s" % e)
        return 1

    try:
        os.chmod(LOG_FILE, 0640)
    except OSError, e:
        # log chmod errors and continue
        syslog.syslog(syslog.LOG_NOTICE,
                      "Error chmod'ing %s (errno: %d)" % (LOG_FILE, e.errno))

    try:
        sentry = safelockfile(NFS_UPGRADE_LOCKFILE, 'w')
    except EnvironmentError, e:
        syslog.syslog(syslog.LOG_NOTICE,
                      "Error running nfs upgrade script: %s" % e)
        return 1

    if os.path.exists(NFS_UPGRADED_FILE):
        logger.info("%s: NFS upgrade has already occurred successfully." %
                    time.ctime())
        sentry.close()
        return 0

    # Begin upgrade
    try:
        sysctl_upgrade()
    except RuntimeError, e:
        logger.error("Error running nfs upgrade script: %s" % str(e))
        syslog.syslog(syslog.LOG_NOTICE,
                      "Error running nfs upgrade script: %s" % str(e));
        print str(e)
        sentry.close()
        return 1

    msg = "%s: Upgrade was successful" % time.ctime()
    logger.info(msg)
    print >>file(NFS_UPGRADED_FILE, 'w'), msg
    sentry.close()
    return 0

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