#!/usr/bin/env python
#
# Copyright C 2011-2012 by Isilon Systems LLC. All rights not expressly
# granted herein are reserved.
# Created in the United States of America.
#
# This digital media is protected by U.S. and international copyright and
# intellectual property laws. Unless otherwise specified, all information
# and screens appearing as part of this digital medium, including
# software, services, documents, text, images, icons, and logos design;
# the selection, assembly, arrangement, and design thereof; and the code
# that enables its presentation, are the sole property of Isilon Systems,
# LLC.
#
# Except as otherwise required by applicable law, no part of any
# copyrighted material may be reproduced, modified, translated,
# distributed, published, or transmitted by any means, electronic or
# mechanical, for any purpose, without the prior written consent of Isilon
# Systems.
#
# Isilon and Isilon Systems are registered trademarks of Isilon Systems,
# LLC.  Legion is a trademark of Isilon Systems, LLC.  All other
# trademarks and registered trademarks are the property of their
# respective holders.
#

# PROVIDE: isi_module_init

# This script loads all listed modules in parallel and returns when all
# have been completely loaded

MODULES = [
    'cpuctl',
    'fcdm',
    'ib_nvram',
    'ib_user',
# XXX: we want to fix this but runtime initialization causes em and cxgb
# interfaces to come up in a different order than loader initialized
#    'if_cxgb',
#    'if_em',
# XXX: see code below for why if_igb isn't autoloaded.
#    'if_igb',
    'if_lagg',
#    'imdd',
    'isi_dspcom',
    'isi_i2c',
    'isp',
    'ispfw',
# XXX: The IB team wants the mlx driver loaded earlier than this script
#      allows, so these are enabled in loader.conf rather than here. 
#    'mlx4_core',
#    'mlx4_ib',
#    'mps',
#    'mthca',
    'rnv',
    'lnv',
    'sdp',
    'acpi_sel',
    ]

import subprocess
import sys
import time
import isi.misc as misc

def fw_exit(error):
    # Lookup the mthca FW information for the hw versions library sysctl.
    #
    # NOTE: The FW information is required by the CTO initialization
    #       code the probes the hardware, so we must do it before
    #       going any further.
    #

    # Only run fw_exit for mthca hardware
    if subprocess.call(["sysctl", "-Nq", "dev.mthca"], stdout=subprocess.PIPE, stderr=subprocess.PIPE):
        sys.exit(error)

    mp = subprocess.Popen("/usr/bin/isi_hwtools/isi_ib_fw -d mthca0 -byte_mode -nofs -qq query", shell=True, stdout=subprocess.PIPE)

    fw_info = mp.stdout.read()
    fw_info = fw_info.split("-W- Running quick query - Skipping full image integrity checks.")
    fw_info = fw_info[1].strip()

    if (fw_info is not None):
        misc.sysctlbyname('dev.mthca.fw_info=' + fw_info)

    sys.exit(error)

if __name__ == "__main__":

    print 'Loading all modules'

    process_dict = {}

    # HACK: Probably Kylin/Hotfoot or newer.
    #
    # Unload if_em so we don't run into collisions in the linker global
    # namespace with the e1000 KPIs as the driver is forked for em and igb
    # when we try to load if_igb.
    if (subprocess.call('/sbin/kldstat -v | /usr/bin/grep -q if_em',
                        shell=True, stderr=subprocess.PIPE) == 0 and
        subprocess.call('/sbin/ifconfig -l | /usr/bin/grep -q em0', shell=True)):
        p = subprocess.Popen(['/sbin/kldunload', '-f', 'if_em'],
                             stderr=subprocess.PIPE, shell=False)
        __, err = p.communicate()
        if p.returncode:
            print 'kldunload if_em failed:', err
        else:
            MODULES.append('if_igb')
            MODULES.sort()

    # if ifs is not built into kernel, add it to MODULES
    try:
        misc.sysctlbyname('efs.gmp.group')
    except misc.error:
        MODULES.append('efs')

    for module in MODULES:
        print 'kldload ' + module
        p = subprocess.Popen('/sbin/kldload ' + module, shell=True)
        process_dict[p] = module

    MAX_WAIT_TIME = 30
    DELAY = 0.1

    wait_time = 0

    while (wait_time < MAX_WAIT_TIME):
        # Check if all done
        is_done = True

        for process in process_dict:
            if process.poll() == None:
                is_done = False
                break

            if process.returncode != 0:
                print "ERROR: '%s' failed to load!" % process_dict[process]
                fw_exit(1)

        if not is_done:
            time.sleep(DELAY)
            wait_time += DELAY
        else:
            print 'Completed module load'
            fw_exit(0)

    print 'ERROR: isi_module_init timed out'

    for process in process_dict:
        if process.poll() == None:
            print 'Did not complete loading %s' % process_dict[process]

    fw_exit(1)
