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

import os
import syslog
import time
import logging
import subprocess

import sys

CLI_PATH="/usr/local/share/CLI/"
sys.path.append(CLI_PATH)

import papi.papi as papi
import papi.rest as rest
import papi.conn as conn
import papi.container as container

import isi.config as iconf
import isi.sys.version as sysver
from isi.app.lib import safelockfile

IFS_BACKUP_PATH = "/ifs/.ifsvar/tmp"
LOG_FILE = os.path.join(IFS_BACKUP_PATH, "mavericks-smb-upgrade.log")
SMB_UPGRADE_FILE = os.path.join(IFS_BACKUP_PATH, "mavericks-smb-upgraded")

class update_change_permissions:
    def __init__(self, logger):
        self.logger = logger

    def needs_upgrade(self):
        # Check if an upgrade is necessary
        nstat = sysver.Node_Status()
        pVer = nstat.get_previous_version()

        # This upgrade only needs to run if updating from a previous 7.0
        # version
        if pVer >= sysver.MAVERICKS_VERSION and \
                pVer < sysver.MAVERICKS_702_VERSION:
            return True

        return False

    def run(self):
        # Verify isi_papi_d is running
        self.logger.info("Verifying isi_papi_d is running...")
        if not self._verify_running('isi_papi_d', ['/usr/sbin/isi_papi_d']):
            raise Exception("Unable to verify isi_papi_d is running...")

        # Verify lwio is running
        self.logger.info("Verifying lwio is running...")
        if not self._verify_running('lwio', ['/usr/likewise/bin/lwsm', 'autostart']):
            raise Exception("Unable to verify lwio is running...")

        conn.conn = conn.iface()
        data = container.PAPIData(conn.conn, ['1', 'protocols', 'smb', 'shares'], 'shares')

        # Try for up to 60 seconds to retrieve share information
        get_done, count = False, 0
        while not get_done and count < 20:      # 3 second waits
            try:
                data.get()
                get_done = True
            except rest.RestError as e:
                self.logger.error(e)

            count += 1
            if not get_done:
                time.sleep(3)

        if not get_done:
            raise Exception("Failed to retrieve share information.")

        # Perform a PUT on each share to rebuild permissions
        for key in data:
            try:
                uri = papi.URI({'share': key}).format(papi.URI.SMB_SHARE)
                share_data = container.PAPIData(conn.conn, uri, 'shares')
                share_data.put({"permissions": data[key]["permissions"],
                                "run_as_root": data[key]["run_as_root"]})
                self.logger.info("Successfully updated permissions: %s" % key)
            except Exception as e:
                self.logger.exception("Error updating permissions: %s" % e)

    def _verify_running(self, name, start_cmd):
        """ Verify a process is running by name. Will only succeed if there
        is only one instance of the process. """
        running = self.__find_process(name)
        if running:
            return True

        self.logger.info(" * " + name + " does not appear to be running; " +
                "attempting to start the process")
        child = subprocess.Popen(start_cmd)
        child.communicate()

        return self.__find_process(name)

    def __find_process(self, name):
        """ Check if a process is running once a second for up to 10 seconds
        """
        for i in range(10):
            pid = self.__find_pid(name)
            if pid:
                self.logger.info(
                        " * %s identified as process '%s'" % (name, pid))
                return True
            else:
                time.sleep(1)

        return False

    def __find_pid(self, name):
        child = subprocess.Popen(['pgrep', name], stdout=subprocess.PIPE,
                stderr=subprocess.PIPE)

        (results, error) = child.communicate()
        results = results.split()

        if len(results) > 1 or error != "":
            raise Exception("* unable to isolate a running %s process" % name)
        elif len(results) == 1:
            return results[0]
        else:
            return False

def run_upgrade(test, logger):
    upgrade = test(logger)
    name = upgrade.__class__.__name__

    if not upgrade.needs_upgrade():
        return 0

    syslog.syslog(syslog.LOG_NOTICE, "Running mavericks-smb-upgrade:%s" % name)
    logger.info("Running %s" % name)

    try:
        upgrade.run()
        syslog.syslog(syslog.LOG_NOTICE,
                "Successfully ran mavericks-smb-upgrade:%s" % name)
        logger.info("Successfully ran %s" % name)
    except Exception as e:
        syslog.syslog(syslog.LOG_NOTICE,
                "Error while running %s: %s" % (name, e))
        logger.info("Error during %s: %s" % (name, e))

def _setup_logger():
    lnn = "Node %d -" % iconf.getNode()['node id']
    lfmt = lnn + "%(asctime)s - %(levelname)s - %(message)s"

    logger = logging.getLogger("mavericks_smb_upgade")
    logger.setLevel(logging.DEBUG)
    fh = logging.FileHandler(LOG_FILE, 'a')
    fh.setLevel(logging.DEBUG)
    fh.setFormatter(logging.Formatter(lfmt))
    logger.addHandler(fh)

    return logger

def main():
    # We need to block all other nodes from continue until we've finished
    # the upgrade
    try:
        sentry = safelockfile(SMB_UPGRADE_FILE, 'w')
    except EnvironmentError, e:
        syslog.syslog(syslog.LOG_NOTICE,
                      "Error running smb upgrade script, %s"%e)
        return 0

    # Set up a logger object
    logger = _setup_logger()
    logger.info("*** running mavericks-smb-upgrade ***")

    # Run any upgrades/updates
    run_upgrade(update_change_permissions, logger)

    logger.info("*** finished mavericks-smb-upgrade ***")
    sentry.close()

    return 0

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