#!/usr/bin/python

# Copyright C 2002-2011 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,
# Inc.
#
# 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,
# Inc.  Legion is a trademark of Isilon Systems, Inc.  All other
# trademarks and registered trademarks are the property of their
# respective holders.

"""
Checks to make sure that the node's hardware supports the currently
installed OneFS version and hardware components are properly configured.
"""

import os.path
import signal
import sys

import isi.app.lib.cluster as cluster
import isi.celog
from isi.celog.eventids import eventid_map
import isi.hw.hal as hal
import isi.hw.version as hwver
import isi.misc
import isi.sys.version as sysver

EXIT_FAILURE=1
EXIT_SUCCESS=0
UNSUPPORTED_BOOT_FILE='/etc/allow_unsupported_boot'

def check_hardware_components():
    check_nvram_pci_speed()
    check_nvram_pci_width()

def get_devid():
    devid = None
    try:
        devid = int(cluster.Cluster().local_devid())
    except:
        pass
    return devid

def add_celog_event(eventid, severity, specifier, msg):
    try:
        devid = get_devid()
        mev = isi.celog.Event(
                event_id=eventid,
                severity=severity,
                devid=devid,
                specifier=specifier,
                message = msg,
                )
        mev.add()
    except:
        print 'Unable to send CELOG event: %s message: %s' % (eventid, msg)

def cancel_celog_event(eventid, specifier):
    specifier['devid'] = get_devid()
    try:
        isi.celog.anonymous_end(eventid, specifier)
    except:
        print 'Unable to end CELOG event: %s' % eventid

def check_nvram_pci_speed():
    supported = [
        hwver.NVR_LOX,
    ]
    if hal.getNvramType() in supported:
        eventid = eventid_map.get('HW_NVRAM_SOC_LINK_SPEED_ERROR')
        severity = isi.celog.EVENT_LEVEL_CRIT
        specifier = {}
        capability = hal.getNvramPciSpeedCapability()
        expected = hal.getNvramExpectedPciSpeedCapability()
        msg = ("Failed to negotiate PCIe lane speed - expected %s got %s" %
               (expected, capability))
        if capability < expected:
            add_celog_event(eventid, severity, specifier, msg)
        else:
            cancel_celog_event(eventid, specifier)

def check_nvram_pci_width():
    supported = [
        hwver.NVR_LOX,
    ]
    if hal.getNvramType() in supported:
        eventid = eventid_map.get('HW_NVRAM_SOC_LINK_ERROR')
        severity = isi.celog.EVENT_LEVEL_CRIT
        specifier = {}
        capability = hal.getNvramPciWidthCapability()
        expected = hal.getNvramExpectedPciWidthCapability()
        msg = ("Failed to negotiate PCIe lane width - expected %s got %s" %
               (expected, capability))
        if capability < expected:
            add_celog_event(eventid, severity, specifier, msg)
        else:
            cancel_celog_event(eventid, specifier)

def main():
    # don't terminate init script on ^C
    signal.signal(signal.SIGINT,signal.SIG_IGN)

    print 'Verifying hardware configuration...',

    check_hardware_components()

    # Check override to see if we skip the version check.
    if hal.supportsAnyUpdate():
        print 'overridden'
        return EXIT_SUCCESS

    try:
        onefs_version = isi.misc.osrelease()
        # Future Proof check.
        # We then get the Context files from the appropriate place and exec them
        # to get our shim and checker functions.
        exec open("/var/db/versions/checker") in locals()
        gatherer = sysver.Gatherer()
        # For SED nodes, skip drive info, as drives are locked on boot
        skip_drives = False
        if hal.supportsSelfEncryptingDrives():
            skip_drives = True
        node_info = gatherer.gather(skip_drives = skip_drives)
        res = check_reimage(node_info, onefs_version)
        if res != True:
            print '' # force the newline in case of failure
            output_message = '''
            Warning: Invalid hardware configuration!
            %s!
            Please contact EMC Customer Support:
            United States: 1 800 782 4362 (1 800 SVC 4EMC)
            Canada: 1 800 543 4782 (1 800 543 4SVC)
            Worldwide Country Code: 1 508 497 7901
            '''
            sys.stdout.write(output_message % res)
            if os.path.exists(UNSUPPORTED_BOOT_FILE):
                return EXIT_SUCCESS
            return EXIT_FAILURE
    except Exception, e:
        print '' # force the newline in case of exception
        print e
        print 'Unable to verify a valid hardware configuration'
        if os.path.exists(UNSUPPORTED_BOOT_FILE):
            return EXIT_SUCCESS
        return EXIT_FAILURE

    print 'ok'
    return EXIT_SUCCESS

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