#!/usr/bin/python
# Habanero smartpools initializer.

import errno
import os
import os.path
import sys
import logging
import syslog
import shutil

import isi.gconfig as gconf
import isi.smartpools as sp
import isi.smartpools.diskpools as spdp
import isi.smartpools.filepools as fp
import isi.fs.diskpool as fsdp
import isi.ui.lib.global_protection as gprot
import isi.ui.shell.setcmd as setcmd
import isi.sys.version as sysver
import isi.fs.restripe.group as group
import isi.app.lib.cluster as cluster
import isi.config.ifs as ifs
import isi.config.licensing as lic

from isi.app.lib import safelockfile

GLOBAL_PROTECTION_FILES = [
"/etc/mcp/scripts/global_protection.py",
"/etc/mcp/sys/files/global_protection",
"/etc/mcp/sys/global_protection.xml",
"/etc/mcp/override/global_protection.xml",
]

SMARTPOOLS_INIT_LOCK = "/ifs/.ifsvar/modules/smartpools/init-lock"

SMARTPOOLS_INIT_FLAG = "/ifs/.ifsvar/modules/smartpools/initialized"

SMARTPOOLS_PROT_INIT_FLAG = "/ifs/.ifsvar/modules/smartpools/prot_initialized"

MAX_GCONF_COMMIT_RETRIES = 5

# This is run to avoid the race codition where we run init_smartpools before
# MCP gets around to copying the licensing file locally
def copy_licensing_to_local():
    ifs_file = os.path.join("/ifs/.ifsvar",
            lic.LICENSING_MXD_OVERRIDE_FILE[1:])
    local_file = lic.LICENSING_MXD_OVERRIDE_FILE
    temp_file = lic.LICENSING_MXD_OVERRIDE_FILE + ".temp"
    if (not os.path.exists(local_file) and
            os.path.exists(ifs_file)):
        shutil.copy2(ifs_file, temp_file)
        os.rename(temp_file, local_file)
    
def provision_unassigned():
    copy_licensing_to_local()
    while True:
        prot_inited = False
        try:
            sp_cfg = sp.SPConfig()
            dp_cfg = sp_cfg.dp_cfg
            if not dp_cfg.fully_upgraded():
                syslog.syslog(syslog.LOG_NOTICE,
                        "Postponing provisioning of unassigned drives until "
                        "SmartPools is upgraded")
                return

            # Provisioned all unassigned drives; in-memory only for now.
            dp_cfg.autoprov_unassigned()

            # This may raise an UnderprovisionedError exception
            sp_cfg.save(ignore_underprovisioned=False)

        except OSError, e:
            # If we get an ESTALE back from the filesystem, it means
            # someone else modified the diskpools underneath us. Just try
            # again until we succeed. Re-raise any other type of OSError
            # exception though.
            if e.errno == errno.ESTALE:
                continue
            else:
                raise
        else:
            # No exceptions, we saved the diskpools config, break
            break

def main():

    # Really, this only needs to be run by one node on the cluster.
    # If there is a new node joining, then that node should do it, to make sure
    # the node has already put its sysconfig in the cache.  So we just have
    # the highest devid in the group run this. (This is only an issue if
    # the node had joined using the --skip-hardware-version-check flag as
    # otherwise the node that added it would have cached the node's info)
    try:
        local = cluster.Cluster().local()
        up_nodes = group.GroupInfo().getUpStorageNodeList()
        # up_nodes is a sorted list
        if up_nodes and up_nodes[-1] != local:
            # Let someone else do it. 
            return os.EX_OK

    except ifs.error, e:
        # Log the error, but not running this when it needs to be run is 
        # worse than running it when it isn't needed, so continue with
        # the initialization
        syslog.syslog(syslog.LOG_ERR, "Error getting local id" % str(e))
        
    # We need to block all other nodes from continue until we've finished
    # the initialization
    try:
        sentry = safelockfile(SMARTPOOLS_INIT_LOCK, 'w')
    except EnvironmentError, e:
        syslog.syslog(syslog.LOG_ERR, "error acquiring lock: %s" % str(e))
        return os.EX_OSFILE

    try:

        # Always try to provision all the unassigned drives.
        provision_unassigned()
        return os.EX_OK

    except spdp.UnderprovisionedError, e:
        syslog.syslog(syslog.LOG_NOTICE,
                "Postponed provisioning of unassigned drives: %s" % str(e))
        return os.EX_OK
    except sp.ConfigError, e:
        syslog.syslog(syslog.LOG_NOTICE,
                "Error provisioning drives: %s" % str(e))
        return os.EX_SOFTWARE
    except Exception, e:
        import traceback
        syslog.syslog(syslog.LOG_ERR,
                "internal error initializing smartpools: %s\n%s" % \
                      (str(e), traceback.format_exc()))
        return os.EX_SOFTWARE
    finally:
        sentry.close()

if __name__ == "__main__":
    syslog.openlog("init_smartpools")
    rc = main()
    syslog.closelog()
    sys.exit(rc)

